representable 2.4.0.rc3 → 2.4.0.rc4

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.
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