扩展 ActiveRecord: : Base 的 Rails

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

69755 次浏览

第一步

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 :)

有几种方法:

使用 ActiveSupport: : Concern (首选)

有关更多细节,请阅读 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"

继承(首选)

参考托比的 回答

猴子修补(应避免)

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关于正则表达式的著名引文可以重新用来说明与 Monkey-patching 相关的问题。

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

猴子修补程序简单快捷,但是节省的时间和精力总是被提取回来 未来的某个时候; 有复利。这些天,我限制猴子补丁,以快速原型的解决方案在轨道控制台。

您只需扩展该类并简单地使用继承即可。

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


class Foo < AbstractModel
end


class Bar < AbstractModel
end

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

为了补充这个话题,我花了一段时间研究如何测试这样的扩展(我沿着 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

在 Rails 4中,使用关注点来模块化和 DRY 模型的概念已经成为重点。

关注点基本上允许您将一个模型的相似代码或多个模型的相似代码组合在一个模块中,然后在模型中使用这个模块。下面是一个例子:

考虑一个文章模型、一个事件模型和一个注释模型。一篇文章或一个事件有许多评论。注释属于项目或事件。

传统上,模型可能是这样的:

评论模式:

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

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

为此,在 app/model/Concers 中创建一个 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 时,我想强调的一点是 关注点应该用于“基于域的”分组,而不是“技术的”分组。。例如,域分组类似于‘ Commentable’、‘ Taggable’等。基于技术的分组类似于“ FinderMethod”、“ ValidationMethod”。

这里有一个 链接到一个帖子,我发现它对于理解模型中的关注点非常有用。

希望这篇文章能有所帮助:)

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

例如 这篇博文

在 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

希望它能帮助别人!