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/pipeline_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class PipelineTest <
|
|
3
|
+
class PipelineTest < Minitest::Spec
|
|
4
4
|
Song = Struct.new(:title, :artist)
|
|
5
5
|
Artist = Struct.new(:name)
|
|
6
6
|
Album = Struct.new(:ratings, :artists)
|
|
@@ -8,32 +8,29 @@ class PipelineTest < MiniTest::Spec
|
|
|
8
8
|
R = Representable
|
|
9
9
|
P = R::Pipeline
|
|
10
10
|
|
|
11
|
-
Getter = ->(
|
|
12
|
-
StopOnNil = ->(input,
|
|
11
|
+
Getter = ->(_input, _options) { "Yo" }
|
|
12
|
+
StopOnNil = ->(input, _options) { input }
|
|
13
13
|
SkipRender = ->(input, *) { input == "Yo" ? input : P::Stop }
|
|
14
14
|
|
|
15
|
-
Prepare = ->(input,
|
|
15
|
+
Prepare = ->(input, _options) { "Prepare(#{input})" }
|
|
16
16
|
Deserialize = ->(input, options) { "Deserialize(#{input}, #{options[:fragment]})" }
|
|
17
17
|
|
|
18
|
-
SkipParse = ->(input,
|
|
19
|
-
CreateObject = ->(
|
|
18
|
+
SkipParse = ->(input, _options) { input }
|
|
19
|
+
CreateObject = ->(_input, _options) { OpenStruct.new }
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
Setter = ->(input, options) { "Setter(#{input})" }
|
|
21
|
+
Setter = ->(input, _options) { "Setter(#{input})" }
|
|
23
22
|
|
|
24
23
|
AssignFragment = ->(input, options) { options[:fragment] = input }
|
|
25
24
|
|
|
26
25
|
it "linear" do
|
|
27
26
|
_(P[SkipParse, Setter].("doc", {fragment: 1})).must_equal "Setter(doc)"
|
|
28
27
|
|
|
29
|
-
|
|
30
28
|
# parse style.
|
|
31
29
|
_(P[AssignFragment, SkipParse, CreateObject, Prepare].("Bla", {})).must_equal "Prepare(#<OpenStruct>)"
|
|
32
30
|
|
|
33
|
-
|
|
34
31
|
# render style.
|
|
35
|
-
_(P[Getter, StopOnNil, SkipRender, Prepare, Setter].(nil, {}))
|
|
36
|
-
must_equal "Setter(Prepare(Yo))"
|
|
32
|
+
_(P[Getter, StopOnNil, SkipRender, Prepare, Setter].(nil, {}))
|
|
33
|
+
.must_equal "Setter(Prepare(Yo))"
|
|
37
34
|
|
|
38
35
|
# pipeline = Representable::Pipeline[SkipParse , SetResult, ModifyResult]
|
|
39
36
|
# pipeline.(fragment: "yo!").must_equal "modified object from yo!"
|
|
@@ -41,18 +38,15 @@ class PipelineTest < MiniTest::Spec
|
|
|
41
38
|
|
|
42
39
|
Stopping = ->(input, options) { return P::Stop if options[:fragment] == "stop!"; input }
|
|
43
40
|
|
|
44
|
-
|
|
45
41
|
it "stopping" do
|
|
46
|
-
|
|
47
|
-
|
|
48
42
|
pipeline = Representable::Pipeline[SkipParse, Stopping, Prepare]
|
|
49
43
|
_(pipeline.(nil, fragment: "oy!")).must_equal "Prepare()"
|
|
50
44
|
_(pipeline.(nil, fragment: "stop!")).must_equal Representable::Pipeline::Stop
|
|
51
45
|
end
|
|
52
46
|
|
|
53
47
|
describe "Collect" do
|
|
54
|
-
Reverse = ->(input,
|
|
55
|
-
Add = ->(input,
|
|
48
|
+
Reverse = ->(input, _options) { input.reverse }
|
|
49
|
+
Add = ->(input, _options) { "#{input}+" }
|
|
56
50
|
let(:pipeline) { R::Collect[Reverse, Add] }
|
|
57
51
|
|
|
58
52
|
it { _(pipeline.(["yo!", "oy!"], {})).must_equal ["!oy+", "!yo+"] }
|
|
@@ -63,149 +57,172 @@ class PipelineTest < MiniTest::Spec
|
|
|
63
57
|
end
|
|
64
58
|
end
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
60
|
######### scalar property
|
|
70
61
|
|
|
71
|
-
let(:title)
|
|
62
|
+
let(:title) do
|
|
72
63
|
dfn = R::Definition.new(:title)
|
|
73
64
|
|
|
74
65
|
R::Hash::Binding.new(dfn)
|
|
75
|
-
|
|
66
|
+
end
|
|
76
67
|
|
|
77
68
|
it "rendering scalar property" do
|
|
78
69
|
doc = {}
|
|
79
|
-
_(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
_(
|
|
71
|
+
P[
|
|
72
|
+
R::GetValue,
|
|
73
|
+
R::StopOnSkipable,
|
|
74
|
+
R::AssignName,
|
|
75
|
+
R::WriteFragment
|
|
76
|
+
].(nil, {represented: Song.new("Lime Green"), binding: title, doc: doc})
|
|
77
|
+
).must_equal "Lime Green"
|
|
85
78
|
|
|
86
79
|
_(doc).must_equal({"title"=>"Lime Green"})
|
|
87
80
|
end
|
|
88
81
|
|
|
89
82
|
it "parsing scalar property" do
|
|
90
|
-
_(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
_(
|
|
84
|
+
P[
|
|
85
|
+
R::AssignName,
|
|
86
|
+
R::ReadFragment,
|
|
87
|
+
R::StopOnNotFound,
|
|
88
|
+
R::OverwriteOnNil,
|
|
89
|
+
# R::SkipParse,
|
|
90
|
+
R::SetValue,
|
|
91
|
+
].extend(P::Debug).(doc = {"title"=>"Eruption"}, {
|
|
92
|
+
represented: song = Song.new("Lime Green"), binding: title,
|
|
93
|
+
doc: doc
|
|
94
|
+
})
|
|
95
|
+
).must_equal "Eruption"
|
|
98
96
|
_(song.title).must_equal "Eruption"
|
|
99
97
|
end
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
|
|
103
99
|
module ArtistRepresenter
|
|
104
100
|
include Representable::Hash
|
|
105
101
|
property :name
|
|
106
102
|
end
|
|
107
103
|
|
|
108
|
-
let(:artist)
|
|
104
|
+
let(:artist) do
|
|
109
105
|
dfn = R::Definition.new(:artist, extend: ArtistRepresenter, class: Artist)
|
|
110
106
|
|
|
111
107
|
R::Hash::Binding.new(dfn)
|
|
112
|
-
|
|
108
|
+
end
|
|
113
109
|
|
|
114
110
|
let(:song_model) { Song.new("Lime Green", Artist.new("Diesel Boy")) }
|
|
115
111
|
|
|
116
112
|
it "rendering typed property" do
|
|
117
113
|
doc = {}
|
|
118
|
-
_(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
114
|
+
_(
|
|
115
|
+
P[
|
|
116
|
+
R::GetValue,
|
|
117
|
+
R::StopOnSkipable,
|
|
118
|
+
R::StopOnNil,
|
|
119
|
+
R::Decorate,
|
|
120
|
+
R::Serialize,
|
|
121
|
+
R::AssignName,
|
|
122
|
+
R::WriteFragment
|
|
123
|
+
].extend(P::Debug).(nil, {
|
|
124
|
+
represented: song_model, binding: artist, doc: doc,
|
|
125
|
+
options: {}
|
|
126
|
+
})
|
|
127
|
+
).must_equal({"name" => "Diesel Boy"})
|
|
127
128
|
|
|
128
129
|
_(doc).must_equal({"artist"=>{"name"=>"Diesel Boy"}})
|
|
129
130
|
end
|
|
130
131
|
|
|
131
132
|
it "parsing typed property" do
|
|
132
|
-
_(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
_(
|
|
134
|
+
P[
|
|
135
|
+
R::AssignName,
|
|
136
|
+
R::ReadFragment,
|
|
137
|
+
R::StopOnNotFound,
|
|
138
|
+
R::OverwriteOnNil,
|
|
139
|
+
R::AssignFragment,
|
|
140
|
+
R::CreateObject::Class,
|
|
141
|
+
R::Decorate,
|
|
142
|
+
R::Deserialize,
|
|
143
|
+
R::SetValue,
|
|
144
|
+
].extend(P::Debug).(doc = {"artist"=>{"name"=>"Doobie Brothers"}}, {
|
|
145
|
+
represented: song_model, binding: artist, doc: doc,
|
|
146
|
+
options: {}
|
|
147
|
+
})
|
|
148
|
+
).must_equal model = Artist.new("Doobie Brothers")
|
|
143
149
|
_(song_model.artist).must_equal model
|
|
144
150
|
end
|
|
145
151
|
|
|
146
|
-
|
|
147
152
|
######### collection :ratings
|
|
148
153
|
|
|
149
|
-
let(:ratings)
|
|
154
|
+
let(:ratings) do
|
|
150
155
|
dfn = R::Definition.new(:ratings, collection: true, skip_render: ->(*) { false })
|
|
151
156
|
|
|
152
157
|
R::Hash::Binding::Collection.new(dfn)
|
|
153
|
-
|
|
158
|
+
end
|
|
154
159
|
it "render scalar collection" do
|
|
155
160
|
doc = {}
|
|
156
|
-
_(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
_(
|
|
162
|
+
P[
|
|
163
|
+
R::GetValue,
|
|
164
|
+
R::StopOnSkipable,
|
|
165
|
+
R::Collect[
|
|
166
|
+
R::SkipRender,
|
|
167
|
+
],
|
|
168
|
+
R::AssignName,
|
|
169
|
+
R::WriteFragment
|
|
170
|
+
].extend(P::Debug).(nil, {represented: Album.new([1, 2, 3]), binding: ratings, doc: doc, options: {}})
|
|
171
|
+
).must_equal([1, 2, 3])
|
|
172
|
+
|
|
173
|
+
_(doc).must_equal({"ratings"=>[1, 2, 3]})
|
|
167
174
|
end
|
|
168
175
|
|
|
169
|
-
######### collection :songs, extend: SongRepresenter
|
|
170
|
-
let(:artists)
|
|
176
|
+
######### collection :songs, extend: SongRepresenter
|
|
177
|
+
let(:artists) do
|
|
171
178
|
dfn = R::Definition.new(:artists, collection: true, extend: ArtistRepresenter, class: Artist)
|
|
172
179
|
|
|
173
180
|
R::Hash::Binding::Collection.new(dfn)
|
|
174
|
-
|
|
181
|
+
end
|
|
175
182
|
it "render typed collection" do
|
|
176
183
|
doc = {}
|
|
177
|
-
_(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
_(
|
|
185
|
+
P[
|
|
186
|
+
R::GetValue,
|
|
187
|
+
R::StopOnSkipable,
|
|
188
|
+
R::Collect[
|
|
189
|
+
R::Decorate,
|
|
190
|
+
R::Serialize,
|
|
191
|
+
],
|
|
192
|
+
R::AssignName,
|
|
193
|
+
R::WriteFragment
|
|
194
|
+
].extend(P::Debug).(nil, {
|
|
195
|
+
represented: Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]), binding: artists,
|
|
196
|
+
doc: doc, options: {}
|
|
197
|
+
})
|
|
198
|
+
).must_equal([{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}])
|
|
187
199
|
|
|
188
200
|
_(doc).must_equal({"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]})
|
|
189
201
|
end
|
|
190
202
|
|
|
191
|
-
let(:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]) }
|
|
203
|
+
let(:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]) }
|
|
192
204
|
|
|
193
205
|
it "parse typed collection" do
|
|
194
206
|
doc = {"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]}
|
|
195
|
-
_(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
_(
|
|
208
|
+
P[
|
|
209
|
+
R::AssignName,
|
|
210
|
+
R::ReadFragment,
|
|
211
|
+
R::StopOnNotFound,
|
|
212
|
+
R::OverwriteOnNil,
|
|
213
|
+
# R::SkipParse,
|
|
214
|
+
R::Collect[
|
|
215
|
+
R::AssignFragment,
|
|
216
|
+
R::CreateObject::Class,
|
|
217
|
+
R::Decorate,
|
|
218
|
+
R::Deserialize,
|
|
219
|
+
],
|
|
220
|
+
R::SetValue,
|
|
221
|
+
].extend(P::Debug).(doc, {
|
|
222
|
+
represented: album_model, binding: artists, doc: doc,
|
|
223
|
+
options: {}
|
|
224
|
+
})
|
|
225
|
+
).must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
|
|
209
226
|
|
|
210
227
|
_(album_model.artists).must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
|
|
211
228
|
end
|
|
@@ -238,17 +255,31 @@ let(:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Ha
|
|
|
238
255
|
it "applies on nested Collect" do
|
|
239
256
|
pipeline = P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
|
|
240
257
|
|
|
241
|
-
_(
|
|
258
|
+
_(
|
|
259
|
+
P::Insert.(
|
|
260
|
+
pipeline, R::Default,
|
|
261
|
+
replace: R::StopOnSkipable
|
|
262
|
+
).extend(P::Debug).inspect
|
|
263
|
+
).must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
|
|
242
264
|
_(pipeline).must_equal P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
|
|
243
265
|
|
|
244
|
-
|
|
245
|
-
|
|
266
|
+
_(
|
|
267
|
+
P::Insert.(
|
|
268
|
+
pipeline, R::Default,
|
|
269
|
+
replace: R::StopOnNil
|
|
270
|
+
).extend(P::Debug).inspect
|
|
271
|
+
).must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], Default]"
|
|
246
272
|
end
|
|
247
273
|
|
|
248
274
|
it "applies on nested Collect with Function::CreateObject" do
|
|
249
275
|
pipeline = P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
|
|
250
276
|
|
|
251
|
-
_(
|
|
277
|
+
_(
|
|
278
|
+
P::Insert.(
|
|
279
|
+
pipeline, R::Default,
|
|
280
|
+
replace: R::CreateObject
|
|
281
|
+
).extend(P::Debug).inspect
|
|
282
|
+
).must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
|
|
252
283
|
_(pipeline).must_equal P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
|
|
253
284
|
end
|
|
254
285
|
end
|
|
@@ -266,8 +297,13 @@ let(:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Ha
|
|
|
266
297
|
let(:pipeline) { P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil] }
|
|
267
298
|
|
|
268
299
|
it do
|
|
269
|
-
_(
|
|
300
|
+
_(
|
|
301
|
+
P::Insert.(
|
|
302
|
+
pipeline, R::GetValue,
|
|
303
|
+
delete: true
|
|
304
|
+
).extend(P::Debug).inspect
|
|
305
|
+
).must_equal "Pipeline[Collect[StopOnSkipable], StopOnNil]"
|
|
270
306
|
_(pipeline.extend(P::Debug).inspect).must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], StopOnNil]"
|
|
271
307
|
end
|
|
272
308
|
end
|
|
273
|
-
end
|
|
309
|
+
end
|
data/test/populator_test.rb
CHANGED
|
@@ -7,11 +7,11 @@ class PopulatorTest < Minitest::Spec
|
|
|
7
7
|
|
|
8
8
|
describe "populator: ->{}" do
|
|
9
9
|
representer! do
|
|
10
|
-
collection :songs, populator: ->(
|
|
10
|
+
collection :songs, populator: ->(_input, options) { options[:represented].songs << song = Song.new; song } do
|
|
11
11
|
property :id
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
property :artist, populator: ->(
|
|
14
|
+
property :artist, populator: ->(_input, options) { options[:represented].artist = Artist.new } do
|
|
15
15
|
property :name
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -19,20 +19,20 @@ class PopulatorTest < Minitest::Spec
|
|
|
19
19
|
let(:album) { Album.new([]) }
|
|
20
20
|
|
|
21
21
|
it do
|
|
22
|
-
album.extend(representer).from_hash("songs"=>[{"id"=>1}, {"id"=>2}], "artist"=>{"name"=>"Waste"})
|
|
22
|
+
album.extend(representer).from_hash("songs" => [{"id"=>1}, {"id"=>2}], "artist" => {"name"=>"Waste"})
|
|
23
23
|
_(album.inspect).must_equal "#<struct PopulatorTest::Album songs=[#<struct PopulatorTest::Song id=1>, #<struct PopulatorTest::Song id=2>], artist=#<struct PopulatorTest::Artist name=\"Waste\">>"
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
describe "populator: ->{}, " do
|
|
28
|
-
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
class PopulatorFindOrInstantiateTest < Minitest::Spec
|
|
33
32
|
Song = Struct.new(:id, :title, :uid) do
|
|
34
|
-
def self.find_by(attributes={})
|
|
35
|
-
return new(1, "Resist Stan", "abcd") if attributes[:id]==1# we should return the same object here
|
|
33
|
+
def self.find_by(attributes = {})
|
|
34
|
+
return new(1, "Resist Stan", "abcd") if attributes[:id] == 1 # we should return the same object here
|
|
35
|
+
|
|
36
36
|
new
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -58,9 +58,9 @@ class PopulatorFindOrInstantiateTest < Minitest::Spec
|
|
|
58
58
|
let(:album) { Composer.new.extend(representer).extend(Representable::Debug) }
|
|
59
59
|
|
|
60
60
|
it "finds by :id and creates new without :id" do
|
|
61
|
-
album.from_hash({"song"=>{"id" => 1, "title"=>"Resist Stance"}})
|
|
61
|
+
album.from_hash({"song"=>{"id" => 1, "title" => "Resist Stance"}})
|
|
62
62
|
|
|
63
|
-
_(album.song.title).must_equal "Resist Stance" #
|
|
63
|
+
_(album.song.title).must_equal "Resist Stance" # NOTE: how title is updated from "Resist Stan"
|
|
64
64
|
_(album.song.id).must_equal 1
|
|
65
65
|
_(album.song.uid).must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
|
|
66
66
|
end
|
|
@@ -85,12 +85,16 @@ class PopulatorFindOrInstantiateTest < Minitest::Spec
|
|
|
85
85
|
let(:album) { Struct.new(:songs).new([]).extend(representer) }
|
|
86
86
|
|
|
87
87
|
it "finds by :id and creates new without :id" do
|
|
88
|
-
album.from_hash(
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
album.from_hash(
|
|
89
|
+
{
|
|
90
|
+
"songs" => [
|
|
91
|
+
{"id" => 1, "title" => "Resist Stance"},
|
|
92
|
+
{"title"=>"Suffer"}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
_(album.songs[0].title).must_equal "Resist Stance" # NOTE: how title is updated from "Resist Stan"
|
|
94
98
|
_(album.songs[0].id).must_equal 1
|
|
95
99
|
_(album.songs[0].uid).must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
|
|
96
100
|
|
|
@@ -101,5 +105,4 @@ class PopulatorFindOrInstantiateTest < Minitest::Spec
|
|
|
101
105
|
|
|
102
106
|
# TODO: test with existing collection
|
|
103
107
|
end
|
|
104
|
-
|
|
105
108
|
end
|
data/test/prepare_test.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class PrepareTest < BaseTest
|
|
4
4
|
class PreparerClass
|
|
@@ -6,10 +6,10 @@ class PrepareTest < BaseTest
|
|
|
6
6
|
@object = object
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def ==(
|
|
10
|
-
return unless
|
|
9
|
+
def ==(other)
|
|
10
|
+
return unless other.instance_of?(PreparerClass)
|
|
11
11
|
|
|
12
|
-
object ==
|
|
12
|
+
object == other.object
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
attr_reader :object
|
|
@@ -18,10 +18,10 @@ class PrepareTest < BaseTest
|
|
|
18
18
|
describe "#to_hash" do # TODO: introduce :representable option?
|
|
19
19
|
representer! do
|
|
20
20
|
property :song,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
:prepare => ->(options) { options[:binding][:arbitrary].new(options[:input]) },
|
|
22
|
+
:arbitrary => PreparerClass,
|
|
23
|
+
:extend => true,
|
|
24
|
+
:representable => false # don't call #to_hash.
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
let(:hit) { Struct.new(:song).new(song).extend(representer) }
|
|
@@ -31,7 +31,6 @@ class PrepareTest < BaseTest
|
|
|
31
31
|
_(hit.to_hash).must_equal({"song" => PreparerClass.new(song)})
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
35
34
|
# it "calls #from_hash on the existing song instance, nothing else" do
|
|
36
35
|
# song_id = hit.song.object_id
|
|
37
36
|
|
|
@@ -42,16 +41,15 @@ class PrepareTest < BaseTest
|
|
|
42
41
|
# end
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
|
|
46
44
|
describe "#from_hash" do
|
|
47
45
|
representer! do
|
|
48
46
|
property :song,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
:prepare => ->(options) { options[:binding][:arbitrary].new(options[:input]) },
|
|
48
|
+
:arbitrary => PreparerClass,
|
|
49
|
+
# :extend => true, # TODO: typed: true would be better.
|
|
50
|
+
:instance => String.new, # pass_fragment
|
|
51
|
+
:pass_options => true,
|
|
52
|
+
:representable => false # don't call #to_hash.
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
let(:hit) { Struct.new(:song).new.extend(representer) }
|
|
@@ -63,4 +61,4 @@ class PrepareTest < BaseTest
|
|
|
63
61
|
_(hit.song).must_equal(PreparerClass.new(String.new))
|
|
64
62
|
end
|
|
65
63
|
end
|
|
66
|
-
end
|
|
64
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# TODO: move me to separate file.
|
|
4
|
+
class PrivateOptionsTest < Minitest::Spec
|
|
4
5
|
representer!(decorator: true) do
|
|
5
6
|
end
|
|
6
7
|
|
|
@@ -15,4 +16,4 @@ class PrivateOptionsTest < MiniTest::Spec # TODO: move me to separate file.
|
|
|
15
16
|
representer.new(nil).from_hash(options)
|
|
16
17
|
_(options).must_equal({exclude: "name"})
|
|
17
18
|
end
|
|
18
|
-
end
|
|
19
|
+
end
|
data/test/reader_writer_test.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class ReaderWriterTest < BaseTest
|
|
4
4
|
representer! do
|
|
5
5
|
property :name,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
:writer => ->(options) { options[:doc]["title"] = "#{options[:user_options][:nr]}) #{options[:input]}" },
|
|
7
|
+
:reader => ->(options) { self.name = options[:doc]["title"].split(") ").last }
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
subject { OpenStruct.new(:name => "Disorder And Disarray").extend(representer) }
|
|
@@ -16,4 +16,4 @@ class ReaderWriterTest < BaseTest
|
|
|
16
16
|
it "uses :reader when parsing" do
|
|
17
17
|
_(subject.from_hash({"title" => "15) The Wars End"}).name).must_equal "The Wars End"
|
|
18
18
|
end
|
|
19
|
-
end
|
|
19
|
+
end
|
data/test/realistic_benchmark.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "benchmark"
|
|
3
3
|
|
|
4
4
|
SONG_PROPERTIES = 50.times.collect do |i|
|
|
5
5
|
"song_property_#{i}"
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
module SongRepresenter
|
|
10
9
|
include Representable::JSON
|
|
11
10
|
|
|
@@ -18,7 +17,6 @@ class NestedProperty < Representable::Decorator
|
|
|
18
17
|
SONG_PROPERTIES.each { |p| property p }
|
|
19
18
|
end
|
|
20
19
|
|
|
21
|
-
|
|
22
20
|
class SongDecorator < Representable::Decorator
|
|
23
21
|
include Representable::JSON
|
|
24
22
|
|
|
@@ -36,7 +34,7 @@ Song = Struct.new(*SONG_PROPERTIES.map(&:to_sym))
|
|
|
36
34
|
Album = Struct.new(:songs)
|
|
37
35
|
|
|
38
36
|
def random_song
|
|
39
|
-
Song.new(*SONG_PROPERTIES.collect { |
|
|
37
|
+
Song.new(*SONG_PROPERTIES.collect { |_p| Song.new(*SONG_PROPERTIES) })
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
times = []
|
|
@@ -53,7 +51,7 @@ end
|
|
|
53
51
|
puts times.join("")
|
|
54
52
|
|
|
55
53
|
album = Album.new(100.times.collect { random_song })
|
|
56
|
-
require
|
|
54
|
+
require "ruby-prof"
|
|
57
55
|
RubyProf.start
|
|
58
56
|
AlbumRepresenter.new(album).to_hash
|
|
59
57
|
res = RubyProf.stop
|
|
@@ -68,30 +66,26 @@ array[0..60].each { |a| puts a }
|
|
|
68
66
|
## 100 songs, 1000 attrs
|
|
69
67
|
# 0.470000 0.010000 0.480000 ( 0.483708)
|
|
70
68
|
|
|
71
|
-
|
|
72
69
|
### without binding cache:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
# 2.790000 0.030000 2.820000 ( 2.820190)
|
|
76
71
|
|
|
77
72
|
### with extend: on Song, with binding cache>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
# 2.490000 0.030000 2.520000 ( 2.517433) 2.4-3.0
|
|
74
|
+
### without skip?
|
|
75
|
+
# 2.030000 0.020000 2.050000 ( 2.050796) 2.1-2.3
|
|
76
|
+
|
|
77
|
+
### without :writer
|
|
78
|
+
# 2.270000 0.010000 2.280000 ( 2.284530 1.9-2.2
|
|
79
|
+
### without :render_filter
|
|
80
|
+
# 2.020000 0.000000 2.020000 ( 2.030234) 1.5-2.0
|
|
81
|
+
# ##without default_for and skipable?
|
|
82
|
+
# 1.730000 0.010000 1.740000 ( 1.735597 1.4-1.7
|
|
83
|
+
### without :serialize
|
|
84
|
+
# 1.780000 0.010000 1.790000 ( 1.786791) 1.4-1.7
|
|
85
|
+
### using decorator
|
|
86
|
+
# 1.400000 0.030000 1.430000 ( 1.434206) 1.4-1.6
|
|
87
|
+
### with prepare AFTER representable?
|
|
88
|
+
# 1.330000 0.010000 1.340000 ( 1.335900) 1.1-1.3
|
|
95
89
|
|
|
96
90
|
# representable 2.0
|
|
97
91
|
# 3.000000 0.020000 3.020000 ( 3.013031) 2.7-3.0
|