Search code examples
ruby-on-railsrubyclasscomparison-operators

Ruby === not acting as it when left-hand argument is a class


I'm using Ruby 1.8.7 with Rails 3.0.1 and am having a problem whose root cause appears to be the "Array === object" operation. I saw the same behavior before in a class of my own creation, and programmed around it by not using the "===" operator (I assumed that there was some flaw in my knowledge of Ruby, which is still rather limited). But now that it is happening inside ActionPack, I need to do something about it.

This surfaced when the FormHelper "fields_for" was not acting the way it should. The following view code snippet ("<% %>" removed to improve readability):

form_for @coupon do |f|
  ...
  f.fields_for @coupon.participants do |cp|
    ...
  end
end

gave the error "ActionView::Template::Error (undefined method `model_name' for Array:Class):" inside the form_for helper method. I determined that it was executing the wrong branch of a "case" command, set a breakpoint and started testing. Here are the results:

/Library/Ruby/Gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/form_helper.rb:1152
case record_or_name_or_array
(rdb:1) pp record_or_name_or_array.instance_of? Array
true
(rdb:1) pp Array === record_or_name_or_array
false
(rdb:1) pp Array.object_id
2148267660
(rdb:1) pp record_or_name_or_array.class.object_id
2148267660

This shows pretty definitively that, while "record_or_name_or_array" is definitely an array, "Array === record_or_name_or_array" is returning false.

BTW, in case you're suspecting that "@f.fields_for" is the wrong syntax, I tried it both with and without the "@f." and got the same result. I have also restarted RoR and my machine and the results remain unchanged.


Solution

  • Try this:

    @coupon = Coupon.last
    Array === @coupon.participants #=> false
    Array === @coupon.participants.find(:all) #=> true
    

    Association @coupon.participants is not an array, it is a proxy. The reason why @coupon.participants.class == Array is true is described in activerecord-3.0.9/lib/active_record/associations/association_proxy.rb:25

    Added: Another interesting experiment would be @coupon.participants.superclass.