在ruby中加载/卸载/更新类

我做了一些Ruby类动态加载/卸载/更新实验,实现了插件基础设施.我发现了几点:

>如果在没有首先卸载它的情况下加载同一类的新版本,则新版本基本上与先前版本“顶部”或“合并”.使用先前版本创建的所有现有对象都将使其类定义“更新”.
>卸载类不会影响使用此类创建的现有对象.现有的对象可以保留任何卸载的版本. (类不能再使用,但不能使用已创建的对象)
>如果在卸载先前版本后加载新版本,则创建的新对象将是新版本.但是,在加载新版本之前创建的旧对象不会受到影响,并且仍然是旧版本.

我的问题是,是否有一种简单的方法可以将现有对象从旧版本“switch”创建到新版本(但不是旧版本和新版本的合并版本)?在我看来,可能的方法是在卸载/加载后重新创建对象,这不适合插件(不希望它被销毁).

更新:我的意图是使用新版本更新现有对象,而不会将旧版本与新版本合并(例如更改参数数量或删除方法).卸载然后重新加载似乎是最干净的方法,尽管你必须跟踪所有这些对象并在需要时重新创建它们.此外,昂贵的物体可能不适合重新创建.这给我留下了第二个选项,禁止意外合并发生.只要没有删除方法,没有方法签名改变,合并应该可以正常工作.

以下是我的测试程序:

$cat test.rb
load 'v1.rb'
puts "=> 'v1.rb' loaded"
a1 = A.new
puts "=> object a1(#{a1}) created"
a1.common
a1.method_v1
load 'v2.rb'
puts '',"=> class A updated by 'v2.rb'"
a1.common
a1.method_v1
a1.method_v2

a2 = A.new
puts '',"=> object a2(#{a2}) created"
a2.common
a2.method_v1
a2.method_v2

Object.send(:remove_const, 'A')
puts '',"=> class A unloaded"

A.new rescue puts $!

puts '',"=> class A does not exist now"
a1.common
a1.method_v1
a1.method_v2 rescue puts $!
a2.common
a2.method_v1
a2.method_v2

load 'v3.rb'
puts '',"=> 'v3.rb' loaded"
a1.common
a1.method_v1
a1.method_v2 rescue puts $!
a1.method_v3 rescue puts $!
a2.common
a2.method_v1
a2.method_v2
a2.method_v3 rescue puts $!

a3 = A.new
puts '',"=> object a3(#{a3}) create"
a3.common
a3.method_v1 rescue puts $!
a3.method_v2 rescue puts $!
a3.method_v3

样本输出:

$ruby test.rb
=> 'v1.rb' loaded
=> object a1(#<A:0x1042d4b0>) created
#<A:0x1042d4b0>: common: v1
#<A:0x1042d4b0>: method v1

=> class A updated by 'v2.rb'
#<A:0x1042d4b0>: common: v2
#<A:0x1042d4b0>: method v1
#<A:0x1042d4b0>: method v2

=> object a2(#<A:0x1042cec0>) created
#<A:0x1042cec0>: common: v2
#<A:0x1042cec0>: method v1
#<A:0x1042cec0>: method v2

=> class A unloaded
uninitialized constant A

=> class A does not exist now
#<A:0x1042d4b0>: common: v2
#<A:0x1042d4b0>: method v1
#<A:0x1042d4b0>: method v2
#<A:0x1042cec0>: common: v2
#<A:0x1042cec0>: method v1
#<A:0x1042cec0>: method v2

=> 'v3.rb' loaded
#<A:0x1042d4b0>: common: v2
#<A:0x1042d4b0>: method v1
#<A:0x1042d4b0>: method v2
undefined method `method_v3' for #<A:0x1042d4b0>
#<A:0x1042cec0>: common: v2
#<A:0x1042cec0>: method v1
#<A:0x1042cec0>: method v2
undefined method `method_v3' for #<A:0x1042cec0>

=> object a3(#<A:0x1042c3f8>) create
#<A:0x1042c3f8>: common: v3
undefined method `method_v1' for #<A:0x1042c3f8>
undefined method `method_v2' for #<A:0x1042c3f8>
#<A:0x1042c3f8>: method v3

以下是A类的3个版本:

$cat v1.rb
class A
  def common
    puts "#{self}: common: v1"
  end
  def method_v1
    puts "#{self}: method v1"
  end
end

$cat v2.rb
class A
  def common
    puts "#{self}: common: v2"
  end
  def method_v2
    puts "#{self}: method v2"
  end
end

$cat v3.rb
class A
  def common
    puts "#{self}: common: v3"
  end
  def method_v3
    puts "#{self}: method v3"
  end
end
最佳答案
显然,使用新的类定义完全替换类定义存在危险,无论是合并新版本还是删除旧版本并期望对象自动更新.这种危险是因为旧版本的对象可能处于新版本的无效状态. (例如,初始化方法中初始化类的新版本可能尚未由旧版本定义的实例变量,但可能会出现比此更微妙的错误).无论你如何解决这个问题,都需要小心(以及精心规划的升级路径).

鉴于你知道你正在升级的版本看起来是什么样的(无论如何你都需要升级),让新版本的类从旧版本的类中删除不需要的方法是很简单的:

class A
  remove_method :foo
end

当你说重新定义一个方法来获取不同数量的参数时,我不确定你在谈论什么.这对我来说可以:

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($!)

你不能做的唯一事情(AFAIK)就是改变班级的超类.这是在您第一次定义类时定义的,并且您不允许更改它(尽管您可以再次指定相同的祖先类).

class A < Object
end
class A < Object
end
class A < String #TypeError: superclass mismatch for class A
end

转载注明原文:在ruby中加载/卸载/更新类 - 代码日志