如何在 Psych 中反序列化以返回现有对象,例如类对象?
做一个类的序列化,我可以做
require "psych"
class Class
yaml_tag 'class'
def encode_with coder
coder.represent_scalar 'class', name
end
end
yaml_string = Psych.dump(String) # => "--- !<class> String\n...\n"
但是如果我尝试这样做Psych.load
,我会得到一个匿名类,而不是 String 类。
正常的反序列化方法是Object#init_with(coder)
,但这只会改变现有匿名类的状态,而我想要 String 类。
Psych::Visitors::ToRuby#visit_Psych_Nodes_Scalar(o)
在某些情况下,他们不是用 修改现有对象,而是init_with
确保首先创建正确的对象(例如,调用Complex(o.value)
以反序列化复数),但我认为我不应该对该方法进行修补。
我注定要使用低级或中级发射,还是我错过了什么?
背景
我将描述这个项目,为什么它需要类,以及为什么它需要(反)序列化。
项目
Small Eigen Collider 旨在为 Ruby 创建随机任务以运行。最初的目的是查看 Ruby 的不同实现(例如,Rubinius 和 JRuby)在给定相同的随机任务时是否返回相同的结果,但我发现它对于检测 Rubinius 和 YARV 的段错误也很有用。
每个任务由以下内容组成:
receiver.send(method_name, *parameters, &block)
其中receiver
是随机选择的对象,method_name
是随机选择的方法的名称,*parameters
是随机选择的对象的数组。&block
不是很随机 - 它基本上相当于{|o| o.inspect}
.
例如,如果接收者是“a”,method_name 是 :casecmp,参数是 [“b”],那么您将调用
"a".send(:casecmp, "b") {|x| x.inspect}
这相当于(因为该块是不相关的)
"a".casecmp("b")
Small Eigen Collider 运行此代码,并记录这些输入以及返回值。在此示例中,Ruby 的大多数实现返回 -1,但在某个阶段,Rubinius 返回 +1。(我将此作为错误提交https://github.com/evanphx/rubinius/issues/518并且 Rubinius 维护人员修复了该错误)
为什么需要课程
我希望能够在我的 Small Eigen Collider 中使用类对象。通常,它们将是接收器,但它们也可以是参数之一。
例如,我发现 YARV 段错误的一种方法是
Thread.kill(nil)
在这种情况下,receiver 是类对象 Thread,parameters 是 [nil]。(错误报告:http ://redmine.ruby-lang.org/issues/show/4367 )
为什么需要(反)序列化
Small Eigen Collider 需要序列化有几个原因。
一是每次使用随机数生成器生成一系列随机任务是不切实际的。JRuby 有一个不同的内置随机数生成器,所以即使给定相同的 PRNG 种子,它也会给 YARV 提供不同的任务。相反,我所做的是我创建一个随机任务列表一次(第一次运行 ruby bin/small_eigen_collider),让初始运行将任务列表序列化为 tasks.yml,然后运行程序的后续运行(使用不同的Ruby 实现)读取该 tasks.yml 文件以获取任务列表。
我需要序列化的另一个原因是我希望能够编辑任务列表。如果我有一长串导致分段错误的任务,我想将列表减少到导致分段错误所需的最小值。例如,使用以下错误 https://github.com/evanphx/rubinius/issues/643,
ObjectSpace.undefine_finalizer(:symbol)
本身不会导致分段错误,也不会
Symbol.all_symbols.inspect
但如果你把两者放在一起,它确实如此。但我一开始有数千个任务,需要将其缩减为仅这两个任务。
在这种情况下,返回现有类对象的反序列化是否有意义,或者您认为有更好的方法吗?