Я провел несколько экспериментов с динамической загрузкой / выгрузкой / обновлением класса Ruby в качестве реализации инфраструктуры плагинов.Я обнаружил несколько моментов:
- Если загружать новую версию того же класса без предварительной ее выгрузки, новая, по сути, «top» или «объединяется» с предыдущей версией.Для всех существующих объектов, созданных в предыдущей версии, определение их класса будет «обновлено».
- Выгрузка класса не влияет на существующие объекты, созданные в этом классе.Существующие объекты остаются с той версией, которая только что была выгружена.(класс больше не может использоваться, но не объекты, уже созданные)
- Если загрузка новой версии после выгрузки предыдущей версии, новые созданные объекты будут иметь новую версию.Однако старые объекты, созданные до загрузки новой версии, не будут затронуты и будут по-прежнему иметь старую версию.
Мой вопрос заключается в том, существует ли простой способ сделать существующий объект, созданный из старойверсия класса «переключиться» на новую версию (но не объединенную версию старой и новой версии)?Мне кажется, что возможный способ сделать это - воссоздать объект после выгрузки / загрузки, который не подходит для плагинов (не хочу, чтобы он был уничтожен).
Обновление : мое намерение состояло в том, чтобы обновлять существующие объекты новой версией, без проблемы объединения старой версии с новой версией (например, изменение количества аргументов или удаление метода).Выгрузка, а затем повторная загрузка, кажется, является самым чистым способом сделать это, хотя вы должны отслеживать все такие объекты и воссоздавать их при необходимости.Кроме того, дорогие объекты могут не подходить для воссоздания.Это оставляет меня со вторым вариантом, предотвращая неожиданное слияние.Пока ни один метод не удален, ни одна сигнатура метода не изменена, объединение должно работать нормально.
Ниже приведена моя тестовая программа:
$ 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
Ниже представлены 3 версии класса A:
$ 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