Ruby YAML解析器通过传递构造函数

前端之家收集整理的这篇文章主要介绍了Ruby YAML解析器通过传递构造函数前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在从一个应用程序中获取YAML文件的输入,将它们解析成对象,让他们做他们的事情.我现在唯一的问题是,YAML解析器似乎忽略了对象“initialize”方法.我正在指望构造函数来填充YAML文件缺少默认值的任何实例变量,并将一些东西存储在类变量中.这是一个例子:
class Test

    @@counter = 0

    def initialize(a,b)
        @a = a
        @b = b

        @a = 29 if @b == 3

        @@counter += 1
    end

    def self.how_many
        p @@counter
    end

    attr_accessor :a,:b

end

require 'YAML'

a = Test.new(2,3)
s = a.to_yaml
puts s
b = YAML::load(s)
puts b.a
puts b.b
Test.how_many

puts ""

c = Test.new(4,4)
c.b = 3
t = c.to_yaml
puts t
d = YAML::load(t)
puts d.a
puts d.b
Test.how_many

我会预期以上输出

--- !ruby/object:Test
a: 29
b: 3
29
3
2

--- !ruby/object:Test
a: 4
b: 3
29
3
4

相反,我得到:

--- !ruby/object:Test
a: 29
b: 3
29
3
1

--- !ruby/object:Test
a: 4
b: 3
4
3
2

我不明白如何使这些对象不使用它们定义的初始化方法.我也想知道是否有任何强制解析器使用initialize方法.

解决方法

从Yaml反序列化一个对象不使用initialize方法,因为一般来说,对象实例变量(这是默认的Yaml序列化存储)和初始化参数之间没有对应关系.

例如,考虑一个初始化的对象,看起来像这样(没有其他实例变量):

def initialize(param_one,param_two)
  @a_variable = some_calculation(param_one,param_two)
end

现在当一个实例被反序列化时,Yaml处理器的值为@a_variable,但是initialize方法需要两个参数,所以它不能调用它.即使实例变量的数量与初始化参数的数量相匹配,并不一定是它们对应的情况,即使处理器不知道它们被传递到初始化的顺序.

将Ruby对象序列化和反序列化为Yaml的默认过程是在序列化期间写出所有实例变量(使用其名称),然后在反序列化时分配类的新实例,并在此新实例上简单设置相同的实例变量.

当然有时你需要更多的控制这个过程.如果您使用的是Psych Yaml处理器(这是Ruby 1.9.3中的默认设置),那么您应该适当地实现encode_with(用于序列化)或init_with(用于反序列化)方法.

为了序列化,Psych将调用一个对象的encode_with方法,如果它存在,传递一个coder object.这个对象允许你指定如何在Yaml中表示对象 – 通常你只是像哈希一样对待它.

对于反序列化,Psych会调用init_with方法,如果它存在于您的对象上,而不是使用上述默认过程,再次传递一个编码器对象.这一次,编码器将包含关于Yaml中对象表示的信息.

请注意,您不需要同时提供这两种方法,如果需要,您可以提供任何一种方法.如果同时提供这两个,那么在init_with中传递的编码器对象将基本上与该方法运行后传递给encode_with的对象相同.

作为一个例子,考虑一个对象,其中有一些从别人计算出来的实例变量(可能是一个优化来避免大量的计算),但是不应该被序列化为Yaml.

class Foo

  def initialize(first,second)
    @first = first
    @second = second
    @calculated = expensive_calculation(@first,@second)
  end

  def encode_with(coder)
    # @calculated shouldn’t be serialized,so we just add the other two.
    # We could provide different names to use in the Yaml here if we
    # wanted (as long as the same names are used in init_with).
    coder['first'] = @first
    coder['second'] = @second
  end

  def init_with(coder)
    # The Yaml only contains values for @first and @second,we need to
    # recalculate @calculated so the object is valid.
    @first = coder['first']
    @second = coder['second']
    @calculated = expensive_calculation(@first,@second)
  end

  # The expensive calculation
  def expensive_calculation(a,b)
    ...
  end
end

当你将这个类的一个实例转储给Yaml时,它会看起来像这样,没有计算的值:

--- !ruby/object:Foo
first: 1
second: 2

当您将此Yaml加载到Ruby中时,创建的对象将设置@calculated实例变量.

如果你希望你可以在init_with内调用initialize,但是我认为在初始化一个新类的实例之前要清楚一点,并且从Yaml反序列化一个现有的实例.我会建议将常用逻辑提取到可以从两个方面调用方法,

原文链接:https://www.f2er.com/ruby/266024.html

猜你在找的Ruby相关文章