From: Jacob Fugal Date: 2005-05-24T06:04:15+09:00 Subject: Re: Array.=== Bug, Rails Bug, or brain failure? On 5/23/05, 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. IANARE (Rails Expert), but my guess it that whatever x.y is is proxying for a wrapped array. One possilbe reason for this could be delayed instantiation, etc. In that case we'd have something like: class ArrayProxy // somehow clean out *all* inherited methods to get a // clean proxy (this even cleans out .class, so be careful! def initialize( &block ) @thunk = block end def contained_array @contained_array ||= @thunk.call end def method_missing( symbol, *args, &block=nil ) contained_array( symbol, *args, &block ) end end So then we can have this code: a = ArrayProxy.new { [ :a, :b, :c ] } a.class # => Array a[0] # => :a a.is_a?( Array ) # => true However, a is still an ArrayProxy, not an Array. Case equality, as you pointed out, is an operator defined on the conditional, not the tested value. So even though a.is_a?( Array ) by the proxy, Array === a is false, since a is not, actually an Array. So how's Array.=== implemented then? I don't know, but my guess is that it's inherited from Class#=== without change, and that Class#=== is implemented in C and thus ignores the proxy in effect on a. In short, you need to be *real* careful when using full out proxies ala method_missing, because once the abstraction begins to leak, things get real confusing. In my opinion cleaning out *everything* (including .class, etc.) is not a good idea, but the decision was obviously intentional. So even if we agree that this have .class get passed through the proxy isn't desirable, it falls into the class of "feature", not bug. :) Jacob Fugal