psych
psych copied to clipboard
.to_yaml fails when tempfile is an instance variable
Here's a quick script to reproduce the issue:
require 'tempfile'
require 'psych'
class Foo
def initialize
@tf = Tempfile.new('foo')
end
end
f = Foo.new
puts f.to_yaml
Then ruby foo.rb
produces the following stacktrace:
/Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:26:in `block in initialize': undefined method `name' for nil:NilClass (NoMethodError)
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `yield'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `default'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `block in initialize'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `yield'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `default'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `block in initialize'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `yield'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `default'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `block in initialize'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `yield'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `default'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in `block in initialize'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in `yield'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in `default'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in `accept'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:449:in `block in dump_ivars'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in `each'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in `dump_ivars'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:125:in `visit_Object'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in `accept'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:67:in `push'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych.rb:242:in `dump'
from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/core_ext.rb:14:in `psych_to_yaml'
from foo.rb:11:in `<main>'
Is there a reason I shouldn't be using .to_yaml
with such an object? Or is this a legitimate bug?
Well, you can't really serialize an IO object. It should raise an exception. Maybe not this exception, but serializing an io object doesn't make sense.
Aaron Patterson http://tenderlovemaking.com/ I'm on an iPhone so I apologize for top posting.
On Dec 19, 2013, at 11:41 AM, sdjespersen [email protected] wrote:
Here's a quick script to reproduce the issue:
require 'tempfile' require 'psych'
class Foo def initialize @tf = Tempfile.new('foo') end end
f = Foo.new puts f.to_yaml Then ruby foo.rb produces the following stacktrace:
/Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:26:in
block in initialize': undefined method
name' for nil:NilClass (NoMethodError) from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:inyield' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
default' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:inblock in initialize' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
yield' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:indefault' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
block in initialize' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:inyield' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
default' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:inblock in initialize' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
yield' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:indefault' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:28:in
block in initialize' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:inyield' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in
default' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:inaccept' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:449:in
block in dump_ivars' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:ineach' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in
dump_ivars' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:125:invisit_Object' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:103:in
accept' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:67:inpush' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych.rb:242:in
dump' from /Users/me/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/1.9.1/psych/core_ext.rb:14:inpsych_to_yaml' from foo.rb:11:in
' Is there a reason I shouldn't be using .to_yaml with such an object? Or is this a legitimate bug?
Reply to this email directly or view it on GitHub.
I see. However, the following doesn't complain:
tf = Tempfile.new('foo')
tf.to_yaml # => "--- !ruby/object:File {}\n"
Ya, it probably shouldn't. Psych should behave like marshal:
irb(main):001:0> Marshal.dump File.open('/dev/null')
TypeError: can't dump File
from (irb):1:in `dump'
from (irb):1
from /Users/aaron/.rbenv/versions/2.1.0-dev/bin/irb:11:in `<main>'
irb(main):002:0>
A similar exception would be a nice feature, but as you pointed out, my application of yaml here is somewhat misguided.
@tenderlove, had a similar issue,
my use case was to log as much data as possible from a failed request, for example, in a rails controller method:
Event.create( :title => "Invalid request from client",
:params => request.filtered_parameters.to_yaml,
:session => session.to_yaml,
:kind => 'invalid request',
:message => options[:message],
:severity => 'low',
:request => request.env.reject{|k,v| k.to_s =~ /^action_controller..*/ or k.to_s =~ /^action_dispatch..*/}.to_yaml,
:response => {:body => response.body, :status => response.status, :location => response.location, :content_type => response.content_type, :charset => response.charset, }.to_yaml)
and the app is failing at:
request.env.reject{|k,v| k.to_s =~ /^action_controller..*/ or k.to_s =~ /^action_dispatch..*/}.to_yaml
The Marshall approach would force explicit filtering of a myriad of keys in a deep hash, and I guess the Syck behaviour under Rails 3.0 was to simply dump whatever was there and dumpable and give no useful information where no useful information can be given.
Deep hashes can be a bitch.
Both approaches have merit, maybe make the behaviour a configuration option in #to_yaml
?
A funny addition to the problem was that the exception happened only under Passenger in production, not on the development machine.
Sorry I haven't made an progress on this in a while. How about this:
- Files (including tempfiles) will raise an exception by default
- You'll be able to configure an error handler to return a different value rather than raising an exception
For example:
Psych.dump(deeply_nested_hash, :on_error => lambda { |obj,ex|
if obj == blah
nil # nil is dumped rather than `obj`
else
raise ex
end
})
Don't know if his is related, but I tried to serialize a hash created from and Icalandar event and got the error: NoMethodError: undefined method
name' for nil:NilClass`
Debugging the problem I found this post. After a little digging I found my problem was that what I though was a text field in the hash had a class of `Icalendar::Values::Text'.
I can't remember the exact circumstance, but several publicly available icalanders (Apple's holidays) had some character sequences that was throwing an encoding error. I ended up having to force encoding on those text fields:
e.summary = e.summary.force_encoding("UTF-8")
That fix to a bug in (Icalendar,?) gave me the undefined method error. There is also a new issue (#208) that reference forced encoding. I fixed my problem by converting the fields to_s before serializing since I assume psych can't account for every gem specific class. The hash I created worked fine in displaying the calendar, but I decided to serialize and save it and ran into this problem.