YAML::load raises undefined class/module error

不羁的心 提交于 2019-12-01 03:27:31

When you use YAML.dump to serialize an object in Ruby, the class name is use as part of the Yaml tag so that the correct class can be used when loading the object. For example:

require 'yaml'

class Foo; end

puts YAML.dump Foo.new

produces

--- !ruby/object:Foo {}

When you use YAML.load on that string, Psych knows what class to instantiate for the deserialized object.

If you try to call YAML.load on a Yaml string that specifies a class that hasn’t been defined, then you will get the error:

require 'yaml'

# No Bar class has been defined
YAML.load '--- !ruby/object:Bar {}'

produces:

/Users/matt/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `path2class': undefined class/module Bar (ArgumentError)
    from /Users/matt/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/to_ruby.rb:312:in `resolve_class'
...

This is because Psych needs to create an instance of class Bar, but doesn’t have the definition of the class available. This explains why adding require 'whatever' before loading the Yaml works – now Ruby has the definition of the class loaded and so can create an instance of it (note that there is no definitive link between class name and file name in Ruby, it’s just convention).

The solution therefore is to make sure that when you’re loading any Ruby objects from Yaml you have already required any files that may contain definitions of any classes potentially in that Yaml.

Atul Khanduri

Matt's Answer provides necessary details.

But when I do changes in code and then do some task, that de-serializes some data, without page load (in AJAX) then it fails with same error.

It's better to use require_dependency than require to autoload your changes.

In case of module (not tested with class) and de-serializing using YAML, you can also instantiate your Module before de-serializing with require to fix the issue. See here.

Source: SO answer and Psych issue report in Github

P.S: This problem persists only in Development as config.cache_classes is enable in Production.

Otheus

Amending to Matt's answer. In lieu of a pure-hash based solution (which the Psych team seems to have no interest in doing), modify the header string to strip off the object classification. I did this simply with the following code.

yamltext = File.read("somefile","r")
yamltext.sub!(/^--- \!.*$/,'---')
hash = YAML.load(yamltext)

This might fail (I don't know) if the input stream consists of several documents with different object classifiers (I dont know if that's even valid).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!