Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord Class Methods / Relations self

I am writing some rails code that uses proxy objects around ActiveRecord models. However, whenever a Class Method is called on an ActiveRecord::Relation or ActiveRecord::Associations::CollectionProxy, the value of self is the original ActiveRecord class as opposed to the relation. 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>

Desired functionality would be that the wrapped object in the second case would be the ActiveRecord::Relation object returned by Blah.where. Is there an easy way of achieving this?

like image 864
Slicedpan Avatar asked Oct 21 '14 10:10

Slicedpan


2 Answers

Something along the lines of

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

works for me (prior to rails 4, use scoped rather than all):

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

results in the proxy object holding a relation with the appropriate conditions applied.

like image 182
Frederick Cheung Avatar answered Sep 17 '22 02:09

Frederick Cheung


@FrederickCheung answer is the Right Way To Go. I post this answer to explain why it is happenning.

The mystery sits in active_record/relation/delegation.rb file:

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 relation object, which is just delegating it to the class, hence self is always a class itself here.

like image 38
BroiSatse Avatar answered Sep 19 '22 02:09

BroiSatse