【问题标题】:how does this Ruby class method get invoked?这个 Ruby 类方法是如何被调用的?
【发布时间】:2012-07-20 22:24:03
【问题描述】:

Exporting CSV from a rails app 的截屏中,Ryan Bates 展示了以下简单代码。

我试图弄清楚 Product::to_csv 类方法实际上是如何在 ProductController.rb 的第 5 行被调用的,因为它似乎不遵循正常的 Ruby 规则。

product.rb

1 class Product < ActiveRecord::Base   
2   def self.to_csv(options = {})
3     ...
4   end
5 end

products_controller.rb

1 class ProductsController < ApplicationController
2  def index
3    @products = Product.order(:name)
4    respond_to do |format|
5      format.csv { send_data @products.to_csv }
6      ...
7    end
8  end
9 end

由于 to_csv 是一个类方法,我希望调用看起来像 Product::to_csv()。

根据文档,@products 是 ActiveRecord::Relation 的一个实例。为什么发送到 ActiveRecord::Relation 实例的消息会导致 Product 类对象上的方法被调用?更奇怪的是,将发送方和接收方中的 to_csv 重命名为任意名称会导致 NoMethodError,所以也许有一些基于以 to_ 开头的名称的魔法?

我是否遗漏了一些明显的东西?任何澄清将不胜感激。

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    这只是 Rails 所做的事情之一。任何类方法都会自动成为“集合”方法,这意味着它们可用于关系对象。范围和类方法可以这样互换。

    【讨论】:

    • 谢谢,这可以解释这种行为。如果在某个地方的 Rails 指南或文档中提到了这一点,那就太好了。根据您的回答,我查看了源代码。如果我理解正确,ActiveRecord::Relations 将在响应它们时将方法调用委托给关联的类对象,然后最终委托给 :arel
    • 如果您有兴趣进一步探索这一点,我开始研究一个概念验证 gem,它允许您将模型与集合分开。它被称为Herd。我在生产中没有做太多,但你可以查看源代码,看看我必须做什么才能在关系类上"register" methods
    • 我忘了提到 ActiveRecord::Relations 似乎也将方法委托给生成的 Array 对象(必要时触发数据库获取)。它将首先在关联的 ActiveRecord::Base 上查找类方法,然后尝试 Array,最后尝试 :arel 导致的任何内容。假设我在代码中找到了正确的位置。谢谢(你的)信息。我一定会去看看 Herd。
    • 一些实验表明,rails 对all 的含义很聪明——因此,对于调用all 的类方法,这种事情的行为可能与预期的一样。很高兴看到一些文档或代码解释魔术行为在哪些情况下生效。
    【解决方案2】:

    我现在可以回答以下问题:

    为什么要向 ActiveRecord::Relation 的实例发送消息 导致调用 Product 类对象上的方法?

    ActiveRecord::Relation 类用于链接多个方法,而不会实际触发多个 SQL 查询。这样你就可以写Product.where('price &lt;= ?', 100).order(:price).limit(30) 这样的东西,Rails 只会执行一个查询。

    之所以如此神奇,是因为您有一个 ActiveRecord::Relation 实例,直到您尝试访问数据(例如,因为 firstall 调用),那时查询将运行并且您将拥有 @987654326 @ 或他的后代之一。

    长话短说,如果您使用 @products.class 检查类,您会看到 ActiveRecord::Relation 但稍后您有 Product 实例,然后您可以调用 to_csv 方法。

    【讨论】:

    • 我知道 ActiveRecord::Relation 的实例代表 SQL 查询的一部分或全部,并且调用 first 或 all on one 会导致查询执行并创建一个(在这种情况下)实例数组类产品。这并没有解释如何将 to_csv 消息发送到 ActiveRecord::Relation 的实例导致 类方法 to_csv() 被调用。
    猜你喜欢
    • 1970-01-01
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 2021-01-16
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多