Skip to content

Commit

Permalink
* ext/psych/lib/psych/visitors/to_ruby.rb: Added ability to load array
Browse files Browse the repository at this point in the history
  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
  • Loading branch information
tenderlove committed Jan 18, 2012
1 parent e58d77b commit ee3ebc2
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 3 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Wed Jan 18 10:39:47 2012 Aaron Patterson <[email protected]>

* 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 <[email protected]>

* configure.in (SPT_TYPE): enable as SPT_REUSEARGV on Darwin.
Expand Down
16 changes: 16 additions & 0 deletions ext/psych/lib/psych/visitors/to_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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)

Expand Down
43 changes: 40 additions & 3 deletions ext/psych/lib/psych/visitors/yaml_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
28 changes: 28 additions & 0 deletions test/psych/test_array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit ee3ebc2

Please sign in to comment.