diff --git a/ChangeLog b/ChangeLog index 70b6782107..fc224f9a4a 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 bf48f9d029..bb29c8b52d 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -119,6 +119,11 @@ module Psych 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 @@ module Psych 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 f37396602c..1e22501ad4 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -301,9 +301,13 @@ module Psych 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 @@ module Psych 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 ec6a1aa147..9eedbb4fda 100644 --- a/test/psych/test_array.rb +++ b/test/psych/test_array.rb @@ -2,11 +2,39 @@ require 'psych/helper' 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)