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

kru*_*hah 5 ruby ruby-on-rails 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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

哪一个是处理这个的首选方式?

什么时候用?

wma*_*ley 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
Run Code Online (Sandbox Code Playgroud)

模块是多重继承的一种形式,有时会增加不必要的复杂性.检查装饰器,服务或其他类型的对象是否更有意义.并非所有东西都需要一个花哨的宏,为你的模型添加50个回调.如果你做得太多,你会恨你的生活.


如果你想要猴子补丁(不要这样做),这是我的老答案:

# config/initializers/activerecord_extensions.rb
ActiveRecord::Base.send(:include, MyModule)
Run Code Online (Sandbox Code Playgroud)

或者没有猴子修补(见Mori的回应):

# app/models/base_model.rb
class BaseModel < ActiveRecord::Base
  self.abstract_class = true
  include MyModule
end
Run Code Online (Sandbox Code Playgroud)

编辑:在一个大型项目的几个月后,我已经意识到让每个模型都继承自新的基础模型类更好,正如Mori解释的那样.将模块直接包含在ActiveRecord :: Base中的问题是这会干扰依赖ActiveRecord的第三方代码.最好不要在没必要的情况下进行猴子补丁.在这种情况下,从长远来看,创建新的基类最终会变得更简单.


Mor*_*ori 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
Run Code Online (Sandbox Code Playgroud)