bry*_*sai 5 ruby metaprogramming
我做了一些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
Run Code Online (Sandbox Code Playgroud)
样本输出:
$ 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
Run Code Online (Sandbox Code Playgroud)
以下是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
Run Code Online (Sandbox Code Playgroud)
显然,用新的类定义完全替换类定义是存在危险的,无论您是合并新版本还是删除旧版本并期望对象自动更新。这种危险在于,旧版本的对象对于新版本可能处于无效状态。(例如,新版本的类在其initialize方法中初始化的实例变量可能尚未由旧版本定义,但也可能存在比这更微妙的错误)。因此,无论您如何实现这一点,都需要小心(以及精心计划的升级路径)。
鉴于您知道要升级的版本是什么样的(无论如何您都需要明智地升级),让新版本的类从旧版本的类中删除不需要的方法是非常简单的:
class A
remove_method :foo
end
Run Code Online (Sandbox Code Playgroud)
当你说重新定义一个方法来采用不同数量的参数时存在问题时,我不确定你在说什么。这对我来说可以:
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($!)
Run Code Online (Sandbox Code Playgroud)
你唯一不能做的事情(AFAIK)就是改变类的超类。这是在您第一次定义类时定义的,并且不允许您更改它(尽管您可以再次指定相同的祖先类)。
class A < Object
end
class A < Object
end
class A < String #TypeError: superclass mismatch for class A
end
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1719 次 |
| 最近记录: |