Skip to content

If YAML.dump can’t produce valid YAML…

Yesterday I had fun with ruby’s YAML module not loading a piece of YAML I needed it to load. Syntax Error… fine–all in a day’s work. But then I started looking at the YAML in question and I realized, this was GENERATED by the module itself!

First of all, the reason I was using YAML was as a quick, simple, human-readable backend for a tiny little side-project. I chose YAML over SQLite or CSV because neither have the human-readable thing going for them, plus I’m working in Ruby and it seemed to have nice built-in support for YAML.

You can take any object you like (with obvious exceptions like streams and sockets) and just YAML.dump it, creating a happy string waiting to be written to the nearest file. So given this class:

class Foo
  def initialize
    @bar = 'one'
    @baz = 2
    @qux = [3, 'four', 5]
  end
end

You can do something like this:

>> f = Foo.new
=> #
>> YAML.dump(f)
=> "--- !ruby/object:Foo \nbar: one\nbaz: 2\nqux: \n- 3\n- four\n- 5\n"

And that YAML string at the end there looks like this:

--- !ruby/object:Foo
bar: one
baz: 2
qux:
- 3
- four
- 5

So you can stuff that wherever you’d like (file.yml let’s say) and then at some later date all you need to do is:

>> new_f = YAML.load_file('file.yml')
=> #
>> f == new_f
=> true

Fun times, no? And most importantly fun times that don’t require one learns YAML in great detail. Well, let me show you exactly where the fun times ended for me. For an example, let’s make a simple hash with our f.

>> h = { f => 2 }
=> {#=>2}
>> YAML.dump(h)
=> "--- \n!ruby/object:Foo ? \n  bar: one\n  baz: 2\n  qux: \n  - 3\n  - four\n  - 5\n: 2\n\n"

Seems fine, no?

---
!ruby/object:Foo ?
  bar: one
  baz: 2
  qux:
  - 3
  - four
  - 5
: 2

So we go about our business, right?

>> YAML.load(YAML.dump(h))
ArgumentError: syntax error on line 2, col -1

Boo. So now I have to go and do what I was trying to avoid doing in the first place: learn about YAML. Well after wading through the schema documents for YAML, which are long, full of BNF, and no fun. At the end of the day it turns out that YAML.dump produces invalid YAML whenever an object is used as a key in a Hash. Without going into too much detail (partially because I don’t fully understand it), the question mark needs to come before the ruby object tag for it to be valid.

Here’s a workaround that just does some regexp munging on the string :

>> YAML.load(YAML.dump(h).gsub(/^(!ruby.*) \? *$/) { |m| "? #{$1}" })
=> {#=>2}

Looks like I’ll be submitting my first bug report to rubylang.

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*