views:

116

answers:

2
A: 

In short, there is no way to do this without some serious hacking. What I suggest you to do is to make a to_serialized method that returns an array that the initialize method accepts to get the same state. If you simply want to copy all instance variables over, you could do this:

class A
  def initialize(instance_variables)
    instance_variables.each do |key, value|
      self.instance_variable_set(key, value)
    end
  end

  def to_serialized
    iv = {}
    self.instance_variables.each do |key|
      iv[key] = self.instance_variable_get(key)
    end
  end
end

And to reload the method, you could do this:

obj_state = object.to_serialized
Object.remove_const('A')
load 'file.rb'
object = A.new(obj_state)

Note that this doesn't nest, so if any of the objects the instance variables refer to is reloaded too, you need to "serialize" them yourself.

dvyjones
+1  A: 

Obviously there's a danger in totally replacing the class definition with a new class definition, whether you're merging the new version or deleting the old version and expecting objects to automatically get updated. That danger is in the fact that the old version of the object may be in an invalid state for the new version. (For example, instance variables that the new version of the class initializies in its initialize method may not have been defined by the old version, but there could aso be subtler bugs than this). So care (and a well-planned upgrade path) is needed no matter how you pull this off.

Given that you know what the version you're upgrading from looks like (which you need in order to upgrade sensibly anyway), it's dead simple to have the new version of the class remove unneeded methods from the old version of the class:

class A
  remove_method :foo
end

And I'm not sure what you're talking about when you say there's problems redefining a method to take a different number of parameters. It works fine for me:

class A
  def foo a
    a
  end
end
ainst=A.new
p(ainst.foo 1) rescue puts($!)
p(ainst.foo 1,2) rescue puts($!)

class A
  def foo a,b
    [a,b]
  end
end
p(ainst.foo 1) rescue puts($!)
p(ainst.foo 1,2) rescue puts($!)

The only thing you can't do (AFAIK) is change the class's superclass. That's defined the first time you define the class, and you're not allowed to change it (though you can specify the same ancestor class again).

class A < Object
end
class A < Object
end
class A < String #TypeError: superclass mismatch for class A
end
Ken Bloom
I totally agree with you, the upgrading must be well planned and controlled. So if a method is removed, the new version should explicitly remove it (by remove_method). The problem with method signature (change of number of arguments) was in fact related to version-mismatch between caller and this class. But reflecting on that, this should also be contained with the "controlled" upgrade path. There shouldn't be any version-mismatch, even outside this class.
bryantsai