Skip to content

Commit

Permalink
* ext/psych/lib/psych/visitors/to_ruby.rb: revive hashes with ivars
Browse files Browse the repository at this point in the history
* ext/psych/lib/psych/visitors/yaml_tree.rb: dump hashes with ivars.
  Fixes github.com/psych/issues/43

* test/psych/test_hash.rb: test for change

fixes #43
  • Loading branch information
tenderlove committed Jan 8, 2015
1 parent 1109112 commit 8f84ad0
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 8 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
Fri Jan 9 06:58:43 2015 Aaron Patterson <aaron@tenderlovemaking.com>

* ext/psych/lib/psych/visitors/to_ruby.rb: revive hashes with ivars

* ext/psych/lib/psych/visitors/yaml_tree.rb: dump hashes with ivars.
Fixes github.com/psych/issues/43

* test/psych/test_hash.rb: test for change

Sun Nov 23 13:11:24 2014 Sean Griffin <sean@thoughtbot.com>

* lib/psych/visitors/to_ruby.rb: Allow loading any BasicObject that
Expand Down
14 changes: 14 additions & 0 deletions lib/psych/visitors/to_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ def visit_Psych_Nodes_Mapping o
end
set

when /^!ruby\/hash-with-ivars(?::(.*))?$/
hash = $1 ? resolve_class($1).new : {}
o.children.each_slice(2) do |key, value|
case key.value
when 'elements'
revive_hash hash, value
when 'ivars'
value.children.each_slice(2) do |k,v|
hash.instance_variable_set accept(k), accept(v)
end
end
end
hash

when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/
revive_hash register(o, resolve_class($1).new), o

Expand Down
45 changes: 37 additions & 8 deletions lib/psych/visitors/yaml_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -367,17 +367,46 @@ def visit_Range o
end

def visit_Hash o
tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
implicit = !tag
ivars = o.instance_variables

register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK))
if ivars.any?
tag = "!ruby/hash-with-ivars"
tag << ":#{o.class}" unless o.class == ::Hash

o.each do |k,v|
accept k
accept v
end
register(o, @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK))

@emitter.end_mapping
@emitter.scalar 'elements', nil, nil, true, false, Nodes::Scalar::ANY

@emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
o.each do |k,v|
accept k
accept v
end
@emitter.end_mapping

@emitter.scalar 'ivars', nil, nil, true, false, Nodes::Scalar::ANY

@emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
o.instance_variables.each do |ivar|
accept ivar
accept o.instance_variable_get ivar
end
@emitter.end_mapping

@emitter.end_mapping
else
tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}"
implicit = !tag

register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK))

o.each do |k,v|
accept k
accept v
end

@emitter.end_mapping
end
end

def visit_Psych_Set o
Expand Down
16 changes: 16 additions & 0 deletions test/psych/test_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ def setup
@hash = { :a => 'b' }
end

def test_hash_with_ivars
@hash.instance_variable_set :@foo, 'bar'
dup = Psych.load Psych.dump @hash
assert_equal 'bar', dup.instance_variable_get(:@foo)
end

def test_hash_subclass_with_ivars
x = X.new
x[:a] = 'b'
x.instance_variable_set :@foo, 'bar'
dup = Psych.load Psych.dump x
assert_cycle x
assert_equal 'bar', dup.instance_variable_get(:@foo)
assert_equal X, dup.class
end

def test_load_with_class_syck_compatibility
hash = Psych.load "--- !ruby/object:Hash\n:user_id: 7\n:username: Lucas\n"
assert_equal({ user_id: 7, username: 'Lucas'}, hash)
Expand Down

0 comments on commit 8f84ad0

Please sign in to comment.