to_yaml raises a NoMethodError when used with a SimpleDelegator #100

Closed
aisrael opened this Issue Nov 14, 2012 · 9 comments

Projects

None yet

7 participants

@aisrael
aisrael commented Nov 14, 2012

The following code:

require 'delegate'
require 'yaml'

class Foo
end

class Delegate < SimpleDelegator
end

class Bar < Struct.new :foo
end

foo = Foo.new
delegate = Delegate.new(foo)
bar = Bar.new(delegate)

bar.to_yaml # raises NoMethodError: undefined method `name' for nil:NilClass

The last line raises an error:

~/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:26:in
`block in initialize': undefined method `name' for nil:NilClass (NoMethodError)

I've looked at the source for yaml_tree.rb around line 26 and it goes:

@dispatch_cache = Hash.new do |h,klass|
    method = "visit_#{(klass.name || '').split('::').join('_')}"

Is this an edge-case interaction between SimpleDelegator and Psych YAML engine? Neither foo.to_yaml nor delegate.to_yaml work properly—it seems that only when another object contains a reference to a SimpleDelegator does to_yaml fail.

@sbounmy
sbounmy commented Nov 25, 2012

having the exact same issue with delayed_job_active_record by using handle_asynchronously to queue jobs https://github.com/collectiveidea/delayed_job#queuing-jobs

@dividedmind

A hackish workaround:

# note the resulting encoding is a bit ugly
class Delegator
  def encode_with coder
    ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
    coder['obj'] = __getobj__
    unless ivars.empty?
      coder['ivars'] = Hash[ivars.map{|var| [var[1..-1], instance_variable_get(var)]}]
    end
  end

  def init_with coder
    (coder['ivars'] || {}).each do |k, v|
      instance_variable_set :"@#{k}", v
    end
    __setobj__ coder['obj']
  end
end
@noma4i
noma4i commented May 27, 2013

Same error if trying to serialize params received from multipart form submission.
How-to:

  1. Create sample form with file upload.
  2. make params.to_hash.to_yaml
  3. Enjoy!
@ixti
ixti commented Jun 14, 2013

Same error when you try to dump response from HTTParty:

require 'httparty'
require 'yaml'

module FQL
  include HTTParty

  base_uri 'http://graph.facebook.com'

  def self.link_stat url
    self.get("/fql", {
      :query => {
        :q => <<-FBQL.squeeze(' ').strip
          select total_count
            from link_stat
           where url=#{url.inspect}
        FBQL
      }
    })
  end
end

YAML.dump FQL.link_stat "http://www.example.com"
@afn
afn commented Sep 11, 2014

Maybe psych should define visit_BasicObject in addition to (or instead of) visit_Object? It blows up here:

        @dispatch_cache = Hash.new do |h,klass|
          method = "visit_#{(klass.name || '').split('::').join('_')}"

          method = respond_to?(method) ? method : h[klass.superclass]

          raise(TypeError, "Can't dump #{target.class}") unless method

          h[klass] = method
        end

because it traverses up the class hierarchy expecting to encounter a class for which method is defined, but there is no catch-all for objects that don't inherit from Object.

@afn afn referenced this issue in collectiveidea/delayed_job Sep 12, 2014
Closed

undefined method `name' for nil:NilClass with rails 4.2 beta #687

@tenderlove
Member

Defining a serialization mechanism for BasicObject doesn't make sense for solving a problem with a DelegateObject. We can add a custom serialization mechanism for SimpleDelegator, I think.

@afn
afn commented Sep 15, 2014

@tenderlove I agree; these are probably two separate problems: (1) can't serialize SimpleDelegator-based objects, and (2) can't serialize objects that derive from BasicObject.

For the latter, there are a couple of reasonable approaches:

  1. Raise an error with a more sensible message than undefined method `name' for nil:NilClass
  2. Define a default fallback serializer for BasicObject.
@tenderlove
Member

Well, Marshal seems to be able to dump SimpleDelegator based objects, so I think Psych should support it. Marshal won't dump BasicObjects though.

@tenderlove tenderlove closed this in #205 Sep 17, 2014
@ixti
ixti commented Sep 18, 2014

👍 👏 ❤️

@handlers handlers added a commit to sunlightlabs/opencongress that referenced this issue Feb 11, 2015
@handlers handlers Specify psych version to make serialization work a504b7f
@myfreeweb myfreeweb added a commit to myfreeweb/freshcerts that referenced this issue Feb 11, 2016
@myfreeweb myfreeweb update deps, add psych dep
ruby has built-in psych but it's old on some rubies
and it has this issue there: ruby/psych#100
0b8b8c8
@myfreeweb myfreeweb referenced this issue in myfreeweb/freshcerts Feb 11, 2016
Open

OpenSSL::X509::RequestError - header too long #1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment