我想创建一个模块,为从活动记录库继承的类提供一些常用方法。
以下是我们可以实现的双向方式。
1)
module Commentable
def self.extended(base)
base.class_eval do
include InstanceMethods
extend ClassMethods
end
end
module ClassMethods
def test_commentable_classmethod
puts 'test class method'
end
end
module InstanceMethods
def test_commentable_instance_method
puts 'test instance method'
end
end
end
ActiveRecord::Base.extend(Commentable)
2)
module Commentable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def test_commentable_classmethod
puts 'test class method'
end
end
def test_commentable_instance_method
puts 'test instance methods'
end
end
ActiveRecord::Base.send(:include, Commentable)
哪一种是处理此问题的首选方式?
和
什么时候使用?
答案 0 :(得分:8)
从Rails 5开始,推荐的方法是创建一个模块并将其包含在需要它的模型中,或者在所有模型继承的ApplicationRecord中包含它。 (您可以在旧版本的Rails中从头开始轻松实现此模式。)
# app/models/concerns/my_module.rb
module MyModule
extend ActiveSupport::Concern
module ClassMethods
def has_some_new_fancy_feature(options = {})
...
end
end
end
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include MyModule
end
模块是多重继承的一种形式,有时会增加不必要的复杂性。检查装饰器,服务或其他类型的对象是否更有意义。并非所有东西都需要一个花哨的宏,为你的模型添加50个回调。如果你做得太多,你会恨你的生活。
如果你想要猴子补丁(不要这样做),这是我的老答案:
# config/initializers/activerecord_extensions.rb
ActiveRecord::Base.send(:include, MyModule)
或者没有猴子修补(见Mori's response):
# app/models/base_model.rb
class BaseModel < ActiveRecord::Base
self.abstract_class = true
include MyModule
end
编辑:在一个大项目的几个月后,我意识到让每个模型都从新的基础模型类继承为Mori explains更好。将模块直接包含在ActiveRecord :: Base中的问题是,这会干扰依赖ActiveRecord的第三方代码。最好不要在没必要的情况下进行猴子补丁。在这种情况下,从长远来看,创建一个新的基类最终会变得更简单。
答案 1 :(得分:6)
另一种方法是通过继承ActiveRecord::Base
创建自己的基类,然后让模型继承该基类。这样做的好处是可以清楚地表明您的模型没有在vanilla ActiveRecord上运行:
class MyBase < ActiveRecord::Base
self.abstract_class = true
def self.a_class_method
end
def an_instance_method
end
end
class Foo < MyBase
end
Foo.a_class_method
Foo.new.an_instance_method
答案 2 :(得分:0)
Module ActiveRecordUtilities
class MyBase < ActiveRecord::Base
self.abstract_class = true
def self.a_class_method
end
def an_instance_method
end
end
end##class ends
end##module ends
并且可以使用它...假设在user.rb
include ActiveRecordUtilities::MyBase
User.a_class_method
@user.instance_method
============================ OR ================== ==
module MyUtils
def do_something_funky
# Some exciting code
end
end
class Account < ActiveRecord::Base
belongs_to :person, :extend => MyUtils
end
And then call it like this:
@account = Account.first
@account.person.do_something_funky