27

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

def foo
  # do something
end

我应该把这个放在哪里?

我知道通用代码位于Rails 应用程序的lib目录中。

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

class A < ActiveRecord::Base
includes Foo

class B < ActiveRecord::Base
includes Foo

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

4

6 回答 6

39

创建一个模块,您可以将其放在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 文件。

于 2009-11-08T23:29:41.773 回答
14

你真的有两个选择:

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

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

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

如果共享功能对每个类都是通用的并且 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
于 2009-11-09T00:24:01.703 回答
6

我是这样做的...首先创建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
于 2013-02-07T02:08:26.187 回答
5

一种选择是将它们放在一个新目录中,例如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目录中更有条理,因为它们靠近使用它们的类。

于 2009-11-08T23:36:06.163 回答
5

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

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更好的方法。

于 2013-12-23T19:23:17.770 回答
4

正如其他人提到的那样, include Foo 是做事的方式......但是它似乎并没有为您提供基本模块所需的功能。以下是许多 Rails 插件在新实例方法之外添加类方法和新回调的形式。

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
于 2009-11-09T18:19:22.070 回答