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