Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions lib/psych/visitors/to_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,15 @@ def visit_Psych_Nodes_Mapping o

when /^!ruby\/data(-with-ivars)?(?::(.*))?$/
data = register(o, resolve_class($2).allocate) if $2
members = {}
members = nil
values = nil

if $1 # data-with-ivars
ivars = {}
o.children.each_slice(2) do |type, vars|
case accept(type)
when 'members'
revive_data_members(members, vars)
members, values = revive_data_members(vars)
data ||= allocate_anon_data(o, members)
when 'ivars'
revive_hash(ivars, vars)
Expand All @@ -216,10 +217,12 @@ def visit_Psych_Nodes_Mapping o
data.instance_variable_set ivar, v
end
else
revive_data_members(members, o)
members, values = revive_data_members(o)
end
data ||= allocate_anon_data(o, members)
values = data.members.map { |m| members[m] }
unless members == data.class.members
raise ArgumentError, "Data members in YAML (#{members}) do not match the members of #{data.class} (#{data.class.members})"
end
init_data(data, values)
data.freeze
data
Expand Down Expand Up @@ -368,17 +371,18 @@ def register_empty object
end

def allocate_anon_data node, members
klass = class_loader.data.define(*members.keys)
klass = class_loader.data.define(*members)
register(node, klass.allocate)
end

def revive_data_members hash, o
def revive_data_members o
keys = []
values = []
o.children.each_slice(2) do |k,v|
name = accept(k)
value = accept(v)
hash[class_loader.symbolize(name)] = value
keys << class_loader.symbolize(accept(k))
values << accept(v)
end
hash
[keys, values]
end

def revive_hash hash, o, tagged= false
Expand Down
31 changes: 31 additions & 0 deletions test/psych/test_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,37 @@ def test_load
assert_equal "hello", obj.bar
assert_equal "bar", obj.foo
end

def test_members_must_be_identical
TestData.const_set :D, Data.define(:a, :b)
d = Psych.dump(TestData::D.new(1, 2))

# more members
TestData.send :remove_const, :D
TestData.const_set :D, Data.define(:a, :b, :c)
e = assert_raise(ArgumentError) { Psych.unsafe_load d }
assert_equal 'Data members in YAML ([:a, :b]) do not match the members of Psych::TestData::D ([:a, :b, :c])', e.message

# less members
TestData.send :remove_const, :D
TestData.const_set :D, Data.define(:a)
e = assert_raise(ArgumentError) { Psych.unsafe_load d }
assert_equal 'Data members in YAML ([:a, :b]) do not match the members of Psych::TestData::D ([:a])', e.message

# completely different members
TestData.send :remove_const, :D
TestData.const_set :D, Data.define(:foo, :bar)
e = assert_raise(ArgumentError) { Psych.unsafe_load d }
assert_equal 'Data members in YAML ([:a, :b]) do not match the members of Psych::TestData::D ([:foo, :bar])', e.message

# different order
TestData.send :remove_const, :D
TestData.const_set :D, Data.define(:b, :a)
e = assert_raise(ArgumentError) { Psych.unsafe_load d }
assert_equal 'Data members in YAML ([:a, :b]) do not match the members of Psych::TestData::D ([:b, :a])', e.message
ensure
TestData.send :remove_const, :D
end
end
end

Loading