summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--ext/psych/lib/psych/visitors/to_ruby.rb14
-rw-r--r--ext/psych/lib/psych/visitors/yaml_tree.rb45
-rw-r--r--test/psych/test_hash.rb16
4 files changed, 76 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 2a1f1c06c8c..c090ffc2437 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
+
Thu Jan 8 17:05:00 2015 Seiei Higa <hanachin@gmail.com>
* vm_method.c (rb_method_entry): if no super class, no original
diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb
index e696ebd4ff6..adf38a21e8c 100644
--- a/ext/psych/lib/psych/visitors/to_ruby.rb
+++ b/ext/psych/lib/psych/visitors/to_ruby.rb
@@ -261,6 +261,20 @@ module Psych
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
diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb
index 989e1f0de5b..bedf9d35b24 100644
--- a/ext/psych/lib/psych/visitors/yaml_tree.rb
+++ b/ext/psych/lib/psych/visitors/yaml_tree.rb
@@ -367,17 +367,46 @@ module Psych
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
diff --git a/test/psych/test_hash.rb b/test/psych/test_hash.rb
index 264a4719e2b..8d7ba1b1e00 100644
--- a/test/psych/test_hash.rb
+++ b/test/psych/test_hash.rb
@@ -10,6 +10,22 @@ module Psych
@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)