ActiveRecord Class Methods / Relationships

I am writing some rails code that uses proxy objects around ActiveRecord models. However, whenever a class method is called on ActiveRecord::Relation

or ActiveRecord::Associations::CollectionProxy

, the value self

is the original ActiveRecord class and not the relationship. For example:

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>

      

The desired functionality would be that the wrapped object in the second case would be the object ActiveRecord::Relation

returned Blah.where

. Is there an easy way to achieve this?

+3


source to share


3 answers


Something along the lines

class Blah < ActiveRecord::Base   
  def self.thing
    MyProxyObject.new(all)
  end
end

      

works for me (up to rails 4, use scoped

, not all):



Blah.where(:foo => 1).thing

      

causes the mediation object to be related to the matching conditions.

+4


source


@FrederickCheung's answer is the right way to go. I am posting this answer to explain why this is happening.

The secret is in the file 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

      

As you can see, it defines a new method on the relationship object that simply delegates it to the class, so self

it is always the class itself.

+1


source


Found a hacky way to do this, I'll leave this unacceptable for a while in case someone comes up with a more elegant solution.

ActiveRecord::Relation.instance_eval do 
  def thing
    if klass.respond_to? :thing
      MyProxyObject.new(self)
    else
      raise NoMethodError.new
    end
  end
end

      

This just adds a method to the class ActiveRecord::Relation

(in practice this should also be defined for ActiveRecord::Associations::CollectionProxy

and ActiveRecord::AssociationRelation

). It checks if the base class has a specific method, and if it creates a proxy object wrapped around itself, instead

0


source







All Articles