Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

to_yaml raises a NoMethodError when used with a SimpleDelegator #100

Closed
aisrael opened this issue Nov 14, 2012 · 9 comments · Fixed by #205
Closed

to_yaml raises a NoMethodError when used with a SimpleDelegator #100

aisrael opened this issue Nov 14, 2012 · 9 comments · Fixed by #205

Comments

@aisrael
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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.

@tenderlove
Copy link
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
Copy link

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
Copy link
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.

@ixti
Copy link

ixti commented Sep 18, 2014

👍 👏 ❤️

plantfansam added a commit to sunlightlabs/opencongress that referenced this issue Feb 11, 2015
valpackett added a commit to valpackett/freshcerts that referenced this issue Feb 11, 2016
ruby has built-in psych but it's old on some rubies
and it has this issue there: ruby/psych#100
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

7 participants