representable 2.4.0.rc3 → 2.4.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +7 -2
  3. data/Rakefile +6 -0
  4. data/lib/representable.rb +21 -17
  5. data/lib/representable/binding.rb +3 -2
  6. data/lib/representable/definition.rb +1 -1
  7. data/lib/representable/deprecations.rb +31 -5
  8. data/lib/representable/deserializer.rb +24 -34
  9. data/lib/representable/hash/collection.rb +9 -2
  10. data/lib/representable/hash_methods.rb +2 -2
  11. data/lib/representable/parse_strategies.rb +6 -7
  12. data/lib/representable/pipeline.rb +12 -1
  13. data/lib/representable/pipeline_factories.rb +9 -2
  14. data/lib/representable/serializer.rb +3 -3
  15. data/lib/representable/version.rb +1 -1
  16. data/test-with-deprecations/as_test.rb +65 -0
  17. data/test-with-deprecations/benchmarking.rb +83 -0
  18. data/test-with-deprecations/binding_test.rb +46 -0
  19. data/test-with-deprecations/blaaaaaaaa_test.rb +69 -0
  20. data/test-with-deprecations/cached_test.rb +147 -0
  21. data/test-with-deprecations/class_test.rb +119 -0
  22. data/test-with-deprecations/coercion_test.rb +52 -0
  23. data/test-with-deprecations/config/inherit_test.rb +135 -0
  24. data/test-with-deprecations/config_test.rb +122 -0
  25. data/test-with-deprecations/decorator_scope_test.rb +28 -0
  26. data/test-with-deprecations/decorator_test.rb +96 -0
  27. data/test-with-deprecations/default_test.rb +34 -0
  28. data/test-with-deprecations/defaults_options_test.rb +93 -0
  29. data/test-with-deprecations/definition_test.rb +264 -0
  30. data/test-with-deprecations/example.rb +310 -0
  31. data/test-with-deprecations/examples/object.rb +31 -0
  32. data/test-with-deprecations/exec_context_test.rb +93 -0
  33. data/test-with-deprecations/features_test.rb +70 -0
  34. data/test-with-deprecations/filter_test.rb +57 -0
  35. data/test-with-deprecations/for_collection_test.rb +74 -0
  36. data/test-with-deprecations/generic_test.rb +116 -0
  37. data/test-with-deprecations/getter_setter_test.rb +21 -0
  38. data/test-with-deprecations/hash_bindings_test.rb +87 -0
  39. data/test-with-deprecations/hash_test.rb +160 -0
  40. data/test-with-deprecations/heritage_test.rb +62 -0
  41. data/test-with-deprecations/if_test.rb +79 -0
  42. data/test-with-deprecations/include_exclude_test.rb +88 -0
  43. data/test-with-deprecations/inherit_test.rb +159 -0
  44. data/test-with-deprecations/inline_test.rb +272 -0
  45. data/test-with-deprecations/instance_test.rb +266 -0
  46. data/test-with-deprecations/is_representable_test.rb +77 -0
  47. data/test-with-deprecations/json_test.rb +355 -0
  48. data/test-with-deprecations/lonely_test.rb +239 -0
  49. data/test-with-deprecations/mongoid_test.rb +31 -0
  50. data/test-with-deprecations/nested_test.rb +115 -0
  51. data/test-with-deprecations/object_test.rb +60 -0
  52. data/{test/---deserialize-pipeline_test.rb → test-with-deprecations/parse_pipeline_test.rb} +29 -2
  53. data/test-with-deprecations/parse_strategy_test.rb +279 -0
  54. data/{test → test-with-deprecations}/pass_options_test.rb +0 -0
  55. data/test-with-deprecations/pipeline_test.rb +277 -0
  56. data/test-with-deprecations/populator_test.rb +105 -0
  57. data/test-with-deprecations/prepare_test.rb +67 -0
  58. data/test-with-deprecations/private_options_test.rb +18 -0
  59. data/test-with-deprecations/reader_writer_test.rb +19 -0
  60. data/test-with-deprecations/realistic_benchmark.rb +115 -0
  61. data/test-with-deprecations/render_nil_test.rb +21 -0
  62. data/test-with-deprecations/represent_test.rb +88 -0
  63. data/test-with-deprecations/representable_test.rb +511 -0
  64. data/test-with-deprecations/schema_test.rb +148 -0
  65. data/test-with-deprecations/serialize_deserialize_test.rb +33 -0
  66. data/test-with-deprecations/skip_test.rb +81 -0
  67. data/test-with-deprecations/stringify_hash_test.rb +41 -0
  68. data/test-with-deprecations/test_helper.rb +135 -0
  69. data/test-with-deprecations/test_helper_test.rb +25 -0
  70. data/test-with-deprecations/uncategorized_test.rb +67 -0
  71. data/test-with-deprecations/user_options_test.rb +15 -0
  72. data/test-with-deprecations/wrap_test.rb +152 -0
  73. data/test-with-deprecations/xml_bindings_test.rb +62 -0
  74. data/test-with-deprecations/xml_test.rb +503 -0
  75. data/test-with-deprecations/yaml_test.rb +162 -0
  76. data/test/as_test.rb +3 -3
  77. data/test/cached_test.rb +2 -2
  78. data/test/class_test.rb +5 -5
  79. data/test/exec_context_test.rb +2 -2
  80. data/test/filter_test.rb +1 -1
  81. data/test/getter_setter_test.rb +4 -4
  82. data/test/if_test.rb +2 -2
  83. data/test/include_exclude_test.rb +88 -0
  84. data/test/instance_test.rb +15 -15
  85. data/test/lonely_test.rb +18 -2
  86. data/test/object_test.rb +4 -4
  87. data/test/parse_pipeline_test.rb +64 -0
  88. data/test/parse_strategy_test.rb +3 -3
  89. data/test/pipeline_test.rb +8 -12
  90. data/test/prepare_test.rb +2 -3
  91. data/test/reader_writer_test.rb +3 -3
  92. data/test/representable_test.rb +12 -48
  93. data/test/serialize_deserialize_test.rb +9 -9
  94. data/test/skip_test.rb +11 -11
  95. data/test/test_helper.rb +2 -0
  96. data/test/uncategorized_test.rb +10 -10
  97. data/test/user_options_test.rb +15 -0
  98. data/test/wrap_test.rb +1 -1
  99. metadata +65 -4
