2010-02-24 63 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 

希望它能帮助别人!

相关问题