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

时间:2009-11-08 22:59:01

标签: ruby-on-rails ruby mixins

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

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方法,就像我在每个方法中定义它一样?

6 个答案:

答案 0 :(得分:36)

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

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

答案 1 :(得分:14)

你真的有两个选择:

  1. 将模块用于通用逻辑并将其包含在A&amp; A中。乙
  2. 使用扩展ActiveRecord并具有A&amp; A的公共类C. B延伸C.
  3. 如果共享功能不是每个类的核心,但使用#1,但适用于每个类。例如:

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

    如果共享功能对每个班级和A&amp; A共同使用,则使用#2 B分享自然关系:

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

答案 2 :(得分: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

答案 3 :(得分: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目录中更有条理,因为它们靠近使用它们的类。

答案 4 :(得分: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作为更好的方法。

答案 5 :(得分:4)

正如其他人所提到的那样,包括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
相关问题