简体   繁体   English

Rails扩展ActiveRecord :: Base

[英]Rails extending ActiveRecord::Base

I've done some reading about how to extend ActiveRecord:Base class so my models would have some special methods. 我已经做了一些关于如何扩展ActiveRecord:Base类的阅读,所以我的模型会有一些特殊的方法。 What is the easy way to extend it (step by step tutorial)? 扩展它的简单方法是什么(逐步教程)?

There are several approaches : 有几种方法:

Using ActiveSupport::Concern (Preferred) 使用ActiveSupport :: Concern(首选)

Read the ActiveSupport::Concern documentation for more details. 阅读ActiveSupport :: Concern文档以获取更多详细信息。

Create a file called active_record_extension.rb in the lib directory. lib目录中创建一个名为active_record_extension.rb的文件。

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)

Create a file in the config/initializers directory called extensions.rb and add the following line to the file: config/initializers目录中创建一个名为extensions.rb的文件,并将以下行添加到该文件中:

require "active_record_extension"

Inheritance (Preferred) 继承(首选)

Refer to Toby's answer . 请参阅Toby的回答

Monkey patching (Should be avoided) 猴子修补(应该避免)

Create a file in the config/initializers directory called active_record_monkey_patch.rb . config/initializers目录中创建一个名为active_record_monkey_patch.rb

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

The famous quote about Regular expressions by Jamie Zawinski can be re-purposed to illustrate the problems associated with monkey-patching. 关于Jamie Zawinski的正则表达式的着名引用可以重新用于说明与猴子修补相关的问题。

Some people, when confronted with a problem, think “I know, I'll use monkey patching.” Now they have two problems. 有些人在遇到问题时会想“我知道,我会使用猴子补丁。”现在他们有两个问题。

Monkey patching is easy and quick. 猴子修补简单快捷。 But, the time and effort saved is always extracted back sometime in the future; 但是,节省的时间和精力总是在未来的某个时候被提取出来; with compound interest. 复利。 These days I limit monkey patching to quickly prototype a solution in the rails console. 这些天我限制猴子修补,以快速在rails控制台中原型解决方案。

You can just extend the class and simply use inheritance. 您可以只扩展类并简单地使用继承。

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

class Foo < AbstractModel
end

class Bar < AbstractModel
end

You can also use ActiveSupport::Concern and be more Rails core idiomatic like: 您还可以使用ActiveSupport::Concern并将其作为更多Rails核心惯用语,如:

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

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

[Edit] following the comment from @daniel [编辑]跟随@daniel的评论

Then all your models will have the method foo included as an instance method and the methods in ClassMethods included as class methods. 然后,所有模型都将foo方法作为实例方法包含在内,并将ClassMethods的方法作为类方法包含在内。 Eg on a FooBar < ActiveRecord::Base you will have: FooBar.bar and FooBar#foo 例如,在FooBar < ActiveRecord::Base你将拥有: FooBar.barFooBar#foo

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

With Rails 4, the concept of using concerns to modularize and DRY up your models has been in highlights. 使用Rails 4,使用关注点模块化和干燥模型的概念已经成为亮点。

Concerns basically allow you to group similar code of a model or across multiple models in a single module and then use this module in the models. 问题基本上允许您在单个模块中将模型的相似代码或多个模型分组,然后在模型中使用此模块。 Here is a example: 这是一个例子:

Consider a Article model, a Event model and a Comment Model. 考虑文章模型,事件模型和评论模型。 A article or A event has many comments. 文章或A事件有很多评论。 A comment belongs to either article or event. 评论属于文章或事件。

Traditionally, the models may look like this: 传统上,模型可能如下所示:

Comment Model: 评论模型:

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

Article Model: 文章模型:

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

Event Model 事件模型

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

As we can notice, there is a significant piece of code common to both Event and Article Model. 我们可以注意到,事件和文章模型都有一个共同的重要代码。 Using concerns we can extract this common code in a separate module Commentable. 使用关注点,我们可以在单独的模块中提取此公共代码。

For this create a commentable.rb file in app/model/concerns. 为此,在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

And Now your models look like this : 现在您的模型看起来像这样:

Comment Model: 评论模型:

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

Article Model: 文章模型:

class Article < ActiveRecord::Base
    include Commentable
end

Event Model 事件模型

class Event < ActiveRecord::Base    
    include Commentable
end

One point I will like to highlight while using Concerns is that Concerns should be used for 'domain based' grouping rather than 'technical' grouping. 在使用Concerns时我想强调的一点是, 应该将关注点用于“基于域”的分组而不是“技术”分组。 For example, a domain grouping is like 'Commentable', 'Taggable' etc. A technical based grouping will be like 'FinderMethods', 'ValidationMethods'. 例如,域分组就像“可评论”,“可标记”等。基于技术的分组将类似于“FinderMethods”,“ValidationMethods”。

Here is a link to a post that I found very useful for understanding concerns in Models. 这是一个帖子链接 ,我发现这对于理解模型中的问题非常有用。

Hope the writeup helps :) 希望写作有帮助:)

Step 1 步骤1

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

Step 2 第2步

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

Step 3 第3步

There is no step 3 :)

Rails 5 provides a built-in mechanism for extending ActiveRecord::Base . Rails 5提供了一个用于扩展ActiveRecord::Base的内置机制。

This is achieved by providing additional layer: 这是通过提供额外的层来实现的:

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

and all models inherit from that one: 并且所有模型都继承自那个:

class Post < ApplicationRecord
end

See eg this blogpost . 参见例如这篇博文

Just to add to this topic, I spent a while working out how to test such extensions (I went down the ActiveSupport::Concern route.) 只是为了添加这个主题,我花了一些时间来研究如何测试这样的扩展(我沿着ActiveSupport::Concern路线走了。)

Here's how I set up a model for testing my extensions. 这是我如何设置模型来测试我的扩展。

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

With Rails 5, all models are inherited from ApplicationRecord & it gives nice way to include or extend other extension libraries. 使用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

Suppose the special methods module needs to be available across all models, include it in application_record.rb file. 假设特殊方法模块需要在所有模型中可用,请将其包含在application_record.rb文件中。 If we wants to apply this for a particular set of models, then include it in the respective model classes. 如果我们想将其应用于特定的模型集,则将其包含在相应的模型类中。

# 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

If you want to have the methods defined in the module as class methods, extend the module to ApplicationRecord. 如果要将模块中定义的方法作为类方法,请将模块扩展到ApplicationRecord。

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

Hope it help others ! 希望能帮到别人!

I have 我有

ActiveRecord::Base.extend Foo::Bar

in an initializer 在初始化程序中

For a module like below 对于像下面这样的模块

module Foo
  module Bar
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM