Reputation: 5015
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?
Upvotes: 5
Views: 1215
Reputation: 44725
@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.
Upvotes: 1
Reputation: 84182
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.
Upvotes: 5
Reputation: 5015
Have found a hackish way of doing this, I will leave this unaccepted for a while on the off chance that someone will come 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 the method to the ActiveRecord::Relation
class (in practice this should also be defined for ActiveRecord::Associations::CollectionProxy
and ActiveRecord::AssociationRelation
). It checks to see if the base class has the method defined, and if so creates the proxy object wrapped around itself instead
Upvotes: 0