representable 1.8.5 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGES.md +36 -0
  4. data/Gemfile +3 -3
  5. data/README.md +62 -2
  6. data/Rakefile +1 -1
  7. data/lib/representable.rb +32 -109
  8. data/lib/representable/TODO.getting_serious +7 -1
  9. data/lib/representable/autoload.rb +10 -0
  10. data/lib/representable/binding.rb +20 -16
  11. data/lib/representable/bindings/xml_bindings.rb +0 -1
  12. data/lib/representable/coercion.rb +23 -31
  13. data/lib/representable/config.rb +45 -46
  14. data/lib/representable/declarative.rb +78 -0
  15. data/lib/representable/decorator.rb +39 -10
  16. data/lib/representable/definition.rb +40 -33
  17. data/lib/representable/deserializer.rb +2 -0
  18. data/lib/representable/for_collection.rb +25 -0
  19. data/lib/representable/hash.rb +4 -8
  20. data/lib/representable/hash/collection.rb +2 -9
  21. data/lib/representable/hash_methods.rb +0 -7
  22. data/lib/representable/inheritable.rb +50 -0
  23. data/lib/representable/json.rb +3 -9
  24. data/lib/representable/json/collection.rb +1 -3
  25. data/lib/representable/json/hash.rb +4 -9
  26. data/lib/representable/mapper.rb +8 -5
  27. data/lib/representable/parse_strategies.rb +1 -0
  28. data/lib/representable/pipeline.rb +14 -0
  29. data/lib/representable/represent.rb +6 -0
  30. data/lib/representable/version.rb +1 -1
  31. data/lib/representable/xml.rb +3 -18
  32. data/lib/representable/xml/collection.rb +2 -4
  33. data/lib/representable/xml/hash.rb +2 -10
  34. data/lib/representable/yaml.rb +1 -20
  35. data/representable.gemspec +2 -2
  36. data/test/class_test.rb +5 -10
  37. data/test/coercion_test.rb +31 -92
  38. data/test/config/inherit_test.rb +128 -0
  39. data/test/config_test.rb +114 -80
  40. data/test/definition_test.rb +107 -64
  41. data/test/features_test.rb +41 -0
  42. data/test/filter_test.rb +59 -0
  43. data/test/for_collection_test.rb +74 -0
  44. data/test/inherit_test.rb +44 -3
  45. data/test/inheritable_test.rb +97 -0
  46. data/test/inline_test.rb +0 -18
  47. data/test/instance_test.rb +0 -19
  48. data/test/json_test.rb +9 -44
  49. data/test/lonely_test.rb +1 -0
  50. data/test/parse_strategy_test.rb +30 -0
  51. data/test/represent_test.rb +88 -0
  52. data/test/representable_test.rb +3 -50
  53. data/test/schema_test.rb +123 -0
  54. data/test/test_helper.rb +1 -1
  55. data/test/xml_test.rb +34 -38
  56. metadata +25 -15
  57. data/lib/representable/decorator/coercion.rb +0 -4
  58. data/lib/representable/readable_writeable.rb +0 -29
  59. data/test/inheritance_test.rb +0 -22
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+
3
+ # tests Inheritable:: classes (the #inherit! method). This can be moved to uber if necessary.
4
+
5
+ class ConfigInheritableTest < MiniTest::Spec
6
+ class CloneableObject
7
+ include Representable::Cloneable
8
+
9
+ # same instance returns same clone.
10
+ def clone
11
+ @clone ||= super
12
+ end
13
+ end
14
+
15
+
16
+ # Inheritable::Array
17
+ it do
18
+ parent = Representable::Inheritable::Array.new([1,2,3])
19
+ child = Representable::Inheritable::Array.new([4])
20
+
21
+ child.inherit!(parent).must_equal([4,1,2,3])
22
+ end
23
+
24
+ # Inheritable::Hash
25
+ Inheritable = Representable::Inheritable
26
+ describe "Inheritable::Hash" do
27
+ it do
28
+ parent = Inheritable::Hash[
29
+ :volume => volume = Uber::Options::Value.new(9),
30
+ :genre => "Powermetal",
31
+ :only_parent => only_parent = Representable::Inheritable::Array["Pumpkin Box"],
32
+ :in_both => in_both = Representable::Inheritable::Array["Roxanne"],
33
+ :hash => {:type => :parent},
34
+ :clone => parent_clone = CloneableObject.new # cloneable is in both hashes.
35
+ ]
36
+ child = Inheritable::Hash[
37
+ :genre => "Metal",
38
+ :pitch => 99,
39
+ :in_both => Representable::Inheritable::Array["Generator"],
40
+ :hash => {:type => :child},
41
+ :clone => child_clone = CloneableObject.new
42
+ ]
43
+
44
+ child.inherit!(parent)
45
+
46
+ # order:
47
+ child.to_a.must_equal [
48
+ [:genre, "Powermetal"], # parent overrides child
49
+ [:pitch, 99], # parent doesn't define pitch
50
+ [:in_both, ["Generator", "Roxanne"]], # Inheritable array gets "merged".
51
+ [:hash, {:type => :parent}], # normal hash merge: parent overwrites child value.
52
+ [:clone, parent_clone.clone],
53
+ [:volume, volume],
54
+ [:only_parent, ["Pumpkin Box"]],
55
+ ]
56
+
57
+ # clone
58
+ child[:only_parent].object_id.wont_equal parent[:only_parent].object_id
59
+ child[:clone].object_id.wont_equal parent[:clone].object_id
60
+
61
+ # still a hash:
62
+ child.must_equal(
63
+ :genre => "Powermetal",
64
+ :pitch => 99,
65
+ :in_both => ["Generator", "Roxanne"],
66
+ :hash => {:type => :parent},
67
+ :clone => parent_clone.clone,
68
+ :volume => volume,
69
+ :only_parent => ["Pumpkin Box"]
70
+ )
71
+ end
72
+
73
+ # nested:
74
+ it do
75
+ parent = Inheritable::Hash[
76
+ :details => Inheritable::Hash[
77
+ :title => title = "Man Of Steel",
78
+ :length => length = Representable::Definition.new(:length) # Cloneable.
79
+ ]]
80
+
81
+ child = Inheritable::Hash[].inherit!(parent)
82
+ child[:details][:track] = 1
83
+
84
+ parent.must_equal({:details => {:title => "Man Of Steel", :length => length}})
85
+
86
+ child.keys.must_equal [:details]
87
+ child[:details].keys.must_equal [:title, :length, :track]
88
+ child[:details][:title].must_equal "Man Of Steel"
89
+ child[:details][:track].must_equal 1
90
+ child[:details][:length].name.must_equal "length"
91
+
92
+ # clone
93
+ child[:details][:title].object_id.must_equal title.object_id
94
+ child[:details][:length].object_id.wont_equal length.object_id
95
+ end
96
+ end
97
+ end
@@ -208,24 +208,6 @@ class InlineTest < MiniTest::Spec
208
208
  end
