YAML::load raises undefined class/module error
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.
Rails doesn't load classes on deserializing YAML/Marshal objects
Well, after read @tadman and a bunch of answers I have received in the spanish ror mailing list [1] I have collected a few hot tips when you have to deal with Ruby deserializing and class loading in Rails:
Super fast solution
Use config.cache_classes = true
in your development.rb
but you will lost the class auto-refreshing.
Better solution
Require all the classes that are gonna be deserialized but not with require
but with require_dependency
[2] so in development environment the class auto-refreshing will remain working.
Elegant solution
Monkey-patch the YAML and the Marshal gem to tell them to call require_dependency
when they find a non-defined class to deserialize.
And @Xavi has sent me a proposition of monkey-patch Marshal
(he says he wrote it on the air and it is not tested so use it in your own risk) [3]
- [1] http://lists.simplelogica.net/pipermail/ror-es/2011-January/024787.html
- [2] http://apidock.com/rails/ActiveSupport/Dependencies/Loadable/require_dependency
- [3] http://lists.simplelogica.net/pipermail/ror-es/2011-January/024796.html
DelayedJob: Job failed to load: uninitialized constant Syck::Syck
I wrote a post about it.
http://www.kensodev.com/2011/08/16/uninitialized-constant-sycksyck-nameerror/
NoMethodError: undefined method `each_value' when parsing YAML array
It's likely that the declaration at the top, "%YAML 1.1", is causing problems.
No error:
require "yaml"
YAML.load("---\n\n-\n - 'd7744c3878'\n - '80705686'").each
=> #<Enumerator: [["d7744c3878", "80705686"]]:each>
Error:
YAML.load("%YAML 1.1\n---\n\n-\n - 'd7744c3878'\n - '80705686'").each
NoMethodError: undefined method `each' for "%YAML 1.1 ---\n- - 'd7744c3878' - '80705686'":String
from (irb):4
from /Users/modify/.rvm/rubies/ruby-1.9.2-p180/bin/irb:17:in `<main>'
Here I'm using ruby 1.9.2p180 (2011-02-18 revision 30909) [i386-darwin9.8.0]. I also note that in an unmodified environment, #each_value
is available for a Hash
but not an Array
(@oldergod).
It looks like Psych, which is the default YAML interepreter in later versions of Ruby, can handle the %YAML 1.1
directive:
require "psych"
Psych.load("%YAML 1.1\n---\n\n-\n - 'd7744c3878'\n - '80705686'").each
=> #<Enumerator: [["d7744c3878", "80705686"]]:each>
Possible alternatives to using Psych directly would be to switch to a later version of Ruby or to strip the %YAML 1.1
header from the file.
How to parse a yaml file into ruby hashs and/or arrays?
Use the YAML module:
http://ruby-doc.org/stdlib-1.9.3/libdoc/yaml/rdoc/YAML.html
node = YAML::parse( <<EOY )
one: 1
two: 2
EOY
puts node.type_id
# prints: 'map'
p node.value['one']
# prints key and value nodes:
# [ #<YAML::YamlNode:0x8220278 @type_id="str", @value="one", @kind="scalar">,
# #<YAML::YamlNode:0x821fcd8 @type_id="int", @value="1", @kind="scalar"> ]'
# Mappings can also be accessed for just the value by accessing as a Hash directly
p node['one']
# prints: #<YAML::YamlNode:0x821fcd8 @type_id="int", @value="1", @kind="scalar">
http://yaml4r.sourceforge.net/doc/page/parsing_yaml_documents.htm
Delayed::DeserializationError undefined method `has_key?'
Sometimes when we upgrade libs delayed jobs still keep old references.
Try to find the id of delayed_job in logs and play to parse its handler to ruby to find the wrong reference
j = DelayedJob.find(XXX)
data = YAML.load_dj(j.handler)
data.to_ruby
I made a pull request to help with this problem.
Meanwhile you can use this lines
# config/initializers/delayed_job.rb
# Monkey patch to use old class references
module Psych
class << self; attr_accessor :old_class_references end
@old_class_references = {}
class ClassLoader
private
def find klassname
klassname = ::Psych.old_class_references[klassname] || klassname
@cache[klassname] ||= resolve(klassname)
end
end
module Visitors
class ToRuby < Psych::Visitors::Visitor
def revive klass, node
if klass.is_a? String
klassname = ::Psych.old_class_references[klass] || klass
klass = Kernel.const_get(klassname) rescue klassname
end
s = register(node, klass.allocate)
init_with(s, revive_hash({}, node), node)
end
end
end
end
# Add all old dependencies (hash keys) pointing to new references (hash values)
Psych.old_class_references = {
'ActiveRecord::AttributeSet' => 'ActiveModel::AttributeSet'
# ...
}
Related Topics
Exclude Option from Collection.Map in Ruby on Rails
How to Call Methods Defined in Applicationcontroller in Models
How to "Extract" Values from a Multidimensional Array in a Smart Way
Ruby - What's the Difference Between Single and Double Quotes
Why Does Date.Yesterday Counts as Date.Today Also
Ruby Convert Single Quotes to Double Quotes in Xml
Ruby Enterprise Edition VS Ruby 1.9
Rails S Return: [Bug] Segmentation Fault
Remove All Data from Active Storage
Rails Activerecord Create or Find
Encoding Issues in JavaScript Files Using Rails Asset Pipeline
Yaml::Load Raises Undefined Class/Module Error
How to Raise an Exception in an Rspec Test
Ruby Equivalent for Python's For/Else
Ruby - Compare Two Enumerators Elegantly