On May 23, 2005, at 2:30 PM, Markus wrote:

>
> On Mon, 2005-05-23 at 13:21, Jamis Buck wrote:
>
>> Markus,
>>
>> when x.has_many :y, the :y property is not an array. It's an object
>> that walks and talks like an array. (Just try using x.y.find
>> sometime--it doesn't work like the Array version, which bit me hard
>> several times in the past.)
>>
>
> I'd buy that except:
>
> 1) x.y.class.name == 'Array'
>
> 2) x.y.class == Array
>
> 3) x.y.class.ancestors == [].class.ancestors
>
> 4) as I mentioned,
>
>
>>> Adding:
>>>
>>> class Array
>>>     def self.===(other)
>>>         other.is_a? self
>>>         end
>>>     end
>>>
>>> right above the code in question fixes it.
>>>
>
> That's a little strong for duck typing, isn't it?  I my book that's
> somewhere past duct taping and well into bailing wire territory.  I
> admit is possible, but it seems unlikely...

Indeed, truth is stranger than fiction. ;) Consider this snippet from  
AR's association_proxy.rb. (Here's where we're getting into some  
pretty black magic):

    alias_method :proxy_respond_to?, :respond_to?
    instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil 
\?|^proxy_respond_to\?|^send)/ }

    def method_missing(symbol, *args, &block)
      load_target
      @target.send(symbol, *args, &block)
    end

    def respond_to?(symbol, include_priv = false)
      proxy_respond_to?(symbol, include_priv) || (load_target &&  
@target.respond_to?(symbol, include_priv))
    end

It's proxying to an array, and undefining all the relevant methods on  
the proxy so that things like #class get passed to the proxy, too.  
Unfortunately, it looks like === doesn't get proxied, and I'm not  
sure why. Perhaps Ruby does some optimizing under the covers?

At any rate, yah. It can be annoying. But you really are dealing with  
a proxy, and not an array. At least, not directly.

- Jamis