209
209
 
210
210
 
211
- describe "deprecate mixing :extend and inline representers" do # TODO: remove in 2.0.
212
- representer! do
213
- rpr_module = Module.new do
214
- include Representable::Hash
215
- property :title
216
- end
217
- property :song, :extend => rpr_module do
218
- property :artist
219
- end
220
- end
221
-
222
- it do OpenStruct.new(:song => OpenStruct.new(:title => "The Fever And The Sound", :artist => "Strung Out")).extend(representer).
223
- to_hash.
224
- must_equal({"song"=>{"artist"=>"Strung Out", "title"=>"The Fever And The Sound"}})
225
- end
226
- end
227
-
228
-
229
211
  describe "include module in inline representers" do
230
212
  representer! do
231
213
  extension = Module.new do
@@ -247,25 +247,6 @@ class InstanceTest < BaseTest
247
247
  end
248
248
 
249
249
 
250
- describe "instance: true" do
251
- representer!(:inject => :song_representer) do
252
- property :song,
253
- :extend => song_representer, :instance => true
254
- end
255
-
256
- it "uses Binding#get instead of creating an instance, but deprecates" do
257
- album= Struct.new(:song).new(song = Song.new(1, "The Answer Is Still No"))
258
-
259
- album.
260
- extend(representer).
261
- from_hash("song" => {"title" => "Invincible"}).
262
- song.must_equal Song.new(1, "Invincible")
263
-
264
- album.song.object_id.must_equal song.object_id
265
- end
266
- end
267
-
268
-
269
250
  describe "new syntax for instance: true" do
270
251
  representer!(:inject => :song_representer) do
271
252
  property :song, :pass_options => true,
@@ -22,41 +22,6 @@ module JsonTest
22
22
  end
23
23
 
24
24
 