@@ -0,0 +1,277 @@
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::GetValue,
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::SetValue,
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::GetValue,
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, 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::AssignFragment,
140
+ R::CreateObject::Class,
141
+ R::Decorate,
142
+ R::Deserialize,
143
+ R::SetValue,
144
+ ].extend(P::Debug).(doc={"artist"=>{"name"=>"Doobie Brothers"}}, {represented: song_model, binding: artist, doc: doc, options: {}}).must_equal model=Artist.new("Doobie Brothers")
145
+ song_model.artist.must_equal model
146
+ end
147
+
148
+
149
+ ######### collection :ratings
150
+
151
+ let (:ratings) {
152
+ dfn = R::Definition.new(:ratings, collection: true)
153
+
154
+ R::Hash::Binding::Collection.new(dfn)
155
+ }
156
+ it "render scalar collection" do
157
+ doc = {}
158
+ P[
159
+ R::GetValue,
160
+ R::StopOnSkipable,
161
+ R::Collect[
162
+ R::SkipRender,
163
+ ],
164
+ R::AssignName,
165
+ R::WriteFragment
166
+ ].extend(P::Debug).(nil, {represented: Album.new([1,2,3]), binding: ratings, doc: doc}).must_equal([1,2,3])
167
+
168
+ doc.must_equal({"ratings"=>[1,2,3]})
169
+ end
170
+
171
+ ######### collection :songs, extend: SongRepresenter
172
+ let (:artists) {
173
+ dfn = R::Definition.new(:artists, collection: true, extend: ArtistRepresenter, class: Artist)
174
+
175
+ R::Hash::Binding::Collection.new(dfn)
176
+ }
177
+ it "render typed collection" do
178
+ doc = {}
179
+ P[
180
+ R::GetValue,
181
+ R::StopOnSkipable,
182
+ R::Collect[
183
+ R::SkipRender,
184
+ R::Decorate,
185
+ R::Serialize,
186
+ ],
187
+ R::AssignName,
188
+ R::WriteFragment
189
+ ].extend(P::Debug).(nil, {represented: Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]), binding: artists, doc: doc, options: {}}).must_equal([{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}])
190
+
191
+ doc.must_equal({"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]})
192
+ end
193
+
194
+ let (:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]) }
195
+
196
+ it "parse typed collection" do
197
+ doc = {"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]}
198
+ P[
199
+ R::AssignName,
200
+ R::ReadFragment,
201
+ R::StopOnNotFound,
202
+ R::OverwriteOnNil,
203
+ # R::SkipParse,
204
+ R::Collect[
205
+ R::AssignFragment,
206
+ R::SkipRender,
207
+ R::CreateObject::Class,
208
+ R::Decorate,
209
+ R::Deserialize,
210
+ ],
211
+ R::SetValue,
212
+ ].extend(P::Debug).(doc, {represented: album_model, binding: artists, doc: doc, options: {}}).must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
213
+
214
+ album_model.artists.must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
215
+ end
216
+
217
+ # TODO: test with arrays, too, not "only" Pipeline instances.
218
+ describe "#Insert Pipeline[], Function, replace: OldFunction" do
219
+ let (:pipeline) { P[R::GetValue, R::StopOnSkipable, R::StopOnNil] }
220
+
221
+ it "returns Pipeline instance when passing in Pipeline instance" do
222
+ P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_be_instance_of(R::Pipeline)
223
+ end
224
+
225
+ it "replaces if exists" do
226
+ # pipeline.insert!(R::Default, replace: R::StopOnSkipable)
227
+ P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_equal P[R::GetValue, R::Default, R::StopOnNil]
228
+ pipeline.must_equal P[R::GetValue, R::StopOnSkipable, R::StopOnNil]
229
+ end
230
+
231
+ it "replaces Function instance" do
232
+ pipeline = P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
233
+ P::Insert.(pipeline, R::Default, replace: R::Prepare).must_equal P[R::Default, R::StopOnSkipable, R::StopOnNil]
234
+ pipeline.must_equal P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
235
+ end
236
+
237
+ it "does not replace when not existing" do
238
+ P::Insert.(pipeline, R::Default, replace: R::Prepare)
239
+ pipeline.must_equal P[R::GetValue, R::StopOnSkipable, R::StopOnNil]
240
+ end
241
+
242
+ it "applies on nested Collect" do
243
+ pipeline = P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
244
+
245
+ P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
246
+ pipeline.must_equal P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
247
+
248
+
249
+ P::Insert.(pipeline, R::Default, replace: R::StopOnNil).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], Default]"
250
+ end
251
+
252
+ it "applies on nested Collect with Function::CreateObject" do
253
+ pipeline = P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
254
+
255
+ P::Insert.(pipeline, R::Default, replace: R::CreateObject).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
256
+ pipeline.must_equal P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
257
+ end
258
+ end
259
+
260
+ describe "Insert.(delete: true)" do
261
+ let(:pipeline) { P[R::GetValue, R::StopOnNil] }
262
+
263
+ it do
264
+ P::Insert.(pipeline, R::GetValue, delete: true).extend(P::Debug).inspect.must_equal "Pipeline[StopOnNil]"
265
+ pipeline.extend(P::Debug).inspect.must_equal "Pipeline[GetValue, StopOnNil]"
266
+ end
267
+ end
268
+
269
+ describe "Insert.(delete: true) with Collect" do
270
+ let(:pipeline) { P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil] }
271
+
272
+ it do
273
+ P::Insert.(pipeline, R::GetValue, delete: true).extend(P::Debug).inspect.must_equal "Pipeline[Collect[StopOnSkipable], StopOnNil]"
274
+ pipeline.extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], StopOnNil]"
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,105 @@
1
+ require "test_helper"
2
+
3
+ class PopulatorTest < Minitest::Spec
4
+ Song = Struct.new(:id)
5
+ Artist = Struct.new(:name)
6
+ Album = Struct.new(:songs, :artist)
7
+
8
+ describe "populator: ->{}" do
9
+ representer! do
10
+ collection :songs, populator: ->(input, options) { options[:represented].songs << song = Song.new; song } do
11
+ property :id
12
+ end
13
+
14
+ property :artist, populator: ->(input, options) { options[:represented].artist = Artist.new } do
15
+ property :name
16
+ end
17
+ end
18
+
19
+ let (:album) { Album.new([]) }
20
+
21
+ it do
22
+ album.extend(representer).from_hash("songs"=>[{"id"=>1}, {"id"=>2}], "artist"=>{"name"=>"Waste"})
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
+ end
25
+ end
26
+
27
+ describe "populator: ->{}, " do
28
+
29
+ end
30
+ end
31
+
32
+ class PopulatorFindOrInstantiateTest < Minitest::Spec
33
+ 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
36
+ new
37
+ end
38
+ end
39
+
40
+ Composer = Struct.new(:song)
41
+ Composer.class_eval do
42
+ def song=(v)
43
+ @song = v
44
+ "Absolute nonsense" # this tests that the populator always returns the correct object.
45
+ end
46
+
47
+ attr_reader :song
48
+ end
49
+
50
+ describe "FindOrInstantiate with property" do
51
+ representer! do
52
+ property :song, populator: Representable::FindOrInstantiate, class: Song do
53
+ property :id
54
+ property :title
55
+ end
56
+ end
57
+
58
+ let (:album) { Composer.new.extend(representer).extend(Representable::Debug) }
59
+
60
+ it "finds by :id and creates new without :id" do
61
+ album.from_hash({"song"=>{"id" => 1, "title"=>"Resist Stance"}})
62
+
63
+ album.song.title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
64
+ album.song.id.must_equal 1
65
+ album.song.uid.must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
66
+ end
67
+
68
+ it "creates new without :id" do
69
+ album.from_hash({"song"=>{"title"=>"Lower"}})
70
+
71
+ album.song.title.must_equal "Lower"
72
+ album.song.id.must_equal nil
73
+ album.song.uid.must_equal nil
74
+ end
75
+ end
76
+
77
+ describe "FindOrInstantiate with collection" do
78
+ representer! do
79
+ collection :songs, populator: Representable::FindOrInstantiate, class: Song do
80
+ property :id
81
+ property :title
82
+ end
83
+ end
84
+
85
+ let (:album) { Struct.new(:songs).new([]).extend(representer) }
86
+
87
+ it "finds by :id and creates new without :id" do
88
+ album.from_hash({"songs"=>[
89
+ {"id" => 1, "title"=>"Resist Stance"},
90
+ {"title"=>"Suffer"}
91
+ ]})
92
+
93
+ album.songs[0].title.must_equal "Resist Stance" # note how title is updated from "Resist Stan"
94
+ album.songs[0].id.must_equal 1
95
+ album.songs[0].uid.must_equal "abcd" # not changed via populator, indicating this is a formerly "persisted" object.
96
+
97
+ album.songs[1].title.must_equal "Suffer"
98
+ album.songs[1].id.must_equal nil
99
+ album.songs[1].uid.must_equal nil
100
+ end
101
+
102
+ # TODO: test with existing collection
103
+ end
104
+
105
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class PrepareTest < BaseTest
4
+ class PreparerClass
5
+ def initialize(object)
6
+ @object = object
7
+ end
8
+
9
+ def ==(b)
10
+ return unless b.instance_of?(PreparerClass)
11
+
12
+ object == b.object
13
+ end
14
+
15
+ attr_reader :object
16
+ end
17
+
18
+ describe "#to_hash" do # TODO: introduce :representable option?
19
+ representer! do
20
+ property :song,
21
+ :prepare => lambda { |obj, args| args.binding[:arbitrary].new(obj) },
22
+ :arbitrary => PreparerClass,
23
+ :extend => true,
24
+ :pass_options => true,
25
+ :representable => false # don't call #to_hash.
26
+ end
27
+
28
+ let (:hit) { Struct.new(:song).new(song).extend(representer) }
29
+
30
+ it "calls prepare:, nothing else" do
31
+ # render(hit).must_equal_document(output)
32
+ hit.to_hash.must_equal({"song" => PreparerClass.new(song)})
33
+ end
34
+
35
+
36
+ # it "calls #from_hash on the existing song instance, nothing else" do
37
+ # song_id = hit.song.object_id
38
+
39
+ # parse(hit, input)
40
+
41
+ # hit.song.title.must_equal "Suffer"
42
+ # hit.song.object_id.must_equal song_id
43
+ # end
44
+ end
45
+
46
+
47
+ describe "#from_hash" do
48
+ representer! do
49
+ property :song,
50
+ :prepare => lambda { |obj, args| args.binding[:arbitrary].new(obj) },
51
+ :arbitrary => PreparerClass,
52
+ #:extend => true, # TODO: typed: true would be better.
53
+ :instance => String.new, # pass_fragment
54
+ :pass_options => true,
55
+ :representable => false # don't call #to_hash.
56
+ end
57
+
58
+ let (:hit) { Struct.new(:song).new.extend(representer) }
59
+
60
+ it "calls prepare:, nothing else" do
61
+ # render(hit).must_equal_document(output)
62
+ hit.from_hash("song" => {})
63
+
64
+ hit.song.must_equal(PreparerClass.new(String.new))
65
+ end
66
+ end
67
+ end