心理医生说«在中级正在建立一个AST ……»但是,到底是怎么回事?

我使用Sych做了这个旧代码:

 yaml_as "tag:yaml.org,2002:#{self}" def to_yaml(opts = {}) YAML::quick_emit(self, opts) do |out| out.map(taguri, to_yaml_style) do |map| map.add('name', name) map.add('address', full_address.upcase) if full_address? end end end 

输出类似的东西:

 --- !Contact name: SMOKE OIL address: |- SMOKE OIL 1 RUE DE LA PAIX 75002 PARIS FRANCE 

现在,我正在升级旧代码,转到Psych所以,我阅读了doc并做了:

 yaml_as "tag:yaml.org,2002:#{self}" def encode_with(coder) coder['name'] = name coder['address'] = full_address.upcase if full_address? end 

这样做:

 --- !Contact name: SMOKE OIL address: ! "SMOKE OIL\n1 RUE DE LA PAIX\n75002 PARIS\nFRANCE" 

这是很好的YAML但是,它应该是whois服务器的输出,而且人类的可读性更低……

所以,我回到了doc,看了第二种做事方式,即构建AST。 现在,除非我没有看到任何东西,否则没有什么可以解释你如何使用你构建的AST,并以一种方式插入它,Psych.dump(obj)仍然可以工作……

我尝试过(没有多少希望):

 a = Psych::Nodes::Scalar(full_address.upcase) a.style = Psych::Nodes::LITTERAL coder['address'] = a if full_address? 

但是,显然,它并没有做我希望它做的事情……我也尝试过:

 def encode_with(coder) Psych::Nodes::Mapping.new.tap do |map| map.children << Psych::Nodes::Scalar.new("name") map.children << Psych::Nodes::Scalar.new(name) map.children << Psych::Nodes::Scalar.new("address") a = Psych::Nodes::Scalar.new(full_address.upcase) a.style = 4 map.children << a end end 

但是,我无法看到如何将其插入编码器……

此外,回答需要在做递归事务时起作用,这是一个Contact objet,但是人们可以要求一个包含几个联系人的Domain ,我希望它尽可能干燥:-)

那么,任何人都有提示如何做到这一点?

如果你想创建自己的AST,那么就不能使用Psych.dumpPsych.dump使用Psych默认值创建自己的AST。 在您的情况下,您想要自定义AST创建过程。

看一下Psych.dump的来源,你可以看到它使用一个Psych::Visitors::YAMLTree来创建AST。 您可以对其进行子类化并自定义它如何处理您的Contact类以获得所需的输出。 特别是你需要覆盖accept方法 。

这是一个简单的例子,只是Contact类的特殊情况:

 class MyYAMLTree < Psych::Visitors::YAMLTree def accept target return super unless target.is_a? Contact @emitter.start_mapping(nil, "tag:yaml.org,2002:#{target.class}", false, Psych::Nodes::Mapping::BLOCK) @emitter.scalar 'name', nil, nil, true, false, Psych::Nodes::Scalar::ANY @emitter.scalar target.name, nil, nil, true, false, Psych::Nodes::Scalar::ANY @emitter.scalar 'address', nil, nil, true, false, Psych::Nodes::Scalar::ANY #this is the where we make the address string a literal @emitter.scalar target.full_address, nil, nil, true, false, Psych::Nodes::Scalar::LITERAL @emitter.end_mapping end end 

请注意,调用encode_with的是Psych::Visitors::YAMLTree类,这将为您的类完全绕过它。

为了使用它,使用类似的东西(这基本上是使用MyYAMLTree的Psych.dump的简化版本):

 def my_yaml o visitor = MyYAMLTree.new visitor << o visitor.tree.yaml end 

这显然只是一个简单的例子,但希望它能指出你正确的方向。