[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;