[英]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 : 有几种方法:
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"
Refer to Toby's answer . 请参阅Toby的回答 。
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.bar
和FooBar#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.