ChatGPT解决这个技术问题 Extra ChatGPT

Rails 扩展 ActiveRecord::Base

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

什么样的扩展?我们真的需要更多继续下去。

H
Harish Shetty

有几种方法:

使用 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"

继承(首选)

请参阅 Toby 的answer

猴子补丁(应该避免)

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 文件。我在我的答案中添加了这个额外的步骤。
@HartleyBrody 这只是一个偏好问题。如果您使用继承,则必须引入一个新的 ImprovedActiveRecord 并从中继承,当您使用 module 时,您正在更新相关类的定义。我曾经使用继承(由于多年的 Java/C++ 经验)。这些天我主要使用模块。
具有讽刺意味的是,您的链接实际上是在上下文化并指出人们如何误用和过度使用这句话。但说真的,我很难理解为什么“猴子补丁”在这种情况下不是最好的方法。如果您想添加到多个类,那么模块显然是要走的路。但是,如果您的目标是扩展一个类,那么这难道不是 Ruby 以这种方式扩展类如此简单的原因吗?
@MCB,每个大型项目都很少有关于由于猴子修补而引入的难以定位的错误的故事。这是 Avdi 撰写的一篇关于修补的弊端的文章:devblog.avdi.org/2008/02/23/…。 Ruby 2.0 引入了一个名为 Refinements 的新功能,它解决了猴子修补 (yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice) 的大部分问题。有时,一个功能只是为了迫使你去试探命运。有时你会这样做。
@TrantorLiu 是的。我已经更新了答案以反映最新的文档(看起来 class_methods 是在 2014 年推出的github.com/rails/rails/commit/…
T
Toby Hede

您可以扩展类并简单地使用继承。

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

class Foo < AbstractModel
end

class Bar < AbstractModel
end

我喜欢这个想法,因为这是一种标准的做法,但是......我收到一个错误表'moboolo_development.abstract_models'不存在:SHOW FIELDS FROM abstract_models。我应该把它放在哪里?
self.abstract_class = true 添加到您的 AbstractModel。 Rails 现在会将模型识别为抽象模型。
哇!没想到这是可能的。早些时候尝试过,但当 ActiveRecord 在数据库中寻找 AbstractModel 时感到窒息而放弃了。谁知道一个简单的二传手会帮我把事情弄干! (我开始畏缩......这很糟糕)。谢谢托比和哈里什!
在我的情况下,这绝对是最好的方法:我不是在这里用外来方法扩展我的模型能力,而是为我的应用程序的类似行为对象重构常用方法。继承在这里更有意义。没有首选方法,但有两种解决方案,具体取决于您想要实现的目标!
这对我在 Rails4 中不起作用。我创建了 abstract_model.rb 并放在我的模型目录中。在模型内部它有 self.abstract_class = true 然后我继承了我的其他模型... User < AbstractModel.在控制台中我得到:用户(调用'User.connection'建立连接)
n
nikola

您还可以使用 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


请注意,自 Rails 3.2 起,InstanceMethods 已被弃用,只需将您的方法放入模块主体中即可。
我将 ActiveRecord::Base.send(:include, MyExtension) 放在初始化程序中,然后这对我有用。导轨 4.1.9
y
yesnik

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

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

考虑一个文章模型、一个事件模型和一个评论模型。一篇文章或一个事件有很多评论。评论属于文章或事件。

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

评论型号:

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

我们可以注意到,Event 和 Article Model 都有一段重要的代码。使用关注点,我们可以在单独的可注释模块中提取此公共代码。

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

在使用关注点时,我想强调的一点是关注点应该用于“基于域”的分组而不是“技术”分组。例如,域分组类似于“Commentable”、“Taggable”等。基于技术的分组类似于“FinderMethods”、“ValidationMethods”。

这是一个link to a post,我发现它对于理解模型中的关注点非常有用。

希望这篇文章有帮助:)


c
codenamev

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

我想第 2 步必须放在 config/environment.rb 中。它对我不起作用:(。你能再写一些帮助吗?谢谢。
A
Adobe

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


A
Ashik Salman

在 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

希望它可以帮助别人!


W
Will Tomlins

只是为了补充这个主题,我花了一段时间研究如何测试这些扩展(我走的是 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

E
Ed Richards

我有

ActiveRecord::Base.extend Foo::Bar

在初始化器中

对于像下面这样的模块

module Foo
  module Bar
  end
end