在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范围调用的类方法)似乎看似合理,但我想知道整个想法是否存在固有问题?
答案 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方法而不是类方法。
简易解决方案:只需选择其他方法名称。