在多个模型中放置常见代码的位置?

我有两个包含相同方法的模型:

def foo # do something end 

我应该把它放在哪里?

我知道常用代码在Rails应用程序的lib目录中。

但是如果我把它放在lib中一个名为’ Foo ‘的新类中,并且我需要将它的function添加到我的两个ActiveRecord models ,我是这样做的:

 class A < ActiveRecord::Base includes Foo class B < ActiveRecord::Base includes Foo 

然后AB都将包含foo方法,就像我在每个方法中定义它一样?

创建一个模块,您可以将其放在lib目录中:

 module Foo def foo # do something end end 

然后,您可以在每个模型类中include该模块:

 class A < ActiveRecord::Base include Foo end class B < ActiveRecord::Base include Foo end 

AB模型现在将定义foo方法。

如果你遵循Rails命名约定,包括模块的名称和文件的名称(例如foo.rb中的Foo和foo_bar.rb中的FooBar),那么Rails会自动为你加载文件。 否则,您将需要使用require_dependency 'file_name'来加载您的lib文件。

你真的有两个选择:

  1. 将模块用于通用逻辑并将其包含在A和B中
  2. 使用扩展ActiveRecord的公共类C并使A和B扩展C.

如果共享function不是每个类的核心,则使用#1,但适用于每个类。 例如:

 (app/lib/serializable.rb) module Serializable def serialize # do something to serialize this object end end 

如果共享function对于每个类是通用的并且A&B共享自然关系,则使用#2:

 (app/models/letter.rb) class Letter < ActiveRecord::Base def cyrilic_equivilent # return somethign similar end end class A < Letter end class B < Letter end 

一种选择是将它们放在一个新目录中,例如app/models/modules/ 。 然后,您可以将其添加到config/environment.rb

 Dir["#{RAILS_ROOT}/app/models/modules/*.rb"].each do |filename| require filename end 

这将require该目录中的每个文件,因此如果您在模块目录中放置如下文件:

 module SharedMethods def foo #... end end 

然后你可以在模型中使用它,因为它会自动加载:

 class User < ActiveRecord::Base include SharedMethods end 

这种方法比将这些mixin放在lib目录中更有条理,因为它们靠近使用它们的类。

这是我如何做到的…首先创建mixin:

 module Slugged extend ActiveSupport::Concern included do has_many :slugs, :as => :target has_one :slug, :as => :target, :order => :created_at end end 

然后将它混合到需要它的每个模型中:

 class Sector < ActiveRecord::Base include Slugged validates_uniqueness_of :name etc end 

它几乎是漂亮的!

为了完成这个例子,虽然这与问题无关,但这是我的slug模型:

 class Slug < ActiveRecord::Base belongs_to :target, :polymorphic => true end 

如果您需要ActiveRecord :: Base代码作为常用function的一部分,那么使用抽象类也很有用。 就像是:

 class Foo < ActiveRecord::Base self.abstract_class = true #Here ActiveRecord specific code, for example establish_connection to a different DB. end class A < Foo; end class B < Foo; end 

就如此容易。 此外,如果代码与ActiveRecord无关,请将ActiveSupport::Concerns作为更好的方法。

正如其他人所提到的那样,包括Foo是做事的方式……然而,它似乎并没有为您提供基本模块所需的function。 以下是除了新实例方法之外,许多Rails插件用于添加类方法和新回调的forms。

 module Foo #:nodoc: def self.included(base) # :nodoc: base.extend ClassMethods end module ClassMethods include Foo::InstanceMethods before_create :before_method end module InstanceMethods def foo ... end def before_method ... end end end