25
- describe ".from_json" do
26
- it "is delegated to #from_json" do
27
- block = lambda {|*args|}
28
- @Band.any_instance.expects(:from_json).with("{document}", "options") # FIXME: how to NOT expect block?
29
- @Band.from_json("{document}", "options", &block)
30
- end
31
-
32
- it "yields new object and options to block" do
33
- @Band.class_eval { attr_accessor :new_name }
34
- @band = @Band.from_json("{}", :new_name => "Diesel Boy") do |band, options|
35
- band.new_name= options[:new_name]
36
- end
37
- assert_equal "Diesel Boy", @band.new_name
38
- end
39
- end
40
-
41
-
42
- describe ".from_hash" do
43
- it "is delegated to #from_hash not passing the block" do
44
- block = lambda {|*args|}
45
- @Band.any_instance.expects(:from_hash).with("{document}", "options") # FIXME: how to NOT expect block?
46
- @Band.from_hash("{document}", "options", &block)
47
- end
48
-
49
- it "yields new object and options to block" do
50
- @Band.class_eval { attr_accessor :new_name }
51
- @band = @Band.from_hash({}, :new_name => "Diesel Boy") do |band, options|
52
- band.new_name= options[:new_name]
53
- end
54
-
55
- assert_equal "Diesel Boy", @band.new_name
56
- end
57
- end
58
-
59
-
60
25
  describe "#from_json" do
61
26
  before do
62
27
  @band = @Band.new
@@ -204,7 +169,7 @@ module JsonTest
204
169
  end
205
170
 
206
171
  it "#from_json creates correct accessors" do
207
- band = Band.from_json({:name => "Bombshell Rocks"}.to_json)
172
+ band = Band.new.from_json({:name => "Bombshell Rocks"}.to_json)
208
173
  assert_equal "Bombshell Rocks", band.name
209
174
  end
210
175
 
@@ -230,7 +195,7 @@ module JsonTest
230
195
  end
231
196
 
232
197
  it "#from_json creates one Item instance" do
233
- album = Album.from_json('{"label":{"name":"Fat Wreck"}}')
198
+ album = Album.new.from_json('{"label":{"name":"Fat Wreck"}}')
234
199
  assert_equal "Fat Wreck", album.label.name
235
200
  end
236
201
 
@@ -267,7 +232,7 @@ module JsonTest
267
232
  end
268
233
 
269
234
  it "respects :as in #from_json" do
270
- song = Song.from_json({:songName => "Run To The Hills"}.to_json)
235
+ song = Song.new.from_json({:songName => "Run To The Hills"}.to_json)
271
236
  assert_equal "Run To The Hills", song.name
272
237
  end
273
238
 
@@ -288,17 +253,17 @@ module JsonTest
288
253
 
289
254
  describe "#from_json" do
290
255
  it "uses default when property nil in doc" do
291
- album = @Album.from_json({}.to_json)
256
+ album = @Album.new.from_json({}.to_json)
292
257
  assert_equal "30 Years Live", album.name
293
258
  end
294
259
 
295
260
  it "uses value from doc when present" do
296
- album = @Album.from_json({:name => "Live At The Wireless"}.to_json)
261
+ album = @Album.new.from_json({:name => "Live At The Wireless"}.to_json)
297
262
  assert_equal "Live At The Wireless", album.name
298
263
  end
299
264
 
300
265
  it "uses value from doc when empty string" do
301
- album = @Album.from_json({:name => ""}.to_json)
266
+ album = @Album.new.from_json({:name => ""}.to_json)
302
267
  assert_equal "", album.name
303
268
  end
304
269
  end
@@ -333,7 +298,7 @@ end
333
298
  end
334
299
 
335
300
  it "#from_json creates correct accessors" do
336
- cd = CD.from_json({:songs => ["Out in the cold", "Microphone"]}.to_json)
301
+ cd = CD.new.from_json({:songs => ["Out in the cold", "Microphone"]}.to_json)
337
302
  assert_equal ["Out in the cold", "Microphone"], cd.songs
338
303
  end
339
304
 
@@ -364,7 +329,7 @@ end
364
329
 
365
330
  describe "#from_json" do
366
331
  it "pushes collection items to array" do
