心理医生说«在中级正在建立一个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.dump
。 Psych.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
这显然只是一个简单的例子,但希望它能指出你正确的方向。