From: eden li Date: 2007-03-15T15:03:15+09:00 Subject: Re: When introspection doesn't... Ah, the magic of AssociationProxy. I ran into weirdness with this when I was doing a case/when on the return value of a has_many in one of my models. On Mar 15, 11:04 am, Chad Lester wrote: > And similarly, why could binding an object to a method fail with a type > error, even when the object's class method is reporting that it is of > the correct class. > > Yes, it's possible that the object is overriding both "class" and > "method", but I really don't think so. And even if you did, it doesn't > explain how "bind" figures out that it's not really an Array. Looking at the C source for bind(), it checks the object's underlying class against the receiving class, and does so without going through any of the re-defined methods for the given object. This allows it to peek into the real class for the given object even if the object over- rode Object#class. AFAIK, there's no way to prevent bind() from doing this. > I'd love to know what mechanism they used to achieve this magic. If it > was me, I would have probably made a sub-class of Array. If not that, > then I would have inserted my own find_all with an "extend" call. But > that would also show up when you called method(:find_all). So they're > doing something else. If push comes to shove, I can start pouring > through the rails code... but ugh.. It actually, implicitly, does re-define both of these methods. Take a look at lib/active_record/associations/association_proxy.rb ActiveRecord associations are all implemented using a proxy of a real array as returned by find(). The proxy undefs *all* instance methods except for those that begin with underscores among some other 'important' methods. It then defines a method_missing method which catches any undefined method references and passes them to the underlying array. If the association object defines any method that shares the same name as the underlying array object, it'll be called instead of method_missing which prevents the association proxy from handing it to the underlying array. So, in your case, the HasManyAssociation defines #find_all which causes it to be called instead of method_missing... AssociationProxy defines #target which allows you to retrieve the underlying object: >> Array.instance_method(:find_all).bind(foo.target) => #