Irb and 'singleton can't be dumped'

Hi,

Something changed in irb when I went from 0.9-02.06.12 to the more
recent releases, 0.9-02.06.25 and 0.9-02.07.03, and it’s breaking my
code. I’m using ruby 1.6.7.

Brief background: I am using irb as a command line for a simulation
program. Periodically, the simulated world is dumped to disk. The user
can interrupt the simulation with ctrl-C, and enter the irb command line
to inspect and modify the world. To make this easy, the world object is
used as the self for the irb session. Typing ctrl-D from the command
line continues the simulation. It’s very useful.

After the upgrade, the simulation runs normally (including the periodic
dumps), until you ctrl-C and ctrl-D. After that, the first attempt to
dump results in:

main.rb:55:in dump': singleton can't be dumped (TypeError) from main.rb:55 from main.rb:52:intimes’
from main.rb:52

Interestingly, when I make my World class a subclass of Array (or
another built in class) the problem goes away. So this might be related
to the fact that Ruby stores attrs differently for builtin classes than
for arrays. I’d rather not use this workaround, though.

I’ve managed to condense the test case somewhat, so it should be easy to
poke around with. (Please feel free to reuse this snippet–the software
it is part of will be released under the Ruby license.)

Thanks in advance for any help…

Joel

···

require 'irb’
require ‘irb/completion’

module IRB
def IRB.start_world(world)
unless $irb
IRB.initialize nil
IRB.load_modules
end

 workspace = WorkSpace.new(world)

 $irb = Irb.new(workspace)

 @CONF[:IRB_RC].call($irb.context) if @CONF[:IRB_RC]
 @CONF[:MAIN_CONTEXT] = $irb.context

 trap("INT") do
   $irb.signal_handle
 end

 catch(:IRB_EXIT) do
   $irb.eval_input
 end
 print "\n"

 trap("INT") {
   $interrupt_request = true
 }

end
end

require ‘irb/loader’ # as of irb-0.9-02.06.25, this is needed for
ExtendCommand

class World
#class World < Array # avoids the error
include IRB::ExtendCommand ## to avoid adding singleton methods

private
def q
exit!
end
end

w = World.new

trap(“INT”) {
$interrupt_request = true
}

100.times do |i|
sleep 0.1
puts i
Marshal.dump w

if $interrupt_request
IRB.start_world(w)
$interrupt_request = false
end
end