2009-11-08 37 views
21

我有包含同樣的方法兩種型號:何處可以在多個模型中找到通用代碼?

def foo 
    # do something 
end 

我應該在哪裏放呢?

我知道常見的代碼在lib目錄在Rails應用程序中。

但如果我把它放在lib一個新的類名爲「Foo」,我需要它的功能添加到我的兩個ActiveRecord models的,我是這樣做的:

class A < ActiveRecord::Base 
includes Foo 

class B < ActiveRecord::Base 
includes Foo 

,然後兩個AB將包含foo方法,就像我在每個方法中定義它一樣?

回答

34

創建一個模塊,你可以把在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文件。

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目錄,因爲它們靠近使用它們的類。

+0

如果兩個模型都調用「before_save:before_method」,並且我也將它放在SharedMethods中,那麼它是否也能工作?還是它只適用於方法定義? – 2009-11-09 00:10:11

+0

此外,你的'需要'的代碼出現在environment.rb中嗎? – 2009-11-09 00:12:35

+1

您可能需要在「Rails :: Initializer.run do | config | ... end」部分 – nicholaides 2009-11-09 15:34:47

14

你真的有兩個選擇:

  1. 使用共同的邏輯模塊,並將其納入一個&乙
  2. 使用擴展ActiveRecord的一個共同的C類,並有& B伸出C.

如果共享功能不是每個類的核心,但適用於每個類,則使用#1。例如:

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

使用#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 
+0

在這種情況下,需要「字母」關係,對嗎?如何做同樣的事情,並且實際上將activerecord擴展到A和B? – Ron 2013-12-23 18:57:52

+2

選項#2讓Rails認爲A和B都存儲在一個名爲「字母」的表中。如果你只想要共享邏輯,而把A和B保存在不同的表中,就像@Ron指出的[下面](http://stackoverflow.com/a/20749863/2657571),抽象的父類是要走的路。 – EK0 2015-07-01 13:50:33

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 
+0

我試過這種方法,但它仍然告訴我沒有定義的方法(在我的例子中,範圍)。 – Jonah 2013-09-04 00:16:40

5

這裏是我是如何做到的?首先創建混入:

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 

這幾乎是漂亮!

爲了完成這個例子,雖然它是不相關的問題,這是我塞型號:

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

如果您需要的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作爲更好的方法。

相關問題