[ActiveRecord::Base].collect {|a,b| ...} weirdness

Hey everyone.

My mind has been boggled by an issue I ran into a few hours ago. I am
completely lost as to why the following code behaves the way it does
and would appreciate any hints from you guys. It would already be
super-helpful if others could post their output for the following so
that I can figure out whether this is weirdness specific to my setup
or a global phenomenon.

So far, ActiveRecord::Base and its descendants are the only classes
with which I see this behaviour and furthermore it seems to be limited
to Ruby 1.9.1.

Mayday!

Thanks,
Niels (about to dig into array.c)

$ ruby --version; rails --version; ruby <<EOR
require "rubygems"
gem "activerecord"
require "activerecord"

puts "\nActiveRecord::Base disappears:"
[1,String,'foo',ActiveRecord::Base].collect {|a,b| puts [a,b].inspect}

puts "\nActiveRecord::Base stays:"
[1,String,'foo',ActiveRecord::Base].collect {|a| puts [a].inspect}

puts "\nwtf?"

EOR
ruby 1.9.1p129 (2009-05-12 revision 23412) [x86_64-linux]
Rails 2.3.2

ActiveRecord::Base disappears:
[1, nil]
[String, nil]
["foo", nil]
[nil, nil]

ActiveRecord::Base stays:
[1]
[String]
["foo"]
[ActiveRecord::Base]

wtf?

I can confirm that this does not happen with ruby 1.8.6 on my machine.
I don't have ruby 1.9 installed, so I can't be much help beyond that.
Here are my respective outputs with ruby 1.8.6, just as you'd expect:

[1,String,'foo',ActiveRecord::Base].collect {|a,b| puts [a,b].inspect}

[1, nil]
[String, nil]
["foo", nil]
[ActiveRecord::Base, nil]
=> [nil, nil, nil, nil]

[1,String,'foo',ActiveRecord::Base].collect {|a| puts [a].inspect}

[1]
[String]
["foo"]
[ActiveRecord::Base]
=> [nil, nil, nil, nil]

Thanks Steve!

I've just set up an isolated virtual machine for testing this out a
bit further. On a vanilla Ubuntu installation with 1.9.1p129 (latest
stable release) this also occurred. Using a current nightly snapshot
(of what will probably become 1.9.2) however, everything works as
expected.

I am still puzzled by this but for the time being happy to just use
Ruby nightlies. If anyone following Ruby development closely could
shed some more light on this that would be great though!

Best,
Niels

Since this doesn't seem to be a Rails issue, you will probably have
better luck attracting the attention of the Ruby core team if you post
to the Ruby list. Good luck!

You are of course right, Marnen. But for the unlikely case that anyone
else stumbles over this: starting with r24054 (1.9.1-p218) everything
works as expected again.

Apparently it was an issue with ActiveRecord::Base redefining
respond_to? as r24054 is a result of this bug report:
http://redmine.ruby-lang.org/issues/show/1723

For those interested, I reproduced the corresponding change below.

Best,
Niels

--- vm_insnhelper.c (revision
24053)
+++ vm_insnhelper.c (revision
24054)
@@ -761,7 +761,7
@@
     int
i;
     int argc =
orig_argc;
     const int m = iseq-

argc;

- VALUE
ary;
+ VALUE ary,
arg0;
     int opt_pc =
0;

     th->mark_stack_len =
argc;
@@ -771,15 +771,19
@@
      * => {|a|} => a = [1,
2]
      * => {|a, b|} => a, b = [1,
2]

*/
+ arg0 = argv
[0];
     if (!(iseq->arg_simple & 0x02) && /* exclude {|a|}
*/
             (m + iseq->arg_post_len) > 0 && /* this process is
meaningful
*/
- argc == 1 && !NIL_P(ary = rb_check_array_type(argv[0])))
{ /* rhs is only an array
*/
+ argc == 1 && !NIL_P(ary = rb_check_array_type(arg0))) { /
* rhs is only an array
*/
         th->mark_stack_len = argc = RARRAY_LEN
(ary);

         CHECK_STACK_OVERFLOW(th->cfp,
argc);

         MEMCPY(argv, RARRAY_PTR(ary), VALUE,
argc);
     }
+ else
{
+ argv[0] =
arg0;
+ }

     for (i=argc; i<m; i++)
{
         argv[i] =
Qnil;