在活动记录库中添加某些方法的首选方法是什么?

时间:2014-07-31 22:57:59

标签: ruby-on-rails ruby include extend

我想创建一个模块,为从活动记录库继承的类提供一些常用方法。

以下是我们可以实现的双向方式。

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)

哪一种是处理此问题的首选方式?

什么时候使用?

3 个答案:

答案 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)

感谢Mori的回答......你可以这样做: -

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