I have this old code using Sych doing :
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
which outputs something like that :
--- !Contact
name: SMOKE OIL
address: |-
SMOKE OIL
1 RUE DE LA PAIX
75002 PARIS
FRANCE
Now, I’m upgrading that old code, going to Psych so, I read the doc and did :
yaml_as "tag:yaml.org,2002:#{self}"
def encode_with(coder)
coder['name'] = name
coder['address'] = full_address.upcase if full_address?
end
And that does :
--- !Contact
name: SMOKE OIL
address: ! "SMOKE OIL\n1 RUE DE LA PAIX\n75002 PARIS\nFRANCE"
It’s nice YAML but, it’s supposed to be the output of a whois server, and it’s way less readable by humans…
So, I went back to the doc, and looked at the second way of doing things, that is, building an AST. Now, unless I’m not seeing something, nothing explains you how to take the AST you built, and plug it in a way Psych.dump(obj) would still work…
I tried doing (without much hope) :
a = Psych::Nodes::Scalar(full_address.upcase)
a.style = Psych::Nodes::LITTERAL
coder['address'] = a if full_address?
but, obviously, it did not do what I hoped it’d do… I also tried :
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
But, I could not see how to plug it into the coder…
Also, the answer needs to work when doing recursive things, this is a Contact objet, but one can ask for a Domain which will contain a few contacts and I want it as DRY as possible 🙂
So, anyone has a hint on how to do this ?
If you want to create your own AST then you can’t use
Psych.dump.Psych.dumpcreates its own AST using the Psych defaults. In your case you want to customise the AST creation process.Looking at the source of
Psych.dumpyou can see it uses aPsych::Visitors::YAMLTreeto create the AST. You can subclass this and customise how it handles yourContactclass to get the output you want. In particular you need to override theacceptmethod.Here’s a simple example that just special cases the
Contactclass:Note that it’s the
Psych::Visitors::YAMLTreeclass that callsencode_with, this will bypass it altogether for your class.In order to use this, use something like (this is basically a simplified version of
Psych.dumpusing MyYAMLTree):This is obviously just a simple example, but hopefully it’ll point you in the right direction.