From ee3ebc2de5a2d65c718190e8dc230964b683c167 Mon Sep 17 00:00:00 2001 From: tenderlove Date: Wed, 18 Jan 2012 01:44:21 +0000 Subject: [PATCH] * ext/psych/lib/psych/visitors/to_ruby.rb: Added ability to load array subclasses with ivars. * ext/psych/lib/psych/visitors/yaml_tree.rb: Added ability to dump array subclasses with ivars. * test/psych/test_array.rb: corresponding tests git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@34328 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 8 +++++ ext/psych/lib/psych/visitors/to_ruby.rb | 16 +++++++++ ext/psych/lib/psych/visitors/yaml_tree.rb | 43 +++++++++++++++++++++-- test/psych/test_array.rb | 28 +++++++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 70b67821079991..fc224f9a4a748c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Jan 18 10:39:47 2012 Aaron Patterson + + * ext/psych/lib/psych/visitors/to_ruby.rb: Added ability to load array + subclasses with ivars. + * ext/psych/lib/psych/visitors/yaml_tree.rb: Added ability to dump + array subclasses with ivars. + * test/psych/test_array.rb: corresponding tests + Tue Jan 17 17:18:41 2012 Nobuyoshi Nakada * configure.in (SPT_TYPE): enable as SPT_REUSEARGV on Darwin. diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index bf48f9d029650d..bb29c8b52d4b8d 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -119,6 +119,11 @@ def visit_Psych_Nodes_Sequence o map[accept(a.children.first)] = accept a.children.last } map + when /^!(?:seq|ruby\/array):(.*)$/ + klass = resolve_class($1) + list = register(o, klass.allocate) + o.children.each { |c| list.push accept c } + list else list = register(o, []) o.children.each { |c| list.push accept c } @@ -135,6 +140,17 @@ def visit_Psych_Nodes_Mapping o members = Hash[*o.children.map { |c| accept c }] string = members.delete 'str' init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o) + when /^!ruby\/array:(.*)$/ + klass = resolve_class($1) + list = register(o, klass.allocate) + + members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a] + list.replace members['internal'] + + members['ivars'].each do |ivar, v| + list.instance_variable_set ivar, v + end + list when /^!ruby\/struct:?(.*)?$/ klass = resolve_class($1) diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb index f37396602cd82f..1e22501ad4f30f 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -301,9 +301,13 @@ def visit_Psych_Set o end def visit_Array o - register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) - o.each { |c| accept c } - @emitter.end_sequence + if o.class == ::Array + register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) + o.each { |c| accept c } + @emitter.end_sequence + else + visit_array_subclass o + end end def visit_NilClass o @@ -315,6 +319,39 @@ def visit_Symbol o end private + def visit_array_subclass o + tag = "!ruby/array:#{o.class}" + if o.instance_variables.empty? + node = @emitter.start_sequence(nil, tag, false, Nodes::Sequence::BLOCK) + register o, node + o.each { |c| accept c } + @emitter.end_sequence + else + node = @emitter.start_mapping(nil, tag, false, Nodes::Sequence::BLOCK) + register o, node + + # Dump the internal list + accept 'internal' + @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) + o.each { |c| accept c } + @emitter.end_sequence + + # Dump the ivars + accept 'ivars' + @emitter.start_mapping(nil, nil, true, Nodes::Sequence::BLOCK) + o.instance_variables.each do |ivar| + accept ivar + accept o.instance_variable_get ivar + end + @emitter.end_mapping + + @emitter.end_mapping + end + end + + def dump_list o + end + # '%:z' was no defined until 1.9.3 if RUBY_VERSION < '1.9.3' def format_time time diff --git a/test/psych/test_array.rb b/test/psych/test_array.rb index ec6a1aa1472aa7..9eedbb4fda960c 100644 --- a/test/psych/test_array.rb +++ b/test/psych/test_array.rb @@ -2,11 +2,39 @@ module Psych class TestArray < TestCase + class X < Array + end + + class Y < Array + attr_accessor :val + end + def setup super @list = [{ :a => 'b' }, 'foo'] end + def test_subclass + yaml = Psych.dump X.new + assert_match X.name, yaml + + list = X.new + list << 1 + assert_equal X, list.class + assert_equal 1, list.first + end + + def test_subclass_with_attributes + y = Psych.load Psych.dump Y.new.tap {|y| y.val = 1} + assert_equal Y, y.class + assert_equal 1, y.val + end + + def test_backwards_with_syck + x = Psych.load "--- !seq:#{X.name} []\n\n" + assert_equal X, x.class + end + def test_self_referential @list << @list assert_cycle(@list)