ActiveRecord :: Relation命中Array方法而不是我的类方法

时间:2013-04-15 16:50:58

标签: ruby-on-rails scopes

在Rails 3.2模型中,希望创建一个“to_csv”类方法来从ActiveRecord范围生成CSV输出,如下所示:

class Post
   # ...
   def self.to_csv(options = {})
     CSV.generate(options) do |csv|
       scoped.each {|post| csv << record.attributes.values_at('title', 'text')}
     end
   end
end

我希望我可以像这样使用它:

User.first.posts.to_csv

但是,该方法似乎被Array #to_csv方法覆盖。即使我这样做:

User.first.posts.scoped.to_csv

... User.first.posts.scoped 的结果显然是一个ActiveRecord :: Relation,我仍然使用了Array方法。如果我将方法重命名为“to_csvx”,它会按预期工作。

我希望有人可以解释ActiveRecord :: Relation对象如何/为什么优先命中Array #to_csv方法而不是to_csv类方法。虽然这(一种期望被AR范围调用的类方法)似乎看似合理,但我想知道整个想法是否存在固有问题?

1 个答案:

答案 0 :(得分:4)

好的,对于回答我自己的问题非常蹩脚,但基于来自@m_x和Railscast的评论239 http://railscasts.com/episodes/239-activerecord-relation-walkthrough我更深入了解Rails源代码......我可以看到关系respond_to荣誉,method_missing更喜欢将Array方法用于未明确定义为Relation的任何其他方法。

# from rails/active_record/relation/delegation.rb

def respond_to?(method, include_private = false)
  super || Array.method_defined?(method) ||
    @klass.respond_to?(method, include_private) ||
    arel.respond_to?(method, include_private)
end

protected

def method_missing(method, *args, &block)
  if Array.method_defined?(method)
    ::ActiveRecord::Delegation.delegate method, :to => :to_a
    to_a.send(method, *args, &block)
  elsif @klass.respond_to?(method)
    ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
    scoping { @klass.send(method, *args, &block) }
  elsif arel.respond_to?(method)
    ::ActiveRecord::Delegation.delegate method, :to => :arel
    arel.send(method, *args, &block)
  else
    super
  end
end

因此,如果您将ActiveRecord“作用域”类方法命名为与Array实例方法相同,则Relation将转换为Array,并且将调用Array方法而不是类方法。

简易解决方案:只需选择其他方法名称。