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,159 @@
1
+ require 'test_helper'
2
+
3
+ class InheritTest < MiniTest::Spec
4
+ module SongRepresenter # it's important to have a global module so we can test if stuff gets overridden in the original module.
5
+ include Representable::Hash
6
+ property :name, :as => :title do
7
+ property :string, :as => :str
8
+ end
9
+
10
+ property :track, :as => :no
11
+ end
12
+
13
+ let (:song) { Song.new(Struct.new(:string).new("Roxanne"), 1) }
14
+
15
+ describe ":inherit plain property" do
16
+ representer! do
17
+ include SongRepresenter
18
+
19
+ property :track, :inherit => true, :getter => lambda { |*| "n/a" }
20
+ end
21
+
22
+ it { SongRepresenter.prepare(song).to_hash.must_equal({"title"=>{"str"=>"Roxanne"}, "no"=>1}) }
23
+ it { representer.prepare(song).to_hash.must_equal({"title"=>{"str"=>"Roxanne"}, "no"=>"n/a"}) } # as: inherited.
24
+ end
25
+
26
+ describe ":inherit with empty inline representer" do
27
+ representer! do
28
+ include SongRepresenter
29
+
30
+ property :name, :inherit => true do # inherit as: title
31
+ # that doesn't make sense.
32
+ end
33
+ end
34
+
35
+ it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
36
+ # the block doesn't override the inline representer.
37
+ it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
38
+ end
39
+
40
+ describe ":inherit with overriding inline representer" do
41
+ representer! do
42
+ include SongRepresenter
43
+
44
+ puts "passing block"
45
+ property :name, :inherit => true do # inherit as: title
46
+ property :string, :as => :s
47
+ property :length
48
+ end
49
+ end
50
+
51
+ it { representer.prepare( Song.new(Struct.new(:string, :length).new("Believe It", 10), 1)).to_hash.must_equal({"title"=>{"s"=>"Believe It","length"=>10}, "no"=>1}) }
52
+ end
53
+
54
+ describe ":inherit with empty inline and options" do
55
+ representer! do
56
+ include SongRepresenter
57
+
58
+ property :name, inherit: true, as: :name do # inherit module, only.
59
+ # that doesn't make sense. but it should simply inherit the old nested properties.
60
+ end
61
+ end
62
+
63
+ it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
64
+ it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"name"=>{"str"=>"Believe It"}, "no"=>1}) }
65
+ end
66
+
67
+ describe ":inherit with inline without block but options" do
68
+ representer! do
69
+ include SongRepresenter
70
+
71
+ property :name, :inherit => true, :as => :name # FIXME: add :getter or something else dynamic since this is double-wrapped.
72
+ end
73
+
74
+ it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
75
+ it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"name"=>{"str"=>"Believe It"}, "no"=>1}) }
76
+ end
77
+
78
+
79
+
80
+ # no :inherit
81
+ describe "overwriting without :inherit" do
82
+ representer! do
83
+ include SongRepresenter
84
+
85
+ property :track, :representable => true
86
+ end
87
+
88
+ it "replaces inherited property" do
89
+ representer.representable_attrs.size.must_equal 2
90
+
91
+ definition = representer.representable_attrs.get(:track) # TODO: find a better way to assert Definition identity.
92
+ # definition.keys.size.must_equal 2
93
+ definition[:representable]. must_equal true
94
+ definition.name.must_equal "track" # was "no".
95
+ end
96
+ end
97
+
98
+
99
+ # decorator
100
+ describe ":inherit with decorator" do
101
+ representer!(:decorator => true) do
102
+ property :hit do
103
+ property :title, exec_context: :decorator
104
+
105
+ def title
106
+ "Cheap Transistor Radio"
107
+ end
108
+ end
109
+ end
110
+
111
+ let (:inheriting) {
112
+ class InheritingDecorator < representer
113
+ include Representable::Debug
114
+ property :hit, :inherit => true do
115
+ include Representable::Debug
116
+ property :length
117
+ end
118
+ self
119
+ end
120
+ }
121
+
122
+ it { representer.new(OpenStruct.new(hit: OpenStruct.new(title: "I WILL BE OVERRIDDEN", :length => "2:59"))).to_hash.must_equal(
123
+ {"hit"=>{"title"=>"Cheap Transistor Radio"}}) }
124
+
125
+ # inheriting decorator inherits inline representer class (InlineRepresenter#title).
126
+ # inheriting decorator adds :length.
127
+ it { inheriting.new(OpenStruct.new(:hit => OpenStruct.new(:title => "Hole In Your Soul", :length => "2:59"))).to_hash.must_equal(
128
+ {"hit"=>{"title"=>"Cheap Transistor Radio", "length"=>"2:59"}}) }
129
+ end
130
+
131
+
132
+ # :inherit when property doesn't exist, yet.
133
+ describe ":inherit without inheritable property" do
134
+ representer! do
135
+ property :name, :inherit => true
136
+ end
137
+
138
+ it { representer.prepare(Song.new("The Beginning")).to_hash.must_equal({"name"=>"The Beginning"})}
139
+ end
140
+ end
141
+
142
+
143
+ # class InheritancingTest < MiniTest::Spec
144
+ # class SongDecorator < Representable::Decorator
145
+ # include Representable::Hash
146
+ # property :album do
147
+ # # does have Hash.
148
+ # property :title
149
+ # end
150
+ # end
151
+
152
+ # class JsonSongDecorator < SongDecorator
153
+ # include Representable::XML
154
+ # end
155
+
156
+ # it do
157
+ # puts JsonSongDecorator.new(OpenStruct.new(:album => OpenStruct.new(:title => "Erotic Cakes", :tracks => nil))).to_xml
158
+ # end
159
+ # end
@@ -0,0 +1,272 @@
1
+ require 'test_helper'
2
+
3
+ class InlineTest < MiniTest::Spec
4
+ let (:song) { Song.new("Alive") }
5
+ let (:request) { representer.prepare(OpenStruct.new(:song => song)) }
6
+
7
+ {
8
+ :hash => [Representable::Hash, {"song"=>{"name"=>"Alive"}}, {"song"=>{"name"=>"You've Taken Everything"}}],
9
+ :json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"],
10
+ :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
11
+ :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
12
+ }.each do |format, cfg|
13
+ mod, output, input = cfg
14
+
15
+ describe "[#{format}] with :class" do
16
+ representer!(:module => mod) do
17
+ property :song, :class => Song do
18
+ property :name
19
+ end
20
+ end
21
+
22
+ let (:format) { format }
23
+
24
+ it { render(request).must_equal_document output }
25
+ it { parse(request, input).song.name.must_equal "You've Taken Everything"}
26
+ end
27
+ end
28
+
29
+ {
30
+ :hash => [Representable::Hash, {"songs"=>[{"name"=>"Alive"}]}, {"songs"=>[{"name"=>"You've Taken Everything"}]}],
31
+ :json => [Representable::JSON, "{\"songs\":[{\"name\":\"Alive\"}]}", "{\"songs\":[{\"name\":\"You've Taken Everything\"}]}"],
32
+ :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song></open_struct>", { :as => :song }],
33
+ :yaml => [Representable::YAML, "---\nsongs:\n- name: Alive\n", "---\nsongs:\n- name: You've Taken Everything\n"],
34
+ }.each do |format, cfg|
35
+ mod, output, input, collection_options = cfg
36
+ collection_options ||= {}
37
+
38
+ describe "[#{format}] collection with :class" do
39
+ let (:request) { representer.prepare(OpenStruct.new(:songs => [song])) }
40
+
41
+ representer!(:module => mod) do
42
+ collection :songs, collection_options.merge(:class => Song) do
43
+ property :name
44
+ end
45
+ end
46
+
47
+ let (:format) { format } # FIXME: why do we have to define this?
48
+
49
+ it { render(request).must_equal_document output }
50
+ it { parse(request, input).songs.first.name.must_equal "You've Taken Everything"}
51
+ end
52
+ end
53
+
54
+ describe "without :class" do
55
+ representer! do
56
+ property :song do
57
+ property :name
58
+ end
59
+ end
60
+
61
+ it { request.to_hash.must_equal({"song"=>{"name"=>"Alive"}}) }
62
+ end
63
+
64
+
65
+ for_formats(
66
+ :hash => [Representable::Hash, {}],
67
+ # :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
68
+ # :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
69
+ ) do |format, mod, input|
70
+
71
+ describe "parsing [#{format}] where nested property missing" do
72
+ representer!(:module => mod) do
73
+ property :song do
74
+ property :name
75
+ end
76
+ end
77
+
78
+ it "doesn't change represented object" do
79
+ request.send("from_#{format}", input).song.must_equal song
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ describe "inheriting from outer representer" do
86
+ let (:request) { Struct.new(:song, :requester).new(song, "Josephine") }
87
+
88
+ [false, true].each do |is_decorator| # test for module and decorator.
89
+ representer!(:decorator => is_decorator) do
90
+ property :requester
91
+
92
+ property :song, :class => Song do
93
+ property :name
94
+ end
95
+ end
96
+
97
+ let (:decorator) { representer.prepare(request) }
98
+
99
+ it { decorator.to_hash.must_equal({"requester"=>"Josephine", "song"=>{"name"=>"Alive"}}) }
100
+ it { decorator.from_hash({"song"=>{"name"=>"You've Taken Everything"}}).song.name.must_equal "You've Taken Everything"}
101
+ end
102
+ end
103
+
104
+ describe "object pollution" do
105
+ representer!(:decorator => true) do
106
+ property :song do
107
+ property :name
108
+ end
109
+ end
110
+
111
+ it "uses an inline decorator and doesn't alter represented" do
112
+ representer.prepare(Struct.new(:song).new(song)).to_hash
113
+ song.wont_be_kind_of Representable
114
+ end
115
+ end
116
+
117
+ # TODO: should be in extend:/decorator: test.
118
+ # FIXME: this tests :getter{represented}+:extend - represented gets extended twice and the inline decorator overrides original config.
119
+ # for_formats(
120
+ # :hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
121
+ # # :xml => [Representable::XML, "<open_struct></open_struct>"],
122
+ # #:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
123
+ # ) do |format, mod, output, input|
124
+
125
+ # module ArtistRepresenter
126
+ # include Representable::JSON
127
+ # property :label
128
+ # end
129
+
130
+ # describe ":getter with inline representer" do
131
+ # let (:format) { format }
132
+
133
+ # representer!(:module => mod) do
134
+ # self.representation_wrap = :album
135
+
136
+ # property :artist, :getter => lambda { |args| represented }, :extend => ArtistRepresenter
137
+ # end
138
+
139
+ # let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
140
+
141
+ # it "renders nested Album-properties in separate section" do
142
+ # render(album).must_equal_document output
143
+ # end
144
+ # end
145
+ # end
146
+
147
+
148
+ for_formats({
149
+ :hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
150
+ # :xml => [Representable::XML, "<open_struct></open_struct>"],
151
+ #:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
152
+ }) do |format, mod, output, input|
153
+
154
+ class ArtistDecorator < Representable::Decorator
155
+ include Representable::JSON
156
+ property :label
157
+ end
158
+
159
+ describe ":getter with :decorator" do
160
+ let (:format) { format }
161
+
162
+ representer!(:module => mod) do
163
+ self.representation_wrap = "album"
164
+
165
+ property :artist, :getter => lambda { |args| represented }, :decorator => ArtistDecorator
166
+ end
167
+
168
+ let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
169
+
170
+ it "renders nested Album-properties in separate section" do
171
+ render(album).must_equal_document output
172
+ end
173
+ end
174
+ end
175
+
176
+
177
+ # test helper methods within inline representer
178
+ for_formats({
179
+ :hash => [Representable::Hash, {"song"=>{"name"=>"ALIVE"}}],
180
+ :xml => [Representable::XML, "<request>\n <song>\n <name>ALIVE</name>\n </song>\n</request>"],
181
+ :yaml => [Representable::YAML, "---\nsong:\n name: ALIVE\n"],
182
+ }) do |format, mod, output|
183
+
184
+ describe "helper method within inline representer [#{format}]" do
185
+ let (:format) { format }
186
+
187
+ representer!(:module => mod, :decorator => true) do
188
+ self.representation_wrap = :request if format == :xml
189
+
190
+ property :requester
191
+ property :song do
192
+ property :name, :exec_context => :decorator
193
+
194
+ define_method :name do
195
+ represented.name.upcase
196
+ end
197
+
198
+ self.representation_wrap = :song if format == :xml
199
+ end
200
+ end
201
+
202
+ let (:request) { representer.prepare(OpenStruct.new(:song => Song.new("Alive"))) }
203
+
204
+ it do
205
+ render(request).must_equal_document output
206
+ end
207
+ end
208
+ end
209
+
210
+
211
+ describe "include module in inline representers" do
212
+ representer! do
213
+ extension = Module.new do
214
+ include Representable::Hash
215
+ property :title
216
+ end
217
+
218
+ property :song do
219
+ include extension
220
+ property :artist
221
+ end
222
+ end
223
+
224
+ it do OpenStruct.new(:song => OpenStruct.new(:title => "The Fever And The Sound", :artist => "Strung Out")).extend(representer).
225
+ to_hash.
226
+ must_equal({"song"=>{"artist"=>"Strung Out", "title"=>"The Fever And The Sound"}})
227
+ end
228
+ end
229
+
230
+
231
+ # define method in inline representer
232
+ describe "define method in inline representer" do
233
+ Mod = Module.new do
234
+ include Representable::Hash
235
+
236
+ def song
237
+ "Object.new"
238
+ end
239
+
240
+ property :song do
241
+ property :duration
242
+
243
+ def duration
244
+ "6:53"
245
+ end
246
+ end
247
+ end
248
+
249
+ it { Object.new.extend(Mod).to_hash.must_equal("song"=>{"duration"=>"6:53"}) }
250
+ end
251
+
252
+ # define method inline with Decorator
253
+ describe "define method inline with Decorator" do
254
+ dec = Class.new(Representable::Decorator) do
255
+ include Representable::Hash
256
+
257
+ def song
258
+ "Object.new"
259
+ end
260
+
261
+ property :song, :exec_context => :decorator do
262
+ property :duration, :exec_context => :decorator
263
+
264
+ def duration
265
+ "6:53"
266
+ end
267
+ end
268
+ end
269
+
270
+ it { dec.new(Object.new).to_hash.must_equal("song"=>{"duration"=>"6:53"}) }
271
+ end
272
+ end
@@ -0,0 +1,266 @@
1
+ require 'test_helper'
2
+
3
+ class InstanceTest < BaseTest
4
+ Song = Struct.new(:id, :title)
5
+ Song.class_eval do
6
+ def self.find(id)
7
+ new(id, "Invincible")
8
+ end
9
+ end
10
+
11
+ describe "lambda { fragment } (new way of class: lambda { nil })" do
12
+ representer! do
13
+ property :title, :instance => lambda { |fragment, args| fragment }
14
+ end
15
+
16
+ it "skips creating new instance" do
17
+ object = Object.new
18
+ object.instance_eval do
19
+ def from_hash(hash, *args)
20
+ hash
21
+ end
22
+ end
23
+
24
+ song = OpenStruct.new.extend(representer).from_hash(hash = {"title" => object})
25
+ song.title.must_equal object
26
+ end
27
+ end
28
+
29
+
30
+ # TODO: use *args in from_hash.
31
+ # DISCUSS: do we need parse_strategy?
32
+ describe "property with :instance" do
33
+ representer!(:inject => :song_representer) do
34
+ property :song,
35
+ :instance => lambda { |fragment, *args| fragment["id"] == song.id ? song : Song.find(fragment["id"]) },
36
+ :extend => song_representer
37
+ end
38
+
39
+ it( "xxx") { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
40
+ from_hash("song" => {"id" => 1}).song.must_equal Song.new(1, "The Answer Is Still No") }
41
+
42
+ it { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
43
+ from_hash("song" => {"id" => 2}).song.must_equal Song.new(2, "Invincible") }
44
+ end
45
+
46
+
47
+ describe "collection with :instance" do
48
+ representer!(:inject => :song_representer) do
49
+ collection :songs,
50
+ :instance => lambda { |fragment, i, *args|
51
+ fragment["id"] == songs[i].id ? songs[i] : Song.find(fragment["id"])
52
+ }, # 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
+
54
+ :extend => song_representer
55
+ end
56
+
57
+ it {
58
+ album= Struct.new(:songs).new(songs = [
59
+ Song.new(1, "The Answer Is Still No"),
60
+ Song.new(2, "")])
61
+
62
+ album.
63
+ extend(representer).
64
+ from_hash("songs" => [{"id" => 2},{"id" => 2, "title"=>"The Answer Is Still No"}]).songs.must_equal [
65
+ Song.new(2, "Invincible"), Song.new(2, "The Answer Is Still No")]
66
+ }
67
+ end
68
+
69
+ describe "property with lambda receiving fragment and args" do
70
+ representer!(:inject => :song_representer) do
71
+ property :song, :instance => lambda { |fragment, args| Struct.new(:args, :id).new([fragment, args]) }, :extend => song_representer
72
+ end
73
+
74
+ it { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
75
+ from_hash({"song" => {"id" => 1}}, {:volume => 1}).song.args.must_equal([{"id"=>1}, {:volume=>1}]) }
76
+ end
77
+
78
+ # TODO: raise and test instance:{nil}
79
+ # describe "property with instance: { nil }" do # TODO: introduce :representable option?
80
+ # representer!(:inject => :song_representer) do
81
+ # property :song, :instance => lambda { |*| nil }, :extend => song_representer
82
+ # end
83
+
84
+ # let (:hit) { hit = OpenStruct.new(:song => song).extend(representer) }
85
+
86
+ # it "calls #to_hash on song instance, nothing else" do
87
+ # hit.to_hash.must_equal("song"=>{"title"=>"Resist Stance"})
88
+ # end
89
+
90
+ # it "calls #from_hash on the existing song instance, nothing else" do
91
+ # song_id = hit.song.object_id
92
+ # hit.from_hash("song"=>{"title"=>"Suffer"})
93
+ # hit.song.title.must_equal "Suffer"
94
+ # hit.song.object_id.must_equal song_id
95
+ # end
96
+ # end
97
+
98
+ # lambda { |fragment, i, Context(binding: <..>, args: [..])| }
99
+
100
+ describe "sync" do
101
+ representer!(:inject => :song_representer) do
102
+ collection :songs,
103
+ :instance => lambda { |fragment, i, *args|
104
+ songs[i]
105
+ },
106
+ :extend => song_representer,
107
+ # :parse_strategy => :sync
108
+ :setter => lambda { |*| }
109
+ end
110
+
111
+ it {
112
+ album= Struct.new(:songs).new(songs = [
113
+ Song.new(1, "The Answer Is Still No"),
114
+ Song.new(2, "Invncble")])
115
+
116
+ album.
117
+ extend(representer).
118
+ from_hash("songs" => [{"title" => "The Answer Is Still No"}, {"title" => "Invincible"}])
119
+
120
+ album.songs.must_equal [
121
+ Song.new(1, "The Answer Is Still No"),
122
+ Song.new(2, "Invincible")]
123
+
124
+ songs.object_id.must_equal album.songs.object_id
125
+ songs[0].object_id.must_equal album.songs[0].object_id
126
+ songs[1].object_id.must_equal album.songs[1].object_id
127
+ }
128
+ end
129
+
130
+ describe "update existing elements, only" do
131
+ representer!(:inject => :song_representer) do
132
+ collection :songs,
133
+ :instance => lambda { |fragment, i, *args|
134
+
135
+ #fragment["id"] == songs[i].id ? songs[i] : Song.find(fragment["id"])
136
+ songs.find { |s| s.id == fragment["id"] }
137
+ }, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
138
+
139
+ :extend => song_representer,
140
+ # :parse_strategy => :sync
141
+ :setter => lambda { |*| }
142
+ end
143
+
144
+ it("hooray") {
145
+ album= Struct.new(:songs).new(songs = [
146
+ Song.new(1, "The Answer Is Still No"),
147
+ Song.new(2, "Invncble")])
148
+
149
+ album.
150
+ extend(representer).
151
+ from_hash("songs" => [{"id" => 2, "title" => "Invincible"}]).
152
+ songs.must_equal [
153
+ Song.new(1, "The Answer Is Still No"),
154
+ Song.new(2, "Invincible")]
155
+ # TODO: check elements object_id!
156
+
157
+ songs.object_id.must_equal album.songs.object_id
158
+ songs[0].object_id.must_equal album.songs[0].object_id
159
+ songs[1].object_id.must_equal album.songs[1].object_id
160
+ }
161
+ end
162
+
163
+
164
+ describe "add incoming elements, only" do
165
+ representer!(:inject => :song_representer) do
166
+ collection :songs,
167
+ :instance => lambda { |fragment, i, *args|
168
+ songs << song=Song.new(2)
169
+ song
170
+ }, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
171
+
172
+ :extend => song_representer,
173
+ # :parse_strategy => :sync
174
+ :setter => lambda { |*| }
175
+ end
176
+
177
+ it {
178
+ album= Struct.new(:songs).new(songs = [
179
+ Song.new(1, "The Answer Is Still No")])
180
+
181
+ album.
182
+ extend(representer).
183
+ from_hash("songs" => [{"title" => "Invincible"}]).
184
+ songs.must_equal [
185
+ Song.new(1, "The Answer Is Still No"),
186
+ Song.new(2, "Invincible")]
187
+
188
+ songs.object_id.must_equal album.songs.object_id
189
+ songs[0].object_id.must_equal album.songs[0].object_id
190
+ }
191
+ end
192
+
193
+
194
+ # not sure if this must be a library strategy
195
+ describe "replace existing element" do
196
+ representer!(:inject => :song_representer) do
197
+ collection :songs,
198
+ :instance => lambda { |fragment, i, *args|
199
+ id = fragment.delete("replace_id")
200
+ replaced = songs.find { |s| s.id == id }
201
+ songs[songs.index(replaced)] = song=Song.new(3)
202
+ song
203
+ }, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
204
+
205
+ :extend => song_representer,
206
+ # :parse_strategy => :sync
207
+ :setter => lambda { |*| }
208
+ end
209
+
210
+ it {
211
+ album= Struct.new(:songs).new(songs = [
212
+ Song.new(1, "The Answer Is Still No"),
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 [
219
+ Song.new(1, "The Answer Is Still No"),
220
+ Song.new(3, "Soulmate")]
221
+
222
+ songs.object_id.must_equal album.songs.object_id
223
+ songs[0].object_id.must_equal album.songs[0].object_id
224
+ }
225
+ end
226
+
227
+
228
+ describe "replace collection" do
229
+ representer!(:inject => :song_representer) do
230
+ collection :songs,
231
+ :extend => song_representer, :class => Song
232
+ end
233
+
234
+ it {
235
+ album= Struct.new(:songs).new(songs = [
236
+ Song.new(1, "The Answer Is Still No")])
237
+
238
+ album.
239
+ extend(representer).
240
+ from_hash("songs" => [{"title" => "Invincible"}]).
241
+ songs.must_equal [
242
+ Song.new(nil, "Invincible")]
243
+
244
+ songs.object_id.wont_equal album.songs.object_id
245
+ }
246
+ end
247
+
248
+
249
+ describe "new syntax for instance: true" do
250
+ representer!(:inject => :song_representer) do
251
+ property :song, :pass_options => true,
252
+ :extend => song_representer, :instance => lambda { |fragment, args| args.binding.get(represented: args.represented) }
253
+ end
254
+
255
+ it "uses Binding#get instead of creating an instance, but deprecates" do
256
+ album= Struct.new(:song).new(song = Song.new(1, "The Answer Is Still No"))
257
+
258
+ album.
259
+ extend(representer).
260
+ from_hash("song" => {"title" => "Invincible"}).
261
+ song.must_equal Song.new(1, "Invincible")
262
+
263
+ album.song.object_id.must_equal song.object_id
264
+ end
265
+ end
266
+ end