我正在编写一些使用ActiveRecord模型周围代理对象的rails代码。但是,只要在ActiveRecord::Relation
或ActiveRecord::Associations::CollectionProxy
上调用类方法,self
的值就是原始的ActiveRecord类,而不是关系。例如:
class Blah < ActiveRecord::Base
def self.thing
MyProxyObject.new(self)
end
end
Blah.thing #<MyProxyObject @wrapped=Blah>
Blah.where(:column => 'value').thing #<MyProxyObject @wrapped=ActiveRecord::Relation>
所需的功能是第二种情况下的包装对象是ActiveRecord::Relation
返回的Blah.where
对象。有没有一种简单的方法来实现这一目标?
答案 0 :(得分:5)
的内容
class Blah < ActiveRecord::Base
def self.thing
MyProxyObject.new(all)
end
end
适用于我(在rails 4之前,使用scoped
而不是全部):
Blah.where(:foo => 1).thing
导致代理对象与所应用的适当条件保持关系。
答案 1 :(得分:1)
@FrederickCheung回答是正确的方法。我发布这个答案来解释它为什么会发生。
这个谜团位于active_record/relation/delegation.rb
档案中:
def self.delegate_to_scoped_klass(method)
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.#{method}(*args, &block) }
end
RUBY
else
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.send(#{method.inspect}, *args, &block) }
end
RUBY
end
end
正如您所看到的,它在关系对象上定义了一个新方法,它只是将它委托给类,因此self
在这里始终是一个类。
答案 2 :(得分:0)
已经找到了这样做的一种骇人听闻的方式,我会暂时搁置一下,因为有人会想出一个更优雅的解决方案。
ActiveRecord::Relation.instance_eval do
def thing
if klass.respond_to? :thing
MyProxyObject.new(self)
else
raise NoMethodError.new
end
end
end
这只是将方法添加到ActiveRecord::Relation
类(实际上,这也应该为ActiveRecord::Associations::CollectionProxy
和ActiveRecord::AssociationRelation
定义。它检查基类是否已定义方法,如果是,则创建代理对象,而不是