367
- cd = Compilation.from_json({:bands => [
332
+ cd = Compilation.new.from_json({:bands => [
368
333
  {:name => "Cobra Skulls"},
369
334
  {:name => "Diesel Boy"}]}.to_json)
370
335
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
@@ -388,7 +353,7 @@ end
388
353
  end
389
354
 
390
355
  it "respects :as in #from_json" do
391
- songs = Songs.from_json({:songList => ["Out in the cold", "Microphone"]}.to_json)
356
+ songs = Songs.new.from_json({:songList => ["Out in the cold", "Microphone"]}.to_json)
392
357
  assert_equal ["Out in the cold", "Microphone"], songs.tracks
393
358
  end
394
359
 
@@ -6,6 +6,7 @@ require 'representable/json/hash'
6
6
  class LonelyRepresenterTest < MiniTest::Spec
7
7
  module SongRepresenter
8
8
  include Representable::JSON
9
+
9
10
  property :name
10
11
  end
11
12
 
@@ -83,6 +83,24 @@ class ParseStrategySyncTest < BaseTest
83
83
  end
84
84
 
85
85
 
86
+ # Sync errors, when model and incoming are not in sync.
87
+ describe ":sync with error" do
88
+ representer! do
89
+ property :song, :parse_strategy => :sync do
90
+ property :title
91
+ end
92
+ end
93
+
94
+ # object.song is nil whereas the document contains one.
95
+ it do
96
+ assert_raises Representable::DeserializeError do
97
+ OpenStruct.new.extend(representer).from_hash({"song" => {"title" => "Perpetual"}})
98
+ end
99
+ end
100
+ end
101
+
102
+
103
+
86
104
  # Lonely Collection
87
105
  for_formats(
88
106
  :hash => [Representable::Hash::Collection, [{"title"=>"Resist Stance"}], [{"title"=>"Suffer"}]],
@@ -219,6 +237,18 @@ class ParseStrategyLambdaTest < MiniTest::Spec
219
237
  property :title
220
238
  end
221
239
 
240
+ # property with instance: lambda, using representable's setter. # TODO: that should be handled better via my api.
241
+ describe "property parse_strategy: lambda, representable: false" do
242
+ representer! do
243
+ property :title,
244
+ :instance => lambda { |fragment, options| fragment.to_s }, # this will still call song.title= "8675309".
245
+ :representable => false # don't call object.from_hash
246
+ end
247
+
248
+ let (:song) { Song.new(nil, nil) }
249
+ it { song.extend(representer).from_hash("title" => 8675309).title.must_equal "8675309" }
250
+ end
251
+
222
252
 
223
253
  describe "collection" do
224
254
  representer!(:inject => :song_representer) do
@@ -0,0 +1,88 @@
1
+ require 'test_helper'
2
+
3
+ class RepresentTest < MiniTest::Spec
4
+ let (:songs) { [song, Song.new("Can't Take Them All")] }
5
+ let (:song) { Song.new("Days Go By") }
6
+
7
+ for_formats(
8
+ :hash => [Representable::Hash, out=[{"name" => "Days Go By"}, {"name"=>"Can't Take Them All"}], out],
9
+ :json => [Representable::JSON, out="[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", out],
10
+ # :xml => [Representable::XML, out="<a><song></song><song></song></a>", out]
11
+ ) do |format, mod, output, input|
12
+
13
+ # Representer.represents detects collection.
14
+ describe "Module#to_/from_#{format}" do
15
+ let (:format) { format }
16
+
17
+ let (:representer) {
18
+ Module.new do
19
+ include mod
20
+ property :name
21
+
22
+ collection_representer :class => Song # TODOOOOOOOOOOOO: test without Song and fix THIS FUCKINGNoMethodError: undefined method `name=' for {"name"=>"Days Go By"}:Hash ERROR!!!!!!!!!!!!!!!
23
+ end
24
+ }
25
+
26
+ it { render(representer.represent(songs)).must_equal_document output }
27
+ it { parse(representer.represent([]), input).must_equal songs }
28
+ end
29
+
30
+ # Decorator.represents detects collection.
31
+ describe "Decorator#to_/from_#{format}" do
32
+ let (:format) { format }
33
+ let (:representer) {
34
+ Class.new(Representable::Decorator) do
35
+ include mod
36
+ property :name
37
+
38
+ collection_representer :class => Song
39
+ end
40
+ }
41
+
42
+ it { render(representer.represent(songs)).must_equal_document output }
43
+ it { parse(representer.represent([]), input).must_equal songs }
44
+ end
45
+ end
46
+
47
+
48
+ for_formats(
49
+ :hash => [Representable::Hash, out={"name" => "Days Go By"}, out],
50
+ :json => [Representable::JSON, out="{\"name\":\"Days Go By\"}", out],
51
+ # :xml => [Representable::XML, out="<a><song></song><song></song></a>", out]
52
+ ) do |format, mod, output, input|
53
+
54
+ # Representer.represents detects singular.
55
+ describe "Module#to_/from_#{format}" do
56
+ let (:format) { format }
57
+
58
+ let (:representer) {
59
+ Module.new do
60
+ include mod
61
+ property :name
62
+
63
+ collection_representer :class => Song
64
+ end
65
+ }
66
+
67
+ it { render(representer.represent(song)).must_equal_document output }
68
+ it { parse(representer.represent(Song.new), input).must_equal song }
69
+ end
70
+
71
+
72
+ # Decorator.represents detects singular.
73
+ describe "Decorator#to_/from_#{format}" do
74
+ let (:format) { format }
75
+ let (:representer) {
76
+ Class.new(Representable::Decorator) do
77
+ include mod
78
+ property :name
79
+
80
+ collection_representer :class => Song
81
+ end
82
+ }
83
+
84
+ it { render(representer.represent(song)).must_equal_document output }
85
+ it { parse(representer.represent(Song.new), input).must_equal song }
86
+ end
87
+ end
88
+ end
@@ -27,34 +27,7 @@ class RepresentableTest < MiniTest::Spec
27
27
 
28
28
 
29
29
  describe "#representable_attrs" do
30
- it "responds to #representable_attrs" do
31
- assert_equal 1, Band.representable_attrs.size
32
- assert_equal "name", Band.representable_attrs.first.name
33
- end
34
-
35
30
  describe "in module" do
36
- it "returns definitions" do
37
- assert_equal 1, BandRepresentation.representable_attrs.size
38
- assert_equal "name", BandRepresentation.representable_attrs.first.name
39
- end
40
-
41
- it "inherits to including modules xxx " do
42
- assert_equal 2, PunkBandRepresentation.representable_attrs.size
43
- assert_equal "name", PunkBandRepresentation.representable_attrs[:name].name
44
- assert_equal "street_cred", PunkBandRepresentation.representable_attrs[:street_cred].name
45
- end
46
-
47
- it "inherits to including class" do
48
- band = Class.new do
49
- include Representable
50
- include PunkBandRepresentation
51
- end
52
-
53
- assert_equal 2, band.representable_attrs.size
54
- assert_equal "name", band.representable_attrs[:name].name
55
- assert_equal "street_cred", band.representable_attrs[:street_cred].name
56
- end
57
-
58
31
  it "allows including the concrete representer module later" do
59
32
  vd = class VD
60
33
  attr_accessor :name, :street_cred
@@ -78,20 +51,6 @@ class RepresentableTest < MiniTest::Spec
78
51
  # vd.name = "Van Halen"
79
52
  # assert_equal "{\"name\":\"Van Halen\"}", vd.to_json
80
53
  #end
81
-
82
- it "doesn't share inherited properties between family members" do
83
- parent = Module.new do
84
- include Representable
85
- property :id
86
- end
87
-
88
- child = Module.new do
89
- include Representable
90
- include parent
91
- end
92
-
93
- assert parent.representable_attrs.first.object_id != child.representable_attrs.first.object_id, "definitions shouldn't be identical"
94
- end
95
54
  end
96
55
  end
97
56
 
@@ -173,8 +132,8 @@ class RepresentableTest < MiniTest::Spec
173
132
  end
174
133
 
175
134
  it "creates correct Definition" do
176
- assert_equal "albums", RockBand.representable_attrs[:albums].name
177
- assert RockBand.representable_attrs[:albums].array?
135
+ assert_equal "albums", RockBand.representable_attrs.get(:albums).name
136
+ assert RockBand.representable_attrs.get(:albums).array?
178
137
  end
179
138
  end
180
139
 
@@ -185,13 +144,6 @@ class RepresentableTest < MiniTest::Spec
185
144
  end
186
145
 
187
146
 
188
- describe "#definition_class" do
189
- it "returns Definition class" do
190
- assert_equal Representable::Definition, Band.send(:definition_class)
191
- end
192
- end
193
-
194
-
195
147
  # DISCUSS: i don't like the JSON requirement here, what about some generic test module?
196
148
  class PopBand
197
149
  include Representable::JSON
@@ -211,6 +163,7 @@ class RepresentableTest < MiniTest::Spec
211
163
  assert_equal 2, @band.groupies
212
164
  end
213
165
 
166
+
214
167
  it "accepts :exclude option" do
215
168
  @band.from_hash({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]})
216
169
  assert_equal "No One's Choice", @band.name