I wrote a convenience ActiveRecord extension to delegate methods to a base object (based on multi-table inheritance)
class ActiveRecord::Base def self.acts_as(base) class_eval %Q{ def method_missing(method, *args, &blk) #{base}.send(method, *args, &blk) rescue NoMethodError super end } end end
I have a state class and a base class
# state class class MyState < ActiveRecord::Base belongs_to :my_object acts_as :my_object end # base class class MyObject < ActiveRecord::Base has_one :head, :class_name => 'MyState' has_one :t开发者_StackOverflow中文版ail, :class_name => 'MyState' end
When I tried this out, I found out that it doesn't work in some cases. More specifically,
> MyState.first.some_method_in_base nil > MyObject.first.tail.some_method_in_base NoMethodError: undefined method `some_method_in_base' for #<ActiveRecord::Associations::HasOneAssociation:0xABCDEFG>
Can anyone enlighten me as to why one works and the other doesn't?
When you run MyObject.first.tail
the object that actually responds is an AssociationProxy class that
has most of the basic instance methods removed, and delegates # unknown methods to @target via method_missing
You can get more details about the proxy running:
MyObject.first.proxy_owner
MyObject.first.proxy_reflection
MyObject.first.proxy_target
If you look in the code, you can see that the AssociationProxy proxies the method some_method_in_base to your MyState class only if MyState responds to some_method_in_base as you can see in the code below.
private
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
if load_target
if @target.respond_to?(method)
@target.send(method, *args, &block)
else
super
end
end
end
Therefore, the method_missing you have defined in the target class is never called.
精彩评论