我阅读了一些关于如何扩展ActiveRecord:Base类的文章,以便我的模型可以拥有一些特殊方法。有什么简单的方法可以扩展它吗(逐步教程)?
我阅读了一些关于如何扩展ActiveRecord:Base类的文章,以便我的模型可以拥有一些特殊方法。有什么简单的方法可以扩展它吗(逐步教程)?
有几种方法:
阅读ActiveSupport::Concern文档以获取更多细节。
在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)
在 config/initializers
目录下创建一个名为 extensions.rb
的文件,然后在该文件中添加以下一行代码:
require "active_record_extension"
请参考Toby的答案。
在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
著名的正则表达式引用语,来自Jamie Zawinski,可以被重新运用来说明与猴子补丁相关的问题。
有些人面对问题时想:“我知道了,我会使用猴子补丁。”现在他们有两个问题了。
猴子补丁很容易而且快速。但是,所节省的时间和精力在未来总会被以复利的形式提取出来。如今,我仅将猴子补丁用于在Rails控制台中快速原型设计解决方案。
environment.rb
的末尾需要使用 require
命令引入文件。我已经在我的回答中添加了这一额外步骤。 - Harish Shetty你可以直接扩展类并使用继承。
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
AbstractModel
中添加 self.abstract_class = true
。Rails将会把该模型识别为抽象模型。 - Harish Shetty你也可以使用ActiveSupport::Concern
,这样更符合Rails核心的惯用法:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
然后,所有您的模型都将具有名为foo
的方法,作为实例方法包含,并且在ClassMethods
中包含的方法作为类方法。例如,在FooBar < ActiveRecord::Base
上,您将拥有:FooBar.bar
和 FooBar#foo
。
http://api.rubyonrails.org/classes/ActiveSupport/Concern.htmlInstanceMethods
已被废弃,请将您的方法放入模块主体中。 - Daniel RikowskiActiveRecord::Base.send(:include, MyExtension)
,然后这对我起作用了。Rails 4.1.9 - 6ft Dan在Rails 4中,使用concerns来模块化和DRY化您的模型的概念备受关注。
Concerns基本上允许您将一个模型或多个模型中类似的代码分组到一个单独的模块中,然后在模型中使用此模块。以下是一个示例:
考虑一个Article模型、一个Event模型和一个Comment模型。一篇文章或一个事件有很多评论。评论属于文章或事件之一。
传统上,这些模型可能看起来像这样:
Comment模型:
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
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
步骤1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
第二步
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
第三步
There is no step 3 :)
在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
# 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
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
补充一下这个话题,我花了一些时间来研究如何测试这样的扩展(我采用了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
我有
ActiveRecord::Base.extend Foo::Bar
在初始化器中
对于下面这样的模块
module Foo
module Bar
end
end