Skip to content

Commit 1db53a3

Browse files
committed
Add support for ruby 3.2 Data objects
1 parent ac15c01 commit 1db53a3

File tree

7 files changed

+121
-0
lines changed

7 files changed

+121
-0
lines changed

lib/psych/class_loader.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Psych
66
class ClassLoader # :nodoc:
77
BIG_DECIMAL = 'BigDecimal'
88
COMPLEX = 'Complex'
9+
DATA = 'Data'
910
DATE = 'Date'
1011
DATE_TIME = 'DateTime'
1112
EXCEPTION = 'Exception'

lib/psych/visitors/to_ruby.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ def visit_Psych_Nodes_Mapping o
198198
s
199199
end
200200

201+
when /^!ruby\/data(?::(.*))?$/
202+
data = register(o, resolve_class($1).allocate) if $1
203+
members = {}
204+
revive_data_members(members, o)
205+
data ||= allocate_anon_data(o, members)
206+
data.send(:initialize, **members)
207+
data
208+
201209
when /^!ruby\/object:?(.*)?$/
202210
name = $1 || 'Object'
203211

@@ -341,6 +349,20 @@ def register_empty object
341349
list
342350
end
343351

352+
def allocate_anon_data node, members
353+
klass = class_loader.data.define(*members.keys)
354+
register(node, klass.allocate)
355+
end
356+
357+
def revive_data_members hash, o
358+
o.children.each_slice(2) do |k,v|
359+
name = accept(k)
360+
value = accept(v)
361+
hash[class_loader.symbolize(name)] = value
362+
end
363+
hash
364+
end
365+
344366
def revive_hash hash, o, tagged= false
345367
o.children.each_slice(2) { |k,v|
346368
key = accept(k)

lib/psych/visitors/yaml_tree.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,16 @@ def visit_Object o
162162

163163
alias :visit_Delegator :visit_Object
164164

165+
def visit_Data o
166+
tag = ['!ruby/data', o.class.name].compact.join(':')
167+
register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
168+
o.members.each do |member|
169+
@emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY
170+
accept o.send member
171+
end
172+
@emitter.end_mapping
173+
end
174+
165175
def visit_Struct o
166176
tag = ['!ruby/struct', o.class.name].compact.join(':')
167177

test/psych/test_object_references.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ def test_struct_has_references
3131
assert_reference_trip Struct.new(:foo).new(1)
3232
end
3333

34+
def test_data_has_references
35+
assert_reference_trip Data.define(:foo).new(1)
36+
end
37+
3438
def assert_reference_trip obj
3539
yml = Psych.dump([obj, obj])
3640
assert_match(/\*-?\d+/, yml)

test/psych/test_safe_load.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,35 @@ def test_anon_struct
109109
assert_raise(Psych::DisallowedClass) do
110110
Psych.safe_load(<<-eoyml, permitted_classes: [Symbol])
111111
--- !ruby/struct
112+
foo: bar
113+
eoyml
114+
end
115+
end
116+
117+
D = Data.define(:d)
118+
def test_data_depends_on_sym
119+
assert_safe_cycle(D.new(nil), permitted_classes: [D, Symbol])
120+
assert_raise(Psych::DisallowedClass) do
121+
cycle D.new(nil), permitted_classes: [D]
122+
end
123+
end
124+
125+
def test_anon_data
126+
assert Psych.safe_load(<<-eoyml, permitted_classes: [Data, Symbol])
127+
--- !ruby/data
128+
foo: bar
129+
eoyml
130+
131+
assert_raise(Psych::DisallowedClass) do
132+
Psych.safe_load(<<-eoyml, permitted_classes: [Data])
133+
--- !ruby/data
134+
foo: bar
135+
eoyml
136+
end
137+
138+
assert_raise(Psych::DisallowedClass) do
139+
Psych.safe_load(<<-eoyml, permitted_classes: [Symbol])
140+
--- !ruby/data
112141
foo: bar
113142
eoyml
114143
end

test/psych/test_yaml.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# [ruby-core:01946]
88
module Psych_Tests
99
StructTest = Struct::new( :c )
10+
DataTest = Data.define( :c )
1011
end
1112

1213
class Psych_Unit_Tests < Psych::TestCase
@@ -1066,6 +1067,42 @@ def test_ruby_struct
10661067

10671068
end
10681069

1070+
def test_ruby_data
1071+
Object.remove_const :MyBookData if Object.const_defined?(:MyBookData)
1072+
# Ruby Data value objects
1073+
book_class = Data.define(:author, :title, :year, :isbn)
1074+
Object.const_set(:MyBookData, book_class)
1075+
assert_to_yaml(
1076+
[ book_class.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" ),
1077+
book_class.new( [ 'Dave Thomas', 'Andy Hunt' ], "The Pickaxe", 2002,
1078+
book_class.new( "This should be the ISBN", "but I have more data here", 2002, "None" )
1079+
) ], <<EOY
1080+
- !ruby/data:MyBookData
1081+
author: Yukihiro Matsumoto
1082+
title: Ruby in a Nutshell
1083+
year: 2002
1084+
isbn: 0-596-00214-9
1085+
- !ruby/data:MyBookData
1086+
author:
1087+
- Dave Thomas
1088+
- Andy Hunt
1089+
title: The Pickaxe
1090+
year: 2002
1091+
isbn: !ruby/data:MyBookData
1092+
author: This should be the ISBN
1093+
title: but I have more data here
1094+
year: 2002
1095+
isbn: None
1096+
EOY
1097+
)
1098+
1099+
assert_to_yaml( Psych_Tests::DataTest.new( 123 ), <<EOY )
1100+
--- !ruby/data:Psych_Tests::DataTest
1101+
c: 123
1102+
EOY
1103+
1104+
end
1105+
10691106
def test_ruby_rational
10701107
assert_to_yaml( Rational(1, 2), <<EOY )
10711108
--- !ruby/object:Rational

test/psych/visitors/test_yaml_tree.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,24 @@ def test_override_method
7373
assert_equal s.method, obj.method
7474
end
7575

76+
D = Data.define(:foo)
77+
78+
def test_data
79+
assert_cycle D.new('bar')
80+
end
81+
82+
def test_data_anon
83+
d = Data.define(:foo).new('bar')
84+
obj = Psych.unsafe_load(Psych.dump(d))
85+
assert_equal d.foo, obj.foo
86+
end
87+
88+
def test_data_override_method
89+
d = Data.define(:method).new('override')
90+
obj = Psych.unsafe_load(Psych.dump(d))
91+
assert_equal d.method, obj.method
92+
end
93+
7694
def test_exception
7795
ex = Exception.new 'foo'
7896
loaded = Psych.unsafe_load(Psych.dump(ex))

0 commit comments

Comments
 (0)