2010-02-24 64 views
150

我已經做了一些關於如何擴展ActiveRecord:Base類的閱讀,所以我的模型會有一些特殊的方法。什麼是簡單的方法來擴展它(一步一步的教程)?Rails擴展ActiveRecord :: Base

+0

什麼樣的擴展?我們真的需要更多的繼續。 – jonnii 2010-02-24 19:51:06

回答

320

有幾種方法:

使用的ActiveSupport ::關注(首選)

閱讀ActiveSupport::Concern文檔瞭解更多信息。

lib目錄中創建一個名爲active_record_extension.rb的文件。

module ActiveRecordExtension 

    extend ActiveSupport::Concern 

    # add your instance methods here 
    def foo 
    "foo" 
    end 

    # add your static(class) methods here 
    class_methods do 
    #E.g: Order.top_ten   
    def top_ten 
     limit(10) 
    end 
    end 
end 

# include the extension 
ActiveRecord::Base.send(:include, ActiveRecordExtension) 

創建一個名爲extensions.rbconfig/initializers目錄中的文件,並將以下行添加到文件:

require "active_record_extension" 

繼承(首選)

參考託比的answer

猴修補(應儘量避免)

創建一個名爲active_record_monkey_patch.rbconfig/initializers目錄中的文件。

class ActiveRecord::Base  
    #instance method, E.g: Order.new.foo  
    def foo 
    "foo" 
    end 

    #class method, E.g: Order.top_ten   
    def self.top_ten 
    limit(10) 
    end 
end 

有關正則表達式的名言被Jamie Zawinski可以被重新利用來說明與猴子修補相關的問題。

Some people, when confronted with a problem, think 「I know, I'll use monkey patching.」 Now they have two problems.

猴子修補很容易和快捷。但是,節省的時間和精力總是會在將來的某個時候提取回 ;與複利。現在我限制猴子修補,以便在軌道控制檯中快速構建原型解決方案。

+0

嗯......第二個例子是當我運行./scripts/console時出現錯誤 「include」:TypeError:錯誤的參數類型Class (預期模塊)「 – xpepermint 2010-02-24 21:17:36

+0

@xpepermint聽起來像是你用'class MyActiveRecordExtensions'而不是'module MyActiveRecordExtensions'來啓動它的。 – 2010-02-24 23:59:06

+0

沒有...我瘋了一個複製和粘貼。奇怪... – xpepermint 2010-02-25 08:01:02

7

步驟1

module FooExtension 
    def foo 
    puts "bar :)" 
    end 
end 
ActiveRecord::Base.send :include, FooExtension 

步驟2

# Require the above file in an initializer (in config/initializers) 
require 'lib/foo_extension.rb' 

步驟3

There is no step 3 :) 
+1

我猜步驟2必須放置在config/environment.rb中。這對我來說不是很有用:(你可以寫一些更多的幫助嗎?Thx。 – xpepermint 2010-02-24 20:22:59

66

您可以只擴展該類並簡單地使用繼承。

class AbstractModel < ActiveRecord::Base 
    self.abstract_class = true 
end 

class Foo < AbstractModel 
end 

class Bar < AbstractModel 
end 
+0

我喜歡這個想法,因爲它是一種標準的做法,但是...我得到一個錯誤表'moboolo_development。 abstract_models'不存在:顯示來自'abstract_models'的字段。我應該把它放在哪裏? – xpepermint 2010-02-25 07:59:59

+23

將'self.abstract_class = true'添加到'AbstractModel'中。 Rails現在將該模型視爲一種抽象模型。 – 2010-02-25 17:53:40

+0

哇!沒想到這是可能的。當ActiveRecord ch咽在數據庫中尋找'AbstractModel'時嘗試了它並放棄了。誰知道一個簡單的二傳手會幫我幹什麼! (我開始畏縮......這很糟糕)。感謝Toby和Harish! – dooleyo 2013-06-28 04:04:52

19

您還可以使用ActiveSupport::Concern和更Rails核心成語,如:

module MyExtension 
    extend ActiveSupport::Concern 

    def foo 
    end 

    module ClassMethods 
    def bar 
    end 
    end 
end 

ActiveRecord::Base.send(:include, MyExtension) 

[編輯]從@daniel

註釋下面然後所有的車型將有包括方法foo作爲實例方法和包含在類方法中的ClassMethods中的方法。例如。在FooBar < ActiveRecord::Base你將有:FooBar.barFooBar#foo

http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

+5

請注意,'InstanceMethods'已被棄用,因爲Rails 3.2,只是把你的方法放入模塊體。 – 2012-12-12 11:01:54

+0

我在初始化器中放置了'ActiveRecord :: Base.send(:include,MyExtension)',然後這對我有用。 Rails 4.1.9 – 2015-01-20 06:38:02

4

只需添加到這個話題,我花了一段時間去解決如何測試這種擴展(我走下ActiveSupport::Concern路線。)

以下是我如何設置測試擴展的模型。

describe ModelExtensions do 
    describe :some_method do 
    it 'should return the value of foo' do 
     ActiveRecord::Migration.create_table :test_models do |t| 
     t.string :foo 
     end 

     test_model_class = Class.new(ActiveRecord::Base) do 
     def self.name 
      'TestModel' 
     end 

     attr_accessible :foo 
     end 

     model = test_model_class.new(:foo => 'bar') 

     model.some_method.should == 'bar' 
    end 
    end 
end 
0

ActiveRecord::Base.extend Foo::Bar 

在初始化

對於像下面

module Foo 
    module Bar 
    end 
end 
14

使用Rails 4,使用關注的概念模塊化一個模塊並擦乾你的模特一直在亮點。

問題基本上允許您將單個模塊中的類似代碼或多個模型分組,然後在模型中使用此模塊。以下是一個示例:

考慮一篇文章模型,一個事件模型和一個評論模型。一篇文章或一個事件有很多評論。評論屬於文章或事件。

傳統上,模型可能是這樣的:

評論模型:

class Comment < ActiveRecord::Base 
    belongs_to :commentable, polymorphic: true 
end 

文章型號:

class Article < ActiveRecord::Base 
    has_many :comments, as: :commentable 

    def find_first_comment 
    comments.first(created_at DESC) 
    end 

    def self.least_commented 
    #return the article with least number of comments 
    end 
end 

事件模型

class Event < ActiveRecord::Base 
    has_many :comments, as: :commentable 

    def find_first_comment 
    comments.first(created_at DESC) 
    end 

    def self.least_commented 
    #returns the event with least number of comments 
    end 
end 

正如我們可以看到, 那裏 是Event和Article Model共同的重要代碼。使用關注點,我們可以在一個單獨的可評論模塊中提取此通用代碼。

爲此,在app/model/concerns中創建一個commentable.rb文件。

module Commentable 
    extend ActiveSupport::Concern 

    included do 
     has_many :comments, as: :commentable 
    end 

    # for the given article/event returns the first comment 
    def find_first_comment 
     comments.first(created_at DESC) 
    end 

    module ClassMethods  
     def least_commented 
      #returns the article/event which has the least number of comments 
     end 
    end 
end 

,現在你的模型如下圖所示:

評論模型:

class Comment < ActiveRecord::Base 
     belongs_to :commentable, polymorphic: true 
    end 

文章型號:

class Article < ActiveRecord::Base 
    include Commentable 
end 

事件模型

class Event < ActiveRecord::Base  
    include Commentable 
end 

我想強調的一點是,使用Concerns是關注點應該用於「基於域」的分組而不是「技術」分組。例如,域名分組就像'可註釋','可標記'等。基於技術的分組將像'FinderMethods','ValidationMethods'。

這是一個link to a post,我發現它對於理解模型中的問題非常有用。

希望的書面記錄幫助:)

+0

這是我最喜歡的答案,謝謝:) – 2017-03-30 14:36:50

3

的Rails 5提供了一個內置的機制,用於擴展ActiveRecord::Base

這是通過提供附加的層來實現:

# app/models/application_record.rb 
class ApplicationRecord < ActiveRecord::Base 
    self.abstract_class = true 
    # put your extensions here 
end 

和所有模型從一個繼承:

class Post < ApplicationRecord 
end 

參見例如this blogpost

0

使用Rails 5,所有模型都從ApplicationRecord &繼承,它提供了很好的方式來包含或擴展其他擴展庫。

# app/models/concerns/special_methods.rb 
module SpecialMethods 
    extend ActiveSupport::Concern 

    scope :this_month, -> { 
    where("date_trunc('month',created_at) = date_trunc('month',now())") 
    } 

    def foo 
    # Code 
    end 
end 

假設特殊方法模塊需要跨所有模型可用,請將其包含在application_record.rb文件中。如果我們想將其應用於特定的一組模型,則將其包含在相應的模型類中。

# app/models/application_record.rb 
class ApplicationRecord < ActiveRecord::Base 
    self.abstract_class = true 
    include SpecialMethods 
end 

# app/models/user.rb 
class User < ApplicationRecord 
    include SpecialMethods 

    # Code 
end 

如果您希望將模塊中定義的方法定義爲類方法,請將模塊擴展到ApplicationRecord。

# app/models/application_record.rb 
class ApplicationRecord < ActiveRecord::Base 
    self.abstract_class = true 
    extend SpecialMethods 
end 

希望它能幫助別人!

相關問題