representable 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +17 -10
- data/.github/workflows/ci_jruby.yml +21 -0
- data/.github/workflows/ci_truffleruby.yml +19 -0
- data/CHANGES.md +5 -0
- data/Gemfile +1 -1
- data/README.md +0 -1
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/json.rb +14 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +0 -7
- data/lib/representable/xml.rb +7 -0
- data/representable.gemspec +1 -3
- data/test/as_test.rb +8 -13
- data/test/benchmarking.rb +20 -25
- data/test/binding_test.rb +13 -5
- data/test/cached_test.rb +55 -35
- data/test/class_test.rb +46 -33
- data/test/coercion_test.rb +29 -19
- data/test/config/inherit_test.rb +12 -17
- data/test/config_test.rb +5 -7
- data/test/decorator_scope_test.rb +13 -11
- data/test/decorator_test.rb +11 -11
- data/test/default_test.rb +2 -2
- data/test/defaults_options_test.rb +37 -9
- data/test/definition_test.rb +10 -12
- data/test/examples/example.rb +43 -38
- data/test/examples/object.rb +7 -6
- data/test/exec_context_test.rb +18 -23
- data/test/features_test.rb +29 -10
- data/test/filter_test.rb +29 -20
- data/test/for_collection_test.rb +12 -15
- data/test/generic_test.rb +15 -22
- data/test/getter_setter_test.rb +11 -6
- data/test/hash_bindings_test.rb +21 -12
- data/test/hash_test.rb +53 -38
- data/test/heritage_test.rb +0 -1
- data/test/if_test.rb +25 -21
- data/test/include_exclude_test.rb +24 -16
- data/test/inherit_test.rb +114 -27
- data/test/inline_test.rb +42 -33
- data/test/instance_test.rb +151 -109
- data/test/is_representable_test.rb +19 -17
- data/test/json_test.rb +48 -46
- data/test/lonely_test.rb +35 -33
- data/test/nested_test.rb +16 -19
- data/test/object_test.rb +6 -6
- data/test/option_test.rb +10 -8
- data/test/parse_pipeline_test.rb +21 -15
- data/test/pipeline_test.rb +144 -108
- data/test/populator_test.rb +18 -15
- data/test/prepare_test.rb +15 -17
- data/test/private_options_test.rb +3 -2
- data/test/reader_writer_test.rb +4 -4
- data/test/realistic_benchmark.rb +21 -27
- data/test/render_nil_test.rb +1 -1
- data/test/represent_test.rb +14 -18
- data/test/representable_test.rb +86 -53
- data/test/schema_test.rb +51 -21
- data/test/serialize_deserialize_test.rb +14 -14
- data/test/skip_test.rb +39 -28
- data/test/stringify_hash_test.rb +29 -31
- data/test/test_helper.rb +31 -25
- data/test/test_helper_test.rb +7 -7
- data/test/uncategorized_test.rb +20 -15
- data/test/user_options_test.rb +12 -3
- data/test/wrap_test.rb +27 -17
- data/test/xml_assertions.rb +29 -0
- data/test/xml_bindings_test.rb +5 -8
- data/test/xml_namespace_test.rb +46 -39
- data/test/xml_test.rb +109 -88
- data/test/yaml_test.rb +73 -51
- metadata +7 -32
data/test/instance_test.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class InstanceTest < BaseTest
|
|
4
4
|
Song = Struct.new(:id, :title)
|
|
@@ -10,13 +10,13 @@ class InstanceTest < BaseTest
|
|
|
10
10
|
|
|
11
11
|
describe "lambda { fragment } (new way of class: lambda { nil })" do
|
|
12
12
|
representer! do
|
|
13
|
-
property :title, :instance
|
|
13
|
+
property :title, :instance => ->(options) { options[:fragment] }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
it "skips creating new instance" do
|
|
17
17
|
object = Object.new
|
|
18
18
|
object.instance_eval do
|
|
19
|
-
def from_hash(hash, *
|
|
19
|
+
def from_hash(hash, *_args)
|
|
20
20
|
hash
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -26,53 +26,71 @@ class InstanceTest < BaseTest
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
# DISCUSS: do we need parse_strategy?
|
|
29
|
+
# TODO: use *args in from_hash.
|
|
30
|
+
# DISCUSS: do we need parse_strategy?
|
|
32
31
|
describe "property with :instance" do
|
|
33
32
|
representer!(:inject => :song_representer) do
|
|
34
33
|
property :song,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
:instance => ->(options) { options[:fragment]["id"] == song.id ? song : Song.find(options[:fragment]["id"]) },
|
|
35
|
+
:extend => song_representer
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
it(
|
|
40
|
-
|
|
38
|
+
it("xxx") {
|
|
39
|
+
_(
|
|
40
|
+
OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer)
|
|
41
|
+
.from_hash("song" => {"id" => 1}).song
|
|
42
|
+
).must_equal Song.new(1, "The Answer Is Still No")
|
|
43
|
+
}
|
|
41
44
|
|
|
42
|
-
it {
|
|
43
|
-
|
|
45
|
+
it {
|
|
46
|
+
_(
|
|
47
|
+
OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer)
|
|
48
|
+
.from_hash("song" => {"id" => 2}).song
|
|
49
|
+
).must_equal Song.new(2, "Invincible")
|
|
50
|
+
}
|
|
44
51
|
end
|
|
45
52
|
|
|
46
|
-
|
|
47
53
|
describe "collection with :instance" do
|
|
48
54
|
representer!(:inject => :song_representer) do
|
|
49
55
|
collection :songs,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
:instance => ->(options) {
|
|
57
|
+
options[:fragment]["id"] == songs[options[:index]].id ? songs[options[:index]] : Song.find(options[:fragment]["id"])
|
|
58
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
:extend => song_representer
|
|
55
61
|
end
|
|
56
62
|
|
|
57
63
|
it {
|
|
58
|
-
album= Struct.new(:songs).new(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
album = Struct.new(:songs).new(
|
|
65
|
+
[
|
|
66
|
+
Song.new(1, "The Answer Is Still No"),
|
|
67
|
+
Song.new(2, "")
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
_(
|
|
72
|
+
album
|
|
73
|
+
.extend(representer)
|
|
74
|
+
.from_hash("songs" => [{"id" => 2}, {"id" => 2, "title" => "The Answer Is Still No"}]).songs
|
|
75
|
+
).must_equal [
|
|
76
|
+
Song.new(2, "Invincible"), Song.new(2, "The Answer Is Still No")
|
|
77
|
+
]
|
|
66
78
|
}
|
|
67
79
|
end
|
|
68
80
|
|
|
69
81
|
describe "property with lambda receiving fragment and args" do
|
|
70
82
|
representer!(:inject => :song_representer) do
|
|
71
|
-
property :song, :instance =>
|
|
83
|
+
property :song, :instance => ->(options) {
|
|
84
|
+
Struct.new(:args, :id).new([options[:fragment], options[:user_options]])
|
|
85
|
+
}, :extend => song_representer
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
it {
|
|
75
|
-
|
|
88
|
+
it {
|
|
89
|
+
_(
|
|
90
|
+
OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer)
|
|
91
|
+
.from_hash({"song" => {"id" => 1}}, user_options: {volume: 1}).song.args
|
|
92
|
+
).must_equal([{"id"=>1}, {:volume=>1}])
|
|
93
|
+
}
|
|
76
94
|
end
|
|
77
95
|
|
|
78
96
|
# TODO: raise and test instance:{nil}
|
|
@@ -100,26 +118,30 @@ class InstanceTest < BaseTest
|
|
|
100
118
|
describe "sync" do
|
|
101
119
|
representer!(:inject => :song_representer) do
|
|
102
120
|
collection :songs,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
:instance => ->(options) {
|
|
122
|
+
songs[options[:index]]
|
|
123
|
+
},
|
|
124
|
+
:extend => song_representer,
|
|
125
|
+
# :parse_strategy => :sync
|
|
126
|
+
:setter => ->(*) {}
|
|
109
127
|
end
|
|
110
128
|
|
|
111
129
|
it {
|
|
112
|
-
album= Struct.new(:songs).new(
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
album = Struct.new(:songs).new(
|
|
131
|
+
songs = [
|
|
132
|
+
Song.new(1, "The Answer Is Still No"),
|
|
133
|
+
Song.new(2, "Invncble")
|
|
134
|
+
]
|
|
135
|
+
)
|
|
115
136
|
|
|
116
|
-
album
|
|
117
|
-
extend(representer)
|
|
118
|
-
from_hash("songs" => [{"title" => "The Answer Is Still No"}, {"title" => "Invincible"}])
|
|
137
|
+
album
|
|
138
|
+
.extend(representer)
|
|
139
|
+
.from_hash("songs" => [{"title" => "The Answer Is Still No"}, {"title" => "Invincible"}])
|
|
119
140
|
|
|
120
141
|
_(album.songs).must_equal [
|
|
121
142
|
Song.new(1, "The Answer Is Still No"),
|
|
122
|
-
Song.new(2, "Invincible")
|
|
143
|
+
Song.new(2, "Invincible")
|
|
144
|
+
]
|
|
123
145
|
|
|
124
146
|
_(songs.object_id).must_equal album.songs.object_id
|
|
125
147
|
_(songs[0].object_id).must_equal album.songs[0].object_id
|
|
@@ -130,29 +152,34 @@ class InstanceTest < BaseTest
|
|
|
130
152
|
describe "update existing elements, only" do
|
|
131
153
|
representer!(:inject => :song_representer) do
|
|
132
154
|
collection :songs,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:setter => lambda { |*| }
|
|
155
|
+
:instance => ->(options) {
|
|
156
|
+
# fragment["id"] == songs[i].id ? songs[i] : Song.find(fragment["id"])
|
|
157
|
+
songs.find { |s| s.id == options[:fragment]["id"] }
|
|
158
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
|
159
|
+
|
|
160
|
+
:extend => song_representer,
|
|
161
|
+
# :parse_strategy => :sync
|
|
162
|
+
:setter => ->(*) {}
|
|
142
163
|
end
|
|
143
164
|
|
|
144
165
|
it("hooray") {
|
|
145
|
-
album= Struct.new(:songs).new(
|
|
146
|
-
|
|
147
|
-
Song.new(2, "Invncble")])
|
|
148
|
-
|
|
149
|
-
_(album.
|
|
150
|
-
extend(representer).
|
|
151
|
-
from_hash("songs" => [{"id" => 2, "title" => "Invincible"}]).
|
|
152
|
-
songs).must_equal [
|
|
166
|
+
album = Struct.new(:songs).new(
|
|
167
|
+
songs = [
|
|
153
168
|
Song.new(1, "The Answer Is Still No"),
|
|
154
|
-
Song.new(2, "
|
|
155
|
-
|
|
169
|
+
Song.new(2, "Invncble")
|
|
170
|
+
]
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
_(
|
|
174
|
+
album
|
|
175
|
+
.extend(representer)
|
|
176
|
+
.from_hash("songs" => [{"id" => 2, "title" => "Invincible"}])
|
|
177
|
+
.songs
|
|
178
|
+
).must_equal [
|
|
179
|
+
Song.new(1, "The Answer Is Still No"),
|
|
180
|
+
Song.new(2, "Invincible")
|
|
181
|
+
]
|
|
182
|
+
# TODO: check elements object_id!
|
|
156
183
|
|
|
157
184
|
_(songs.object_id).must_equal album.songs.object_id
|
|
158
185
|
_(songs[0].object_id).must_equal album.songs[0].object_id
|
|
@@ -160,88 +187,103 @@ class InstanceTest < BaseTest
|
|
|
160
187
|
}
|
|
161
188
|
end
|
|
162
189
|
|
|
163
|
-
|
|
164
190
|
describe "add incoming elements, only" do
|
|
165
191
|
representer!(:inject => :song_representer) do
|
|
166
192
|
collection :songs,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
:instance => ->(_options) {
|
|
194
|
+
songs << song = Song.new(2)
|
|
195
|
+
song
|
|
196
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
|
197
|
+
|
|
198
|
+
:extend => song_representer,
|
|
199
|
+
# :parse_strategy => :sync
|
|
200
|
+
:setter => ->(*) {}
|
|
175
201
|
end
|
|
176
202
|
|
|
177
203
|
it {
|
|
178
|
-
album= Struct.new(:songs).new(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
204
|
+
album = Struct.new(:songs).new(
|
|
205
|
+
songs = [
|
|
206
|
+
Song.new(1, "The Answer Is Still No")
|
|
207
|
+
]
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
_(
|
|
211
|
+
album
|
|
212
|
+
.extend(representer)
|
|
213
|
+
.from_hash("songs" => [{"title" => "Invincible"}])
|
|
214
|
+
.songs
|
|
215
|
+
).must_equal [
|
|
216
|
+
Song.new(1, "The Answer Is Still No"),
|
|
217
|
+
Song.new(2, "Invincible")
|
|
218
|
+
]
|
|
187
219
|
|
|
188
220
|
_(songs.object_id).must_equal album.songs.object_id
|
|
189
221
|
_(songs[0].object_id).must_equal album.songs[0].object_id
|
|
190
222
|
}
|
|
191
223
|
end
|
|
192
224
|
|
|
193
|
-
|
|
194
225
|
# not sure if this must be a library strategy
|
|
195
226
|
describe "replace existing element" do
|
|
196
227
|
representer!(:inject => :song_representer) do
|
|
197
228
|
collection :songs,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
229
|
+
:instance => ->(options) {
|
|
230
|
+
id = options[:fragment].delete("replace_id")
|
|
231
|
+
replaced = songs.find { |s| s.id == id }
|
|
232
|
+
songs[songs.index(replaced)] = song = Song.new(3)
|
|
233
|
+
song
|
|
234
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
|
235
|
+
|
|
236
|
+
:extend => song_representer,
|
|
237
|
+
# :parse_strategy => :sync
|
|
238
|
+
:setter => ->(*) {}
|
|
208
239
|
end
|
|
209
240
|
|
|
210
241
|
it {
|
|
211
|
-
album= Struct.new(:songs).new(
|
|
212
|
-
|
|
213
|
-
Song.new(2, "Invincible")])
|
|
214
|
-
|
|
215
|
-
_(album.
|
|
216
|
-
extend(representer).
|
|
217
|
-
from_hash("songs" => [{"replace_id"=>2, "id" => 3, "title" => "Soulmate"}]).
|
|
218
|
-
songs).must_equal [
|
|
242
|
+
album = Struct.new(:songs).new(
|
|
243
|
+
songs = [
|
|
219
244
|
Song.new(1, "The Answer Is Still No"),
|
|
220
|
-
Song.new(
|
|
245
|
+
Song.new(2, "Invincible")
|
|
246
|
+
]
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
_(
|
|
250
|
+
album
|
|
251
|
+
.extend(representer)
|
|
252
|
+
.from_hash("songs" => [{"replace_id" => 2, "id" => 3, "title" => "Soulmate"}])
|
|
253
|
+
.songs
|
|
254
|
+
).must_equal [
|
|
255
|
+
Song.new(1, "The Answer Is Still No"),
|
|
256
|
+
Song.new(3, "Soulmate")
|
|
257
|
+
]
|
|
221
258
|
|
|
222
259
|
_(songs.object_id).must_equal album.songs.object_id
|
|
223
260
|
_(songs[0].object_id).must_equal album.songs[0].object_id
|
|
224
261
|
}
|
|
225
262
|
end
|
|
226
263
|
|
|
227
|
-
|
|
228
264
|
describe "replace collection" do
|
|
229
265
|
representer!(:inject => :song_representer) do
|
|
230
266
|
collection :songs,
|
|
231
|
-
|
|
267
|
+
:extend => song_representer, :class => Song
|
|
232
268
|
end
|
|
233
269
|
|
|
234
270
|
it {
|
|
235
|
-
album= Struct.new(:songs).new(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
271
|
+
album = Struct.new(:songs).new(
|
|
272
|
+
songs = [
|
|
273
|
+
Song.new(1, "The Answer Is Still No")
|
|
274
|
+
]
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
_(
|
|
278
|
+
album
|
|
279
|
+
.extend(representer)
|
|
280
|
+
.from_hash("songs" => [{"title" => "Invincible"}])
|
|
281
|
+
.songs
|
|
282
|
+
).must_equal [
|
|
283
|
+
Song.new(nil, "Invincible")
|
|
284
|
+
]
|
|
243
285
|
|
|
244
286
|
_(songs.object_id).wont_equal album.songs.object_id
|
|
245
287
|
}
|
|
246
288
|
end
|
|
247
|
-
end
|
|
289
|
+
end
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class IsRepresentableTest < BaseTest
|
|
4
4
|
describe "representable: false, extend:" do
|
|
5
5
|
representer!(:inject => :song_representer) do
|
|
6
6
|
property :song,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
:representable => false,
|
|
8
|
+
:extend => song_representer
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it "does extend but doesn't call #to_hash" do
|
|
12
|
-
_(
|
|
13
|
-
|
|
12
|
+
_(
|
|
13
|
+
Struct.new(:song).new(song = Object.new).extend(representer)
|
|
14
|
+
.to_hash
|
|
15
|
+
).must_equal("song" => song)
|
|
14
16
|
_(song).must_be_kind_of Representable::Hash
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
|
|
19
20
|
describe "representable: true, no extend:" do
|
|
20
21
|
representer!(:inject => :song_representer) do
|
|
21
22
|
property :song,
|
|
22
|
-
|
|
23
|
+
:representable => true
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
it "doesn't extend but calls #to_hash" do
|
|
@@ -30,8 +31,10 @@ class IsRepresentableTest < BaseTest
|
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
_(
|
|
34
|
-
|
|
34
|
+
_(
|
|
35
|
+
Struct.new(:song).new(song).extend(representer)
|
|
36
|
+
.to_hash
|
|
37
|
+
).must_equal("song" => 1)
|
|
35
38
|
_(song).wont_be_kind_of Representable::Hash
|
|
36
39
|
end
|
|
37
40
|
end
|
|
@@ -41,19 +44,18 @@ class IsRepresentableTest < BaseTest
|
|
|
41
44
|
describe "representable: false, with class:" do
|
|
42
45
|
representer!(:inject => :song_representer) do
|
|
43
46
|
property :song,
|
|
44
|
-
|
|
47
|
+
:representable => false, :class => OpenStruct, :extend => song_representer
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
it "does extend but doesn't call #from_hash" do
|
|
48
|
-
hit = Struct.new(:song).new.extend(representer)
|
|
49
|
-
|
|
51
|
+
hit = Struct.new(:song).new.extend(representer)
|
|
52
|
+
.from_hash("song" => 1)
|
|
50
53
|
|
|
51
54
|
_(hit.song).must_equal OpenStruct.new
|
|
52
55
|
_(hit.song).must_be_kind_of Representable::Hash
|
|
53
56
|
end
|
|
54
57
|
end
|
|
55
58
|
|
|
56
|
-
|
|
57
59
|
describe "representable: true, without extend: but class:" do
|
|
58
60
|
SongReader = Class.new do
|
|
59
61
|
def from_hash(*)
|
|
@@ -63,15 +65,15 @@ class IsRepresentableTest < BaseTest
|
|
|
63
65
|
|
|
64
66
|
representer!(:inject => :song_representer) do
|
|
65
67
|
property :song,
|
|
66
|
-
|
|
68
|
+
:representable => true, :class => SongReader
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
it "doesn't extend but calls #from_hash" do
|
|
70
|
-
hit = Struct.new(:song).new.extend(representer)
|
|
71
|
-
|
|
72
|
+
hit = Struct.new(:song).new.extend(representer)
|
|
73
|
+
.from_hash("song" => "Sonata No.2")
|
|
72
74
|
|
|
73
75
|
_(hit.song).must_equal "Piano?"
|
|
74
76
|
_(hit.song).wont_be_kind_of Representable::Hash
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
|
-
end
|
|
79
|
+
end
|