ActiveRecord类方法/关系自我

时间:2014-10-21 10:29:03

标签: ruby-on-rails activerecord

我正在编写一些使用ActiveRecord模型周围代理对象的rails代码。但是,只要在ActiveRecord::RelationActiveRecord::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对象。有没有一种简单的方法来实现这一目标?

3 个答案:

答案 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::CollectionProxyActiveRecord::AssociationRelation定义。它检查基类是否已定义方法,如果是,则创建代理对象,而不是