yburyug
yburyug

Reputation: 1070

Ruby - Parsing a string of a Hash using YAML - Error if hash entered raw and coerced to string rather than entered as string

I have a gem I have created that wraps Git as a key:value store (dictionary/hash). The source is here.

The way it works in the process referenced is as follows:

  1. run the function set containing a key and a value argument
  2. hash these with git, have the key point at the hash
  3. return the key if this operation is successful and it is added to the global dictionary holing keys and hashes

Now, if I call something like

db.set('key', {some: 'value'})
# => 'key'

and then try to retrieve this,

db.get('key')
Psych::SyntaxError: (<unknown>): did not find expected node content while parsing a flow node at line 1 column 2
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:370:in `parse'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:370:in `parse_stream'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:318:in `parse'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:245:in `load'
from /home/bobby/.rvm/gems/ruby-2.2.1/gems/gkv-0.2.1/lib/gkv/database.rb:21:in `get'
from (irb):6
from /home/bobby/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'

Now, if I set the key as that same dictionary, but as a string:

db.set('key', "{some: 'value'}")
# => 'key'
db.get('key')
# => {"key"=>"value"} 
db.get('key').class
=> Hash

The operation that is performing the git operations' and wrapping them to a kv store source is:

...
    def get(key)
      if $ITEMS.keys.include? key
        YAML.load(Gkv::GitFunctions.cat_file($ITEMS[key].last))
      else
        raise KeyError
      end
    end

    def set(key, value)
      update_items(key, value.to_s)
      key
    end
...

And the get_items function being referenced here's source is:

...    
  def update_items(key, value)
      if $ITEMS.keys.include? key
        history = $ITEMS[key]
        history << Gkv::GitFunctions.hash_object(value.to_s)
        $ITEMS[key] = history
      else
        $ITEMS[key] = [Gkv::GitFunctions.hash_object(value.to_s)]
      end
    end
  end
...

hash_object and cat_object simple wrap git hash-object and git cat-file in a method writing the input to a tmpfile, git adding it, and then erasing the tempfile.

I'm really at a loss as to why this works with strings but not true dictionaries. It results in the exact same error if you use the old hashrocket syntax as well:

db.set('a', {:key => 'value'})
=> "a" 
db.get('a')
# => Psych::SyntaxError: (<unknown>): did not find expected node content while parsing a flow node at line 1 column 2
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:370:in `parse'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:370:in `parse_stream'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:318:in `parse'
from /home/bobby/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/psych.rb:245:in `load'
from /home/bobby/.rvm/gems/ruby-2.2.1/gems/gkv-0.2.1/lib/gkv/database.rb:21:in `get'
from (irb):6
from /home/bobby/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'

Any ideas?

Upvotes: 3

Views: 528

Answers (1)

user12341234
user12341234

Reputation: 7223

In your get method you call YAML.load, but in your set method you use .to_s. This means that the YAML parser is trying to read an arbitrary string as if it were YAML. For symmetry YAML.dump should be used in the set method instead.

I've created a pull request with the changes.

Upvotes: 2

Related Questions