Context: I’m programming a MUD in Ruby for fun and profit–if one
considers that an increase in knowledge == profit.
Up to this point, I’ve only ever done web work with Ruby using Rails,
Merb, etc., so I wanted to embark on a from-scratch project, and MUDs
happen to be a favorite old pastime of mine
One of the things I’m trying to do is implement a development server
that “reloads” my source files automatically when they’re updated,
just like Rails or Merb does. Meaning that I can connect to the MUD
server and explore with my test character, while updating my source
files to change behavior around on the fly.
So, reading through Merb’s way of doing it, I’ve gathered that in
order to properly “reload a class”, one must use remove_const to
“undefine” the Class constant, and then use Kernel.load to reload the
Class’ source file. Simple stuff, and with a separate thread that
continuously monitors my source files for changes in their
modification time, we’re golden. It works great… with one exception.
What about objects of a Class that were already instantiated – before
the Class is reloaded with its changes? Those objects are essentially
still bound to the “old” Class, I’ve found, and do not reflect the
reloaded Class’s changes. This doesn’t really affect Rails or Merb,
since Controller and Model instances do not persist between HTTP
requests, but it certainly affects a MUD, where my test character (and
basically everything else) is always in the object space.
The following IRB session demonstrates: (also on pastie:
http://pastie.org/258400)
>> puts File.read('foo.rb')
class Foo
def hello
"Hello, world!"
end
end
=> nil
>> require 'foo'
=> true
>> f = Foo.new
=> #<Foo:0x117fbb4>
>> f.hello
=> "Hello, world!"
# Now I update foo.rb...
>> puts File.read('foo.rb')
class Foo
def hello
"BRAND NEW Hello World!"
end
end
=> nil
# Reload the file using remove_const and Kernel.load
>> Object.send(:remove_const, :Foo)
=> Foo
>> Kernel.load('foo.rb')
=> true
# Existing instance of Foo DOES NOT use the updated method.
>> f.hello
=> "Hello, world!"
# New object DOES use the updated method.
>> f2 = Foo.new
=> #<Foo:0x112c6f8>
>> f2.hello
=> "BRAND NEW Hello World!"
So here’s the question – is there some way to get an object to…
erm, “reset” its Class? Sounds like evil voodoo that could potentially
make things explode, but I’d truly like to see this thing work If
it’s just not possible though, I can live with that.
BTW – By not doing the remove_const on the Class, I can sort of get
this to work, because reloading the file with Kernel.load will
effectively just reopen the class definition, adding the new changes,
but also not removing methods I’ve deleted. And this does make
things explode, like DataMapper for one, and I get all kinds of
“already initialized constant” warnings.
The only thing I can think of is to try to finagle a way to copy
existing objects into the newly defined Class, using serialization or
something crazy, but that sounds really messy and probably not worth it.
Any comments? – ideas / thoughts / similar-experience / you’re-an-
idiot-for-even-trying-this? Thanks!
Brent