Rails扩展ActiveRecord :: Base

时间:2010-02-24 19:49:38

标签: ruby-on-rails extend rails-activerecord

我已经做了一些关于如何扩展ActiveRecord:Base类的阅读,所以我的模型会有一些特殊的方法。扩展它的简单方法是什么(逐步教程)?

9 个答案:

答案 0 :(得分:331)

有几种方法:

使用ActiveSupport :: Concern(首选)

阅读ActiveSupport::Concern文档了解更多详情。

active_record_extension.rb目录中创建名为lib的文件。

require 'active_support/concern'

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)

在名为config/initializers的{​​{1}}目录中创建一个文件,并将以下行添加到该文件中:

extensions.rb

继承(首选)

请参阅Toby的answer

猴子修补(应该避免)

在名为require "active_record_extension" 的{​​{1}}目录中创建一个文件。

config/initializers

Jamie Zawinski关于正则表达式的着名引用可以重新用于说明与猴子修补相关的问题。

  

有些人在遇到问题时会想“我知道,我会用   猴子补丁。“现在他们有两个问题。

猴子修补简单快捷。但是,节省的时间和精力总是被提取出来 将来的某个时候;复利。这些天我限制猴子修补,以便在rails控制台中快速构建解决方案原型。

答案 1 :(得分:70)

您只需扩展该类,只需使用继承即可。

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

class Foo < AbstractModel
end

class Bar < AbstractModel
end

答案 2 :(得分:21)

你也可以使用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

答案 3 :(得分:16)

使用Rails 4,使用关注点来模块化和干掉模型的概念已经成为亮点。

问题基本上允许您在单个模块中对模型的相似代码或多个模型进行分组,然后在模型中使用此模块。这是一个例子:

考虑文章模型,事件模型和评论模型。文章或A事件有很多评论。评论属于文章或事件。

传统上,模型可能如下所示:

评论模型:

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

我们可以注意到,事件和文章模型都有一个共同的重要代码。使用关注点,我们可以在单独的模块中提取此公共代码。

为此,在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

我在使用问题时要强调的一点是关注点应该用于“基于域”的分组而不是“技术”分组。例如,域名分组就像“可评论” ,'Taggable'等基于技术的分组将类似于'FinderMethods','ValidationMethods'。

我发现link to a post对于理解模型中的问题非常有用。

希望写作有帮助:)

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

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

答案 6 :(得分:4)

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

答案 7 :(得分:4)

使用Rails 5,所有模型都继承自ApplicationRecord&amp;它提供了包含或扩展其他扩展库的好方法。

# 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

希望对别人有所帮助!

答案 8 :(得分:0)

我有

ActiveRecord::Base.extend Foo::Bar

在初始化程序中

对于下面的模块

module Foo
  module Bar
  end
end