What collection type does the has_many macro create?

Can someone explain why you can’t use the return value of an
ActiveRecord::Base collection in a case() statement properly? Here’s
an example:

item = User.find_by_username(‘me’).books
[#<Book:0xb771b4b0 … snip

case item; when Array then ‘this is an array’; else “not an array, but a(n) #{item.class}”; end
=> “not an array, but a(n) Array”

item === Array
=> true

Can someone explain what’s going on here? I’m baffled. The workaround
I’ve come up with is to insert

item = item.to_a if item === Array

before the case statement, but that’s sooo hacky.

On Oct 9, 2006, at 9:43 AM, eden li wrote:

Can someone explain what’s going on here? I’m baffled. The
workaround
I’ve come up with is to insert

item = item.to_a if item === Array

before the case statement, but that’s sooo hacky.

Indeed, has_many and friends do not return an Array, they return a
subclass of

ActiveRecord::Associations::AssociationCollection

defined in

active_record/associations/association_collection.rb

– fxn

What sort of magic is going on to have it return Array as its class
then? I’m still baffled about the following console output:

item.class
=> Array

item === Array
=> true

On Oct 9, 2006, at 10:34 AM, eden li wrote:

What sort of magic is going on to have it return Array as its class
then? I’m still baffled about the following console output:

item.class
=> Array

item === Array
=> true

I got something, ActiveRecord::Associations::AssociationProxy
redefines === and defines

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

I traced method_missing is called whenever object.assoc.class is
called and delegates the call to @target, which is a true Array. That
explains partially what we see, but I fail to understand why is
method_missing called at all with a method inherited from Object.

– fxn

On Oct 9, 2006, at 2:32 PM, Xavier N. wrote:

I got something, ActiveRecord::Associations::AssociationProxy
method_missing called at all with a method inherited from Object.
Oh yes, there’s this at the top of that class

instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil
?|^proxy_respond_to?|^proxy_extend|^send)/ }

which effectively removes Object#class and thus method_missing is
triggered.

We are getting closer, but not yet explaining that === fails to match
Array in the case statement.

– fxn

Xavier N. wrote:

I got something, ActiveRecord::Associations::AssociationProxy
redefines === and defines

…snip…

Ah, I didn’t think to look one class up. That explains it.

… I fail to understand why is method_missing called at all
with a method inherited from Object.

I’m guessing it’s this line (#7 of association_proxy.rb in rails
1.1.6):

instance_methods.each { |m| undef_method m unless m =~
/(^__|^nil?|^proxy_respond_to?|^proxy_extend|^send)/ }

It looks like Ruby doesn’t even use the === operator in the case()
statement:

irb(main):005:0> class C; def ===(k); puts “called=== with
#{k.inspect}”;true; end; end
=> nil
irb(main):006:0> case C.new; when String; ‘string’; else; ‘not’; end
=> “not”
irb(main):007:0> C.new===String
called=== with String
=> true

Maybe this question is ready to be ported over to ruby-talk.

On Oct 9, 2006, at 3:09 PM, eden li wrote:

=> true

Maybe this question is ready to be ported over to ruby-talk.

Great!

eden li <[email protected]…> writes:

Xavier N. wrote:

I got something, ActiveRecord::Associations::AssociationProxy
redefines === and defines

…snip…

It’s also useful to understand that a === b often isn’t the same as b
=== a

irb(main):001:0> 1 === Fixnum
=> false
irb(main):002:0> Fixnum === 1
=> true

case 1; when Fixnum; end # Will actually compare Fixnum === 1

Ah, that’s it. So AssociationProxy won’t ever be able to intercept the
=== call in the case/when, thus never allowing it to match the “when
Array” statement. That clears it up…

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs