Ruby on rails 关系点击数组方法而不是我的类方法
在Rails 3.2模型中,我们希望创建一个“to_csv”类方法,从ActiveRecord作用域生成csv输出,如下所示:Ruby on rails 关系点击数组方法而不是我的类方法,ruby-on-rails,scopes,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
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
但是,该方法似乎被数组#to_csv方法覆盖。即使我这样做:
User.first.posts.scoped.to_csv
…并且User.first.posts.scoped的结果显式是一个ActiveRecord::Relation,我仍然使用了数组方法。如果我将该方法重命名为类似“to_csvx”的名称,它将按预期工作
我希望有人能解释ActiveRecord::Relation对象如何/为什么优先命中数组#to_csv方法而不是to_csv类方法。虽然这(一个期望被AR作用域调用的类方法)似乎是合理的,但我想知道整个想法是否存在固有的问题?好的,回答我自己的问题太蹩脚了,但是基于@m_x和Railscast 239的评论,我更深入地了解了Rails源代码。。。我可以看到Relation
response\u to
honors,而method\u missing
更喜欢数组方法,而不是为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“作用域”类方法命名为与数组实例方法相同的方法,则关系将转换为数组,并且将调用数组方法而不是类方法
简单的解决方案:只需选择另一个方法名称。好的,回答我自己的问题太蹩脚了,但基于@m_x和Railscast 239的评论,我对Rails源代码进行了更深入的研究。。。我可以看到Relation
response\u to
honors,而method\u missing
更喜欢数组方法,而不是为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“作用域”类方法命名为与数组实例方法相同的方法,则关系将转换为数组,并且将调用数组方法而不是类方法
简单的解决方案:只需选择另一个方法名称。表面上看,这似乎类似于,但问题的实际点是,如何通过将其链接到关系链的末端,而不是将其作为参数传递,来获取需要转换的记录。我只是在这里猜测:您的
数组#to_csv
方法似乎来自gem/monkey补丁(AFAIK,它不是来自rails本身)。也许这个gem也会修改关系
,以便将委托给_csv
委托给_a(请参阅)。这将解释您的问题,以及为什么使用另一个方法名有效。我应该注意到Ruby版本(1.9.3)这里显示了Array to_csv方法:我的错,我在Ruby 1.8中的一个旧框中尝试了这个方法。我不明白为什么ruby core中会有这样的方法?表面上看,这似乎很相似,但问题的实质是,我如何通过将这些记录链接到关系链的末端,而不是将它们作为参数传入,来获取需要转换的记录。我只是在这里猜测:您的数组#to_csv
方法似乎来自gem/monkey补丁(AFAIK它不是rails本身的)。也许这个gem也会修改关系
,以便将委托给_csv
委托给_a(请参阅)。这将解释您的问题,以及为什么使用另一个方法名有效。我应该注意到Ruby版本(1.9.3)这里显示了Array to_csv方法:我的错,我在Ruby 1.8中的一个旧框中尝试了这个方法。我不明白为什么在ruby内核中会有这样的方法?回答你自己的问题并不是站不住脚的,事实上你甚至应该接受它。为社会做出贡献并帮助人们。。。很好地挖掘了源头。我觉得奇怪的是数组方法比关系方法更受欢迎,也许你应该建议在Github上安装rails补丁。回答你自己的问题并不难,事实上你甚至应该接受它。为社会做出贡献并帮助人们。。。很好地挖掘了源头。我觉得奇怪的是,数组方法比关系方法更受欢迎,也许你应该建议在github上安装rails补丁