representable 2.3.0 → 2.4.0.rc1
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/CHANGES.md +47 -0
- data/Gemfile +5 -0
- data/README.md +33 -0
- data/lib/representable.rb +60 -73
- data/lib/representable/binding.rb +37 -194
- data/lib/representable/cached.rb +10 -46
- data/lib/representable/coercion.rb +8 -8
- data/lib/representable/config.rb +15 -75
- data/lib/representable/debug.rb +41 -59
- data/lib/representable/declarative.rb +34 -53
- data/lib/representable/decorator.rb +11 -40
- data/lib/representable/definition.rb +14 -15
- data/lib/representable/deprecations.rb +90 -0
- data/lib/representable/deserializer.rb +87 -82
- data/lib/representable/for_collection.rb +5 -3
- data/lib/representable/hash.rb +5 -3
- data/lib/representable/hash/binding.rb +6 -15
- data/lib/representable/hash/collection.rb +10 -6
- data/lib/representable/hash_methods.rb +5 -5
- data/lib/representable/insert.rb +31 -0
- data/lib/representable/json.rb +7 -3
- data/lib/representable/json/hash.rb +1 -1
- data/lib/representable/object/binding.rb +5 -5
- data/lib/representable/parse_strategies.rb +37 -3
- data/lib/representable/pipeline.rb +37 -5
- data/lib/representable/pipeline_factories.rb +88 -0
- data/lib/representable/serializer.rb +38 -44
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +4 -0
- data/lib/representable/xml/binding.rb +25 -31
- data/lib/representable/xml/collection.rb +5 -3
- data/lib/representable/xml/hash.rb +7 -2
- data/lib/representable/yaml.rb +6 -3
- data/lib/representable/yaml/binding.rb +4 -4
- data/representable.gemspec +3 -3
- data/test/---deserialize-pipeline_test.rb +37 -0
- data/test/binding_test.rb +7 -7
- data/test/cached_test.rb +31 -19
- data/test/coercion_test.rb +2 -2
- data/test/config/inherit_test.rb +13 -12
- data/test/config_test.rb +12 -67
- data/test/decorator_test.rb +4 -5
- data/test/default_test.rb +34 -0
- data/test/defaults_options_test.rb +93 -0
- data/test/definition_test.rb +19 -39
- data/test/exec_context_test.rb +1 -1
- data/test/filter_test.rb +18 -20
- data/test/getter_setter_test.rb +1 -8
- data/test/hash_bindings_test.rb +13 -13
- data/test/heritage_test.rb +62 -0
- data/test/if_test.rb +1 -0
- data/test/inherit_test.rb +5 -3
- data/test/instance_test.rb +3 -4
- data/test/json_test.rb +3 -59
- data/test/lonely_test.rb +47 -3
- data/test/nested_test.rb +8 -2
- data/test/pipeline_test.rb +259 -0
- data/test/populator_test.rb +76 -0
- data/test/realistic_benchmark.rb +39 -7
- data/test/render_nil_test.rb +21 -0
- data/test/represent_test.rb +2 -2
- data/test/representable_test.rb +33 -103
- data/test/schema_test.rb +5 -15
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +1 -1
- data/test/test_helper.rb +6 -0
- data/test/uncategorized_test.rb +67 -0
- data/test/xml_bindings_test.rb +6 -6
- data/test/xml_test.rb +6 -6
- metadata +33 -13
- data/lib/representable/apply.rb +0 -13
- data/lib/representable/inheritable.rb +0 -71
- data/lib/representable/mapper.rb +0 -83
- data/lib/representable/populator.rb +0 -56
- data/test/inheritable_test.rb +0 -97
data/test/nested_test.rb
CHANGED
@@ -27,10 +27,16 @@ class NestedTest < MiniTest::Spec
|
|
27
27
|
# self.representation_wrap = :album if format == :xml
|
28
28
|
end
|
29
29
|
|
30
|
-
let (:album) {
|
30
|
+
let (:album) { Album.new("Epitaph", "Brett Gurewitz", 19) }
|
31
|
+
let (:decorator) { representer.prepare(album) }
|
31
32
|
|
32
33
|
it "renders nested Album-properties in separate section" do
|
33
|
-
render(
|
34
|
+
render(decorator).must_equal_document output
|
35
|
+
|
36
|
+
# do not use extend on the nested object. # FIXME: make this a proper test with two describes instead of this pseudo-meta stuff.
|
37
|
+
if is_decorator==true
|
38
|
+
album.wont_be_kind_of(Representable::Hash)
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
it "parses nested properties to Album instance" do
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PipelineTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :artist)
|
5
|
+
Artist = Struct.new(:name)
|
6
|
+
Album = Struct.new(:ratings, :artists)
|
7
|
+
|
8
|
+
R = Representable
|
9
|
+
P = R::Pipeline
|
10
|
+
|
11
|
+
Getter = ->(input, options) { "Yo" }
|
12
|
+
StopOnNil = ->(input, options) { input }
|
13
|
+
SkipRender = ->(input, *) { input == "Yo" ? input : P::Stop }
|
14
|
+
|
15
|
+
Prepare = ->(input, options) { "Prepare(#{input})" }
|
16
|
+
Deserialize = ->(input, options) { "Deserialize(#{input}, #{options[:fragment]})" }
|
17
|
+
|
18
|
+
SkipParse = ->(input, options) { input }
|
19
|
+
CreateObject = ->(input, options) { OpenStruct.new }
|
20
|
+
|
21
|
+
|
22
|
+
Setter = ->(input, options) { "Setter(#{input})" }
|
23
|
+
|
24
|
+
AssignFragment = ->(input, options) { options[:fragment] = input }
|
25
|
+
|
26
|
+
it "linear" do
|
27
|
+
P[SkipParse, Setter].("doc", {fragment: 1}).must_equal "Setter(doc)"
|
28
|
+
|
29
|
+
|
30
|
+
# parse style.
|
31
|
+
P[AssignFragment, SkipParse, CreateObject, Prepare].("Bla", {}).must_equal "Prepare(#<OpenStruct>)"
|
32
|
+
|
33
|
+
|
34
|
+
# render style.
|
35
|
+
P[Getter, StopOnNil, SkipRender, Prepare, Setter].(nil, {}).
|
36
|
+
must_equal "Setter(Prepare(Yo))"
|
37
|
+
|
38
|
+
# pipeline = Representable::Pipeline[SkipParse , SetResult, ModifyResult]
|
39
|
+
# pipeline.(fragment: "yo!").must_equal "modified object from yo!"
|
40
|
+
end
|
41
|
+
|
42
|
+
Stopping = ->(input, options) { return P::Stop if options[:fragment] == "stop!"; input }
|
43
|
+
|
44
|
+
|
45
|
+
it "stopping" do
|
46
|
+
|
47
|
+
|
48
|
+
pipeline = Representable::Pipeline[SkipParse, Stopping, Prepare]
|
49
|
+
pipeline.(nil, fragment: "oy!").must_equal "Prepare()"
|
50
|
+
pipeline.(nil, fragment: "stop!").must_equal Representable::Pipeline::Stop
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "Collect" do
|
54
|
+
Reverse = ->(input, options) { input.reverse }
|
55
|
+
Add = ->(input, options) { "#{input}+" }
|
56
|
+
let(:pipeline) { R::Collect[Reverse, Add] }
|
57
|
+
|
58
|
+
it { pipeline.(["yo!", "oy!"], {}).must_equal ["!oy+", "!yo+"] }
|
59
|
+
|
60
|
+
describe "Pipeline with Collect" do
|
61
|
+
let(:pipeline) { P[Reverse, R::Collect[Reverse, Add]] }
|
62
|
+
it { pipeline.(["yo!", "oy!"], {}).must_equal ["!yo+", "!oy+"] }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
######### scalar property
|
70
|
+
|
71
|
+
let (:title) {
|
72
|
+
dfn = R::Definition.new(:title)
|
73
|
+
|
74
|
+
R::Hash::Binding.new(dfn)
|
75
|
+
}
|
76
|
+
|
77
|
+
it "rendering scalar property" do
|
78
|
+
doc = {}
|
79
|
+
P[
|
80
|
+
R::Get,
|
81
|
+
R::StopOnSkipable,
|
82
|
+
R::AssignName,
|
83
|
+
R::WriteFragment
|
84
|
+
].(nil, {represented: Song.new("Lime Green"), binding: title, doc: doc}).must_equal "Lime Green"
|
85
|
+
|
86
|
+
doc.must_equal({"title"=>"Lime Green"})
|
87
|
+
end
|
88
|
+
|
89
|
+
it "parsing scalar property" do
|
90
|
+
P[
|
91
|
+
R::AssignName,
|
92
|
+
R::ReadFragment,
|
93
|
+
R::StopOnNotFound,
|
94
|
+
R::OverwriteOnNil,
|
95
|
+
# R::SkipParse,
|
96
|
+
R::Set,
|
97
|
+
].extend(P::Debug).(doc={"title"=>"Eruption"}, {represented: song=Song.new("Lime Green"), binding: title, doc: doc}).must_equal "Eruption"
|
98
|
+
song.title.must_equal "Eruption"
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
module ArtistRepresenter
|
104
|
+
include Representable::Hash
|
105
|
+
property :name
|
106
|
+
end
|
107
|
+
|
108
|
+
let (:artist) {
|
109
|
+
dfn = R::Definition.new(:artist, extend: ArtistRepresenter, class: Artist)
|
110
|
+
|
111
|
+
R::Hash::Binding.new(dfn)
|
112
|
+
}
|
113
|
+
|
114
|
+
let (:song_model) { Song.new("Lime Green", Artist.new("Diesel Boy")) }
|
115
|
+
|
116
|
+
it "rendering typed property" do
|
117
|
+
doc = {}
|
118
|
+
P[
|
119
|
+
R::Get,
|
120
|
+
R::StopOnSkipable,
|
121
|
+
R::StopOnNil,
|
122
|
+
R::SkipRender,
|
123
|
+
R::Decorate,
|
124
|
+
R::Serialize,
|
125
|
+
R::AssignName,
|
126
|
+
R::WriteFragment
|
127
|
+
].extend(P::Debug).(nil, {represented: song_model, binding: artist, doc: doc, user_options: {}}).must_equal({"name" => "Diesel Boy"})
|
128
|
+
|
129
|
+
doc.must_equal({"artist"=>{"name"=>"Diesel Boy"}})
|
130
|
+
end
|
131
|
+
|
132
|
+
it "parsing typed property" do
|
133
|
+
P[
|
134
|
+
R::AssignName,
|
135
|
+
R::ReadFragment,
|
136
|
+
R::StopOnNotFound,
|
137
|
+
R::OverwriteOnNil,
|
138
|
+
# R::SkipParse,
|
139
|
+
R::CreateObject,
|
140
|
+
R::Decorate,
|
141
|
+
R::Deserialize,
|
142
|
+
R::Set,
|
143
|
+
].extend(P::Debug).(doc={"artist"=>{"name"=>"Doobie Brothers"}}, {represented: song_model, binding: artist, doc: doc, user_options: {}}).must_equal model=Artist.new("Doobie Brothers")
|
144
|
+
song_model.artist.must_equal model
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
######### collection :ratings
|
149
|
+
|
150
|
+
let (:ratings) {
|
151
|
+
dfn = R::Definition.new(:ratings, collection: true)
|
152
|
+
|
153
|
+
R::Hash::Binding::Collection.new(dfn)
|
154
|
+
}
|
155
|
+
it "render scalar collection" do
|
156
|
+
doc = {}
|
157
|
+
P[
|
158
|
+
R::Get,
|
159
|
+
R::StopOnSkipable,
|
160
|
+
R::Collect[
|
161
|
+
R::SkipRender,
|
162
|
+
],
|
163
|
+
R::AssignName,
|
164
|
+
R::WriteFragment
|
165
|
+
].extend(P::Debug).(nil, {represented: Album.new([1,2,3]), binding: ratings, doc: doc}).must_equal([1,2,3])
|
166
|
+
|
167
|
+
doc.must_equal({"ratings"=>[1,2,3]})
|
168
|
+
end
|
169
|
+
|
170
|
+
######### collection :songs, extend: SongRepresenter
|
171
|
+
let (:artists) {
|
172
|
+
dfn = R::Definition.new(:artists, collection: true, extend: ArtistRepresenter, class: Artist)
|
173
|
+
|
174
|
+
R::Hash::Binding::Collection.new(dfn)
|
175
|
+
}
|
176
|
+
it "render typed collection" do
|
177
|
+
doc = {}
|
178
|
+
P[
|
179
|
+
R::Get,
|
180
|
+
R::StopOnSkipable,
|
181
|
+
R::Collect[
|
182
|
+
R::SkipRender,
|
183
|
+
R::Decorate,
|
184
|
+
R::Serialize,
|
185
|
+
],
|
186
|
+
R::AssignName,
|
187
|
+
R::WriteFragment
|
188
|
+
].extend(P::Debug).(nil, {represented: Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]), binding: artists, doc: doc, user_options: {}}).must_equal([{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}])
|
189
|
+
|
190
|
+
doc.must_equal({"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]})
|
191
|
+
end
|
192
|
+
|
193
|
+
let (:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]) }
|
194
|
+
|
195
|
+
it "parse typed collection" do
|
196
|
+
doc = {"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]}
|
197
|
+
P[
|
198
|
+
R::AssignName,
|
199
|
+
R::ReadFragment,
|
200
|
+
R::StopOnNotFound,
|
201
|
+
R::OverwriteOnNil,
|
202
|
+
# R::SkipParse,
|
203
|
+
R::Collect[
|
204
|
+
R::SkipRender,
|
205
|
+
R::CreateObject,
|
206
|
+
R::Decorate,
|
207
|
+
R::Deserialize,
|
208
|
+
],
|
209
|
+
R::Set,
|
210
|
+
].extend(P::Debug).(doc, {represented: album_model, binding: artists, doc: doc, user_options: {}}).must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
|
211
|
+
|
212
|
+
album_model.artists.must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
|
213
|
+
end
|
214
|
+
|
215
|
+
# TODO: test with arrays, too, not "only" Pipeline instances.
|
216
|
+
describe "#Insert Pipeline[], Function, replace: OldFunction" do
|
217
|
+
let (:pipeline) { P[R::Get, R::StopOnSkipable, R::StopOnNil] }
|
218
|
+
|
219
|
+
it "returns Pipeline instance when passing in Pipeline instance" do
|
220
|
+
P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_be_instance_of(R::Pipeline)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "replaces if exists" do
|
224
|
+
# pipeline.insert!(R::Default, replace: R::StopOnSkipable)
|
225
|
+
P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_equal P[R::Get, R::Default, R::StopOnNil]
|
226
|
+
pipeline.must_equal P[R::Get, R::StopOnSkipable, R::StopOnNil]
|
227
|
+
end
|
228
|
+
|
229
|
+
it "replaces Function instance" do
|
230
|
+
pipeline = P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
|
231
|
+
P::Insert.(pipeline, R::Default, replace: R::Prepare).must_equal P[R::Default, R::StopOnSkipable, R::StopOnNil]
|
232
|
+
pipeline.must_equal P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
|
233
|
+
end
|
234
|
+
|
235
|
+
it "does not replace when not existing" do
|
236
|
+
P::Insert.(pipeline, R::Default, replace: R::Prepare)
|
237
|
+
pipeline.must_equal P[R::Get, R::StopOnSkipable, R::StopOnNil]
|
238
|
+
end
|
239
|
+
|
240
|
+
it "applies on nested Collect" do
|
241
|
+
pipeline = P[R::Get, R::Collect[R::Get, R::StopOnSkipable], R::StopOnNil]
|
242
|
+
|
243
|
+
P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).extend(P::Debug).inspect.must_equal "Pipeline[Get, Collect[Get, Default], StopOnNil]"
|
244
|
+
pipeline.must_equal P[R::Get, R::Collect[R::Get, R::StopOnSkipable], R::StopOnNil]
|
245
|
+
|
246
|
+
|
247
|
+
P::Insert.(pipeline, R::Default, replace: R::StopOnNil).extend(P::Debug).inspect.must_equal "Pipeline[Get, Collect[Get, StopOnSkipable], Default]"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "Insert delete: true" do
|
252
|
+
let(:pipeline) { P[R::Get, R::Collect[R::Get, R::StopOnSkipable], R::StopOnNil] }
|
253
|
+
|
254
|
+
it do
|
255
|
+
P::Insert.(pipeline, R::Get, delete: true).extend(P::Debug).inspect.must_equal "Pipeline[Collect[Get, StopOnSkipable], StopOnNil]"
|
256
|
+
pipeline.extend(P::Debug).inspect.must_equal "Pipeline[Get, Collect[Get, StopOnSkipable], StopOnNil]"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PopulatorFindOrInstantiateTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:id, :title, :uid) do
|
5
|
+
def self.find_by(attributes={})
|
6
|
+
return new(1, "Resist Stan", "abcd") if attributes[:id]==1# we should return the same object here
|
7
|
+
new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Composer = Struct.new(:song)
|
12
|
+
Composer.class_eval do
|
13
|
+
def song=(v)
|
14
|
+
@song = v
|
15
|
+
"Absolute nonsense" # this tests that the populator always returns the correct object.
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :song
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "FindOrInstantiate with property" do
|
22
|
+
representer! do
|
23
|
+
property :song, populator: Representable::FindOrInstantiate, class: Song do
|
24
|
+
property :id
|
25
|
+
property :title
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let (:album) { Composer.new.extend(representer) }
|
30
|
+
|
31
|
+
it "finds by :id and creates new without :id" do
|
32
|
+
album.from_hash({"song"=>{"id" => 1, "title"=>"Resist Stance"}})
|
33
|
+
|
34
|
+
album.song.title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
|
35
|
+
album.song.id.must_equal 1
|
36
|
+
album.song.uid.must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
|
37
|
+
end
|
38
|
+
|
39
|
+
it "creates new without :id" do
|
40
|
+
album.from_hash({"song"=>{"title"=>"Lower"}})
|
41
|
+
|
42
|
+
album.song.title.must_equal "Lower"
|
43
|
+
album.song.id.must_equal nil
|
44
|
+
album.song.uid.must_equal nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "FindOrInstantiate with collection" do
|
49
|
+
representer! do
|
50
|
+
collection :songs, populator: Representable::FindOrInstantiate, class: Song do
|
51
|
+
property :id
|
52
|
+
property :title
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
let (:album) { Struct.new(:songs).new([]).extend(representer) }
|
57
|
+
|
58
|
+
it "finds by :id and creates new without :id" do
|
59
|
+
album.from_hash({"songs"=>[
|
60
|
+
{"id" => 1, "title"=>"Resist Stance"},
|
61
|
+
{"title"=>"Suffer"}
|
62
|
+
]})
|
63
|
+
|
64
|
+
album.songs[0].title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
|
65
|
+
album.songs[0].id.must_equal 1
|
66
|
+
album.songs[0].uid.must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
|
67
|
+
|
68
|
+
album.songs[1].title.must_equal "Suffer"
|
69
|
+
album.songs[1].id.must_equal nil
|
70
|
+
album.songs[1].uid.must_equal nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# TODO: test with existing collection
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/test/realistic_benchmark.rb
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'benchmark'
|
3
3
|
|
4
|
+
Kernel.class_eval do
|
5
|
+
# def respond_to_missing?
|
6
|
+
# raise
|
7
|
+
# end
|
8
|
+
# alias_method :orig_hash, :hash
|
9
|
+
# def hash
|
10
|
+
# puts "hash in #{self.class}"
|
11
|
+
# raise self.inspect if self.class == Symbol
|
12
|
+
# orig_hash
|
13
|
+
# end
|
14
|
+
end
|
15
|
+
Representable.deprecations = false
|
16
|
+
|
4
17
|
SONG_PROPERTIES = 50.times.collect do |i|
|
5
|
-
"
|
18
|
+
"song_property_#{i}"
|
6
19
|
end
|
7
20
|
|
8
21
|
|
@@ -12,37 +25,56 @@ module SongRepresenter
|
|
12
25
|
SONG_PROPERTIES.each { |p| property p }
|
13
26
|
end
|
14
27
|
|
15
|
-
class
|
28
|
+
class NestedProperty < Representable::Decorator
|
16
29
|
include Representable::JSON
|
17
30
|
|
18
31
|
SONG_PROPERTIES.each { |p| property p }
|
19
32
|
end
|
20
33
|
|
21
|
-
|
34
|
+
|
35
|
+
class SongDecorator < Representable::Decorator
|
36
|
+
include Representable::JSON
|
37
|
+
|
38
|
+
SONG_PROPERTIES.each { |p| property p, extend: NestedProperty }
|
39
|
+
end
|
40
|
+
|
41
|
+
class AlbumRepresenter < Representable::Decorator
|
22
42
|
include Representable::JSON
|
23
43
|
|
24
44
|
# collection :songs, extend: SongRepresenter
|
25
45
|
collection :songs, extend: SongDecorator
|
26
46
|
end
|
27
47
|
|
48
|
+
Song = Struct.new(*SONG_PROPERTIES.map(&:to_sym))
|
49
|
+
Album = Struct.new(:songs)
|
50
|
+
|
28
51
|
def random_song
|
29
|
-
|
30
|
-
OpenStruct.new(attrs)
|
52
|
+
Song.new(*SONG_PROPERTIES.collect { |p| Song.new(*SONG_PROPERTIES) })
|
31
53
|
end
|
32
54
|
|
33
55
|
times = []
|
34
56
|
|
35
57
|
3.times.each do
|
36
|
-
album =
|
58
|
+
album = Album.new(100.times.collect { random_song })
|
37
59
|
|
38
60
|
times << Benchmark.measure do
|
39
61
|
puts "================ next!"
|
40
|
-
|
62
|
+
AlbumRepresenter.new(album).to_json
|
41
63
|
end
|
42
64
|
end
|
43
65
|
|
44
66
|
puts times.join("")
|
45
67
|
|
68
|
+
album = Album.new(100.times.collect { random_song })
|
69
|
+
require 'ruby-prof'
|
70
|
+
RubyProf.start
|
71
|
+
AlbumRepresenter.new(album).to_hash
|
72
|
+
res = RubyProf.stop
|
73
|
+
printer = RubyProf::FlatPrinter.new(res)
|
74
|
+
printer.print(array = [])
|
75
|
+
|
76
|
+
array[0..60].each { |a| puts a }
|
77
|
+
|
46
78
|
# 100 songs, 100 attrs
|
47
79
|
# 0.050000 0.000000 0.050000 ( 0.093157)
|
48
80
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RenderNilTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title)
|
5
|
+
|
6
|
+
describe "render_nil: true" do
|
7
|
+
representer! do
|
8
|
+
property :title, render_nil: true
|
9
|
+
end
|
10
|
+
|
11
|
+
it { Song.new.extend(representer).to_hash.must_equal({"title"=>nil}) }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "with :extend it shouldn't extend nil" do
|
15
|
+
representer! do
|
16
|
+
property :title, render_nil: true, extend: Class
|
17
|
+
end
|
18
|
+
|
19
|
+
it { Song.new.extend(representer).to_hash.must_equal({"title"=>nil}) }
|
20
|
+
end
|
21
|
+
end
|