ฉันได้อ่านเกี่ยวกับวิธีการขยาย ActiveRecord: คลาสพื้นฐานดังนั้นแบบจำลองของฉันจะมีวิธีพิเศษบางอย่าง วิธีง่าย ๆ ในการขยาย (การสอนทีละขั้นตอน) คืออะไร?
ฉันได้อ่านเกี่ยวกับวิธีการขยาย ActiveRecord: คลาสพื้นฐานดังนั้นแบบจำลองของฉันจะมีวิธีพิเศษบางอย่าง วิธีง่าย ๆ ในการขยาย (การสอนทีละขั้นตอน) คืออะไร?
คำตอบ:
มีหลายวิธี:
อ่านActiveSupport ::เอกสารที่เกี่ยวข้องสำหรับรายละเอียดเพิ่มเติม
สร้างไฟล์ที่เรียกว่าactive_record_extension.rb
ในlib
ไดเรกทอรี
require 'active_support/concern'
module ActiveRecordExtension
extend ActiveSupport::Concern
# add your instance methods here
def foo
"foo"
end
# add your static(class) methods here
class_methods do
#E.g: Order.top_ten
def top_ten
limit(10)
end
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
สร้างไฟล์ในconfig/initializers
ไดเรกทอรีชื่อextensions.rb
และเพิ่มบรรทัดต่อไปนี้ในไฟล์:
require "active_record_extension"
สร้างไฟล์ในส่วนไดเรกทอรีที่เรียกว่าconfig/initializers
active_record_monkey_patch.rb
class ActiveRecord::Base
#instance method, E.g: Order.new.foo
def foo
"foo"
end
#class method, E.g: Order.top_ten
def self.top_ten
limit(10)
end
end
คำพูดที่มีชื่อเสียงเกี่ยวกับการแสดงออกปกติโดยJamie Zawinskiสามารถนำมาใช้อีกครั้งเพื่อแสดงปัญหาที่เกี่ยวข้องกับการปะแก้ลิง
บางคนเมื่อเผชิญกับปัญหาคิดว่า“ ฉันรู้ว่าฉันจะใช้การปะลิง” ตอนนี้พวกเขามีสองปัญหา
การปะของลิงเป็นเรื่องง่ายและรวดเร็ว แต่เวลาและความพยายามที่บันทึกไว้จะถูกดึงกลับมาบางครั้งในอนาคต; ด้วยดอกเบี้ยทบต้น ทุกวันนี้ฉันได้ จำกัด การปะแก้ลิงเพื่อให้ต้นแบบโซลูชันได้อย่างรวดเร็วในคอนโซลทางรถไฟ
require
environment.rb
ฉันได้เพิ่มขั้นตอนพิเศษนี้ในคำตอบของฉัน
ImprovedActiveRecord
และการสืบทอดจากสิ่งนั้นเมื่อคุณใช้module
คุณกำลังอัพเดตนิยามของคลาสที่เป็นปัญหา ฉันเคยใช้การสืบทอด (สาเหตุของประสบการณ์ Java / C ++) วันนี้ฉันส่วนใหญ่ใช้โมดูล
Refinements
ซึ่งแก้ไขปัญหาส่วนใหญ่ด้วยการแก้ไขลิง ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ) บางครั้งคุณสมบัติก็มีไว้เพื่อบังคับให้คุณล่อลวงชะตากรรม และบางครั้งคุณก็ทำ
คุณสามารถขยายชั้นเรียนและใช้มรดก
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
แสดงเขตข้อมูลจาก ฉันควรใส่ไว้ที่ไหน?
self.abstract_class = true
AbstractModel
Rails จะรับรู้แบบจำลองเป็นแบบนามธรรม
AbstractModel
ในฐานข้อมูล ใครจะรู้ setter ง่าย ๆ จะช่วยให้ฉันแห้งขึ้น! (ฉันเริ่มที่จะประจบประแจง ... มันไม่ดี) ขอบคุณ Toby และ Harish!
คุณยังสามารถใช้ActiveSupport::Concern
และเป็น Rails Core สำนวนที่ชอบมากขึ้น:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[แก้ไข] ติดตามความคิดเห็นจาก @daniel
จากนั้นแบบจำลองทั้งหมดของคุณจะมีวิธีการfoo
รวมเป็นวิธีการอินสแตนซ์และวิธีการ ClassMethods
รวมเป็นวิธีการเรียน เช่นFooBar < ActiveRecord::Base
คุณจะมี: FooBar.bar
และFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
เลิกใช้แล้วตั้งแต่ Rails 3.2 เพียงแค่ใส่วิธีการของคุณลงในส่วนของโมดูล
ActiveRecord::Base.send(:include, MyExtension)
ในเครื่องมือเริ่มต้นและจากนั้นก็ใช้งานได้สำหรับฉัน Rails 4.1.9
ด้วย Rails 4 แนวคิดของการใช้ข้อกังวลในการทำให้เป็นโมดูลและ DRY ในแบบจำลองของคุณเป็นไฮไลท์
ข้อกังวลช่วยให้คุณสามารถจัดกลุ่มโค้ดที่คล้ายกันของโมเดลหรือข้ามหลายโมเดลในโมดูลเดียวจากนั้นใช้โมดูลนี้ในโมเดล นี่คือตัวอย่าง:
พิจารณาโมเดลบทความโมเดลเหตุการณ์และโมเดลข้อคิดเห็น บทความหรือเหตุการณ์มีความคิดเห็นมากมาย ความคิดเห็นเป็นของบทความหรือเหตุการณ์
ตามเนื้อผ้าโมเดลอาจมีลักษณะเช่นนี้:
รูปแบบความคิดเห็น:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
รูปแบบบทความ:
class Article < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#return the article with least number of comments
end
end
รูปแบบกิจกรรม
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#returns the event with least number of comments
end
end
ดังที่เราสามารถสังเกตเห็นได้ว่ามีรหัสที่สำคัญอย่างหนึ่งที่พบได้ทั่วไปสำหรับทั้ง Event และ Article Model ด้วยความกังวลเราสามารถแยกรหัสทั่วไปนี้ในโมดูลที่แยกต่างหากแสดงความคิดเห็นได้
สำหรับสิ่งนี้ให้สร้างไฟล์ commentable.rb ในแอพ / รุ่น / ข้อกังวล
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
# for the given article/event returns the first comment
def find_first_comment
comments.first(created_at DESC)
end
module ClassMethods
def least_commented
#returns the article/event which has the least number of comments
end
end
end
และตอนนี้แบบจำลองของคุณมีลักษณะเช่นนี้:
รูปแบบความคิดเห็น:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
รูปแบบบทความ:
class Article < ActiveRecord::Base
include Commentable
end
รูปแบบกิจกรรม
class Event < ActiveRecord::Base
include Commentable
end
ประเด็นหนึ่งที่ฉันอยากจะเน้นในขณะที่ใช้ความกังวลก็คือควรใช้ความกังวลสำหรับการจัดกลุ่ม 'โดเมนที่ใช้' แทนที่จะใช้การจัดกลุ่มแบบ 'เทคนิค' ตัวอย่างเช่นการจัดกลุ่มโดเมนมีลักษณะเช่น 'แสดงความคิดเห็น', 'แท็กเกเบิล' เป็นต้นการจัดกลุ่มตามเทคนิคจะเหมือนกับ 'FinderMethods', 'ValidationMethods'
นี่คือลิงค์ไปยังโพสต์ที่ฉันพบว่ามีประโยชน์มากสำหรับการทำความเข้าใจข้อกังวลในแบบจำลอง
หวังว่าการเขียนช่วยได้ :)
ขั้นตอนที่ 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
ขั้นตอนที่ 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
ขั้นตอนที่ 3
There is no step 3 :)
ราง 5 ActiveRecord::Base
ให้ในตัวกลไกสำหรับการขยาย
นี่คือความสำเร็จโดยการให้ชั้นเพิ่มเติม:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
และทุกรุ่นสืบทอดจากที่หนึ่ง:
class Post < ApplicationRecord
end
ดูเช่นบล็อกนี้
เพียงเพิ่มในหัวข้อนี้ฉันใช้เวลาสักครู่เพื่อหาวิธีทดสอบส่วนขยายดังกล่าว (ฉันลงไปตามActiveSupport::Concern
เส้นทาง)
นี่คือวิธีการตั้งค่าแบบจำลองสำหรับทดสอบส่วนขยายของฉัน
describe ModelExtensions do
describe :some_method do
it 'should return the value of foo' do
ActiveRecord::Migration.create_table :test_models do |t|
t.string :foo
end
test_model_class = Class.new(ActiveRecord::Base) do
def self.name
'TestModel'
end
attr_accessible :foo
end
model = test_model_class.new(:foo => 'bar')
model.some_method.should == 'bar'
end
end
end
ด้วย Rails 5 ทุกรุ่นได้รับการสืบทอดจาก ApplicationRecord & เป็นวิธีที่ดีในการรวมหรือขยายไลบรารีส่วนขยายอื่น ๆ
# app/models/concerns/special_methods.rb
module SpecialMethods
extend ActiveSupport::Concern
scope :this_month, -> {
where("date_trunc('month',created_at) = date_trunc('month',now())")
}
def foo
# Code
end
end
สมมติว่าโมดูลเมธอดพิเศษต้องพร้อมใช้งานในทุกรุ่นรวมไว้ในไฟล์ application_record.rb หากเราต้องการนำสิ่งนี้ไปใช้กับชุดของโมเดลที่เฉพาะเจาะจงให้รวมไว้ในคลาสโมเดลที่เกี่ยวข้อง
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include SpecialMethods
end
# app/models/user.rb
class User < ApplicationRecord
include SpecialMethods
# Code
end
หากคุณต้องการมีวิธีการที่กำหนดไว้ในโมดูลเป็นวิธีการเรียนขยายโมดูลเพื่อ ApplicationRecord
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
หวังว่ามันจะช่วยให้ผู้อื่น!
ฉันมี
ActiveRecord::Base.extend Foo::Bar
ใน initializer
สำหรับโมดูลเช่นด้านล่าง
module Foo
module Bar
end
end