representable 1.2.3 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -5,3 +5,8 @@ doc
5
5
  Rake.config
6
6
  pkg
7
7
  .project
8
+ .bundle
9
+ Gemfile.lock
10
+ *.swp
11
+ *.swo
12
+ bin
@@ -1,3 +1,9 @@
1
- rvm: 1.9.2
2
1
  notifications:
3
2
  irc: "irc.freenode.org#cells"
3
+ matrix:
4
+ include:
5
+ - rvm: 1.8.7
6
+ gemfile: gemfiles/Gemfile.mongoid-2.4
7
+ - rvm: 1.9.3
8
+ gemfile: Gemfile
9
+
@@ -1,3 +1,8 @@
1
+ h2. 1.2.4
2
+
3
+ * ObjectBinding no longer tries to extend nil values when rendering and @:render_nil@ is set.
4
+ * In XML you can now use @:wrap@ to define an additional container tag around properties and collections.
5
+
1
6
  h2. 1.2.3
2
7
 
3
8
  * Using virtus for coercion now works in both classes and modules. Thanks to @solnic for a great collaboration. Open-source rocks!
@@ -15,7 +15,7 @@ This keeps your representation knowledge in one place when implementing REST ser
15
15
  * Bidirectional - rendering and parsing
16
16
  * OOP access to documents
17
17
  * Support for JSON and XML
18
- * Coercion support with virtus[https://github.com/solnic/virtus.]
18
+ * Coercion support with virtus[https://github.com/solnic/virtus]
19
19
 
20
20
 
21
21
  == Example
@@ -317,6 +317,26 @@ You can also map properties to tag attributes in representable.
317
317
 
318
318
  Naturally, this works for both ways.
319
319
 
320
+ === Wrapping collections
321
+
322
+ It is sometimes unavoidable to wrap tag lists in a container tag.
323
+
324
+ module AlbumRepresenter
325
+ include Representable::XML
326
+
327
+ collection :songs, :from => :song, :wrap => :songs
328
+ end
329
+
330
+ Note that +:wrap+ defines the container tag name.
331
+
332
+ Album.new.to_xml #=>
333
+ <album>
334
+ <songs>
335
+ <song>Laundry Basket</song>
336
+ <song>Two Kevins</song>
337
+ <song>Wright and Rong</song>
338
+ </songs>
339
+ </album>
320
340
 
321
341
  == Coercion
322
342
 
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in roar-rails.gemspec
4
+ gemspec :path => '../'
5
+
6
+ gem 'mongoid', '~> 2.4.0'
@@ -44,5 +44,25 @@ module Representable
44
44
  object
45
45
  end
46
46
  end
47
+
48
+ module Object
49
+ include Binding::Extend # provides #serialize/#deserialize with extend.
50
+
51
+ def serialize(object)
52
+ return object if object.nil?
53
+
54
+ super(object).send(serialize_method, :wrap => false)
55
+ end
56
+
57
+ def deserialize(data)
58
+ # DISCUSS: does it make sense to skip deserialization of nil-values here?
59
+ super(create_object).send(deserialize_method, data)
60
+ end
61
+
62
+ def create_object
63
+ definition.sought_type.new
64
+ end
65
+ end
66
+
47
67
  end
48
68
  end
@@ -3,19 +3,14 @@ require 'representable/binding'
3
3
  module Representable
4
4
  module JSON
5
5
  module ObjectBinding
6
- # TODO: provide a base ObjectBinding for XML/JSON/MP.
7
- include Binding::Extend # provides #serialize/#deserialize with extend.
6
+ include Binding::Object
8
7
 
9
- def serialize(object)
10
- super(object).to_hash(:wrap => false)
8
+ def serialize_method
9
+ :to_hash
11
10
  end
12
11
 
13
- def deserialize(hash)
14
- super(create_object).from_hash(hash)
15
- end
16
-
17
- def create_object
18
- definition.sought_type.new
12
+ def deserialize_method
13
+ :from_hash
19
14
  end
20
15
  end
21
16
 
@@ -3,15 +3,14 @@ require 'representable/binding'
3
3
  module Representable
4
4
  module XML
5
5
  module ObjectBinding
6
- # TODO: provide a base ObjectBinding for XML/JSON/MP.
7
- include Binding::Extend # provides #serialize/#deserialize with extend.
6
+ include Binding::Object
8
7
 
9
- def serialize(object)
10
- super(object).to_node(:wrap => false)
8
+ def serialize_method
9
+ :to_node
11
10
  end
12
11
 
13
- def deserialize(hash)
14
- super(create_object).from_node(hash)
12
+ def deserialize_method
13
+ :from_node
15
14
  end
16
15
 
17
16
  def deserialize_node(node)
@@ -21,10 +20,6 @@ module Representable
21
20
  def serialize_node(node, value)
22
21
  serialize(value)
23
22
  end
24
-
25
- def create_object
26
- definition.sought_type.new
27
- end
28
23
  end
29
24
 
30
25
 
@@ -35,11 +30,20 @@ module Representable
35
30
  end
36
31
 
37
32
  def write(parent, value)
38
- parent << serialize_for(value, parent)
33
+ wrap_node = parent
34
+
35
+ if wrap = definition.options[:wrap]
36
+ parent << wrap_node = node_for(parent, wrap)
37
+ end
38
+
39
+ wrap_node << serialize_for(value, parent)
39
40
  end
40
41
 
41
42
  def read(node)
42
- nodes = node.search("./#{xpath}")
43
+ selector = "./#{xpath}"
44
+ selector = "./#{definition.options[:wrap]}/#{xpath}" if definition.options[:wrap]
45
+ nodes = node.search(selector)
46
+
43
47
  return FragmentNotFound if nodes.size == 0 # TODO: write dedicated test!
44
48
 
45
49
  deserialize_from(nodes)
@@ -48,7 +52,7 @@ module Representable
48
52
  # Creates wrapped node for the property.
49
53
  def serialize_for(value, parent)
50
54
  #def serialize_for(value, parent, tag_name=definition.from)
51
- node = Nokogiri::XML::Node.new(definition.from, parent.document)
55
+ node = node_for(parent, definition.from)
52
56
  serialize_node(node, value)
53
57
  end
54
58
 
@@ -70,19 +74,16 @@ module Representable
70
74
  def xpath
71
75
  definition.from
72
76
  end
77
+
78
+ def node_for(parent, name)
79
+ Nokogiri::XML::Node.new(name.to_s, parent.document)
80
+ end
73
81
  end
74
82
 
75
83
  class CollectionBinding < PropertyBinding
76
- def write(parent, value)
77
- serialize_items(value, parent).each do |node|
78
- parent << node
79
- end
80
- end
81
-
82
- def serialize_items(value, parent)
83
- value.collect do |obj|
84
- serialize_for(obj, parent)
85
- end
84
+ def serialize_for(value, parent)
85
+ # return NodeSet so << works.
86
+ set_for(parent, value.collect { |item| super(item, parent) })
86
87
  end
87
88
 
88
89
  def deserialize_from(nodes)
@@ -90,15 +91,20 @@ module Representable
90
91
  deserialize_node(item)
91
92
  end
92
93
  end
94
+
95
+ private
96
+ def set_for(parent, nodes)
97
+ Nokogiri::XML::NodeSet.new(parent.document, nodes)
98
+ end
93
99
  end
94
100
 
95
101
 
96
102
  class HashBinding < CollectionBinding
97
- def serialize_items(value, parent)
98
- value.collect do |k, v|
99
- node = Nokogiri::XML::Node.new(k, parent.document)
103
+ def serialize_for(value, parent)
104
+ set_for(parent, value.collect do |k, v|
105
+ node = node_for(parent, k)
100
106
  serialize_node(node, v)
101
- end
107
+ end)
102
108
  end
103
109
 
104
110
  def deserialize_from(nodes)
@@ -4,24 +4,24 @@ module Representable::JSON
4
4
  module Hash
5
5
  include Representable::JSON
6
6
  include HashMethods
7
-
7
+
8
8
  def self.included(base)
9
9
  base.class_eval do
10
10
  include Representable
11
11
  extend ClassMethods
12
12
  end
13
13
  end
14
-
15
-
14
+
15
+
16
16
  module ClassMethods
17
17
  def values(options)
18
18
  hash :_self, options
19
19
  end
20
20
  end
21
-
22
-
21
+
22
+
23
23
  def definition_opts
24
- [:_self, :hash => true, :use_attributes => true]
24
+ [:_self, {:hash => true, :use_attributes => true}]
25
25
  end
26
26
  end
27
27
  end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.2.3"
2
+ VERSION = "1.2.4"
3
3
  end
@@ -4,48 +4,48 @@ module Representable::XML
4
4
  module AttributeHash
5
5
  include Representable::XML
6
6
  include Representable::HashMethods
7
-
7
+
8
8
  def self.included(base)
9
9
  base.class_eval do
10
10
  include Representable
11
11
  extend ClassMethods
12
12
  end
13
13
  end
14
-
15
-
14
+
15
+
16
16
  module ClassMethods
17
17
  def values(options)
18
18
  hash :_self, options.merge!(:use_attributes => true)
19
19
  end
20
20
  end
21
-
22
-
21
+
22
+
23
23
  def definition_opts
24
- [:_self, :hash => true, :use_attributes => true]
24
+ [:_self, {:hash => true, :use_attributes => true}]
25
25
  end
26
26
  end
27
-
27
+
28
28
  module Hash
29
29
  include Representable::XML
30
30
  include HashMethods
31
-
31
+
32
32
  def self.included(base)
33
33
  base.class_eval do
34
34
  include Representable
35
35
  extend ClassMethods
36
36
  end
37
37
  end
38
-
39
-
38
+
39
+
40
40
  module ClassMethods
41
41
  def values(options)
42
42
  hash :_self, options
43
43
  end
44
44
  end
45
-
46
-
45
+
46
+
47
47
  def definition_opts
48
- [:_self, :hash => true]
48
+ [:_self, {:hash => true}]
49
49
  end
50
50
  end
51
51
  end
@@ -4,7 +4,7 @@ module JsonTest
4
4
  class APITest < MiniTest::Spec
5
5
  Json = Representable::JSON
6
6
  Def = Representable::Definition
7
-
7
+
8
8
  describe "JSON module" do
9
9
  before do
10
10
  @Band = Class.new do
@@ -12,23 +12,23 @@ module JsonTest
12
12
  property :name
13
13
  property :label
14
14
  attr_accessor :name, :label
15
-
15
+
16
16
  def initialize(name=nil)
17
17
  self.name = name if name
18
18
  end
19
19
  end
20
-
20
+
21
21
  @band = @Band.new
22
22
  end
23
-
24
-
23
+
24
+
25
25
  describe ".from_json" do
26
26
  it "is delegated to #from_json" do
27
27
  block = lambda {|*args|}
28
28
  @Band.any_instance.expects(:from_json).with("{document}", "options") # FIXME: how to NOT expect block?
29
29
  @Band.from_json("{document}", "options", &block)
30
30
  end
31
-
31
+
32
32
  it "yields new object and options to block" do
33
33
  @Band.class_eval { attr_accessor :new_name }
34
34
  @band = @Band.from_json("{}", :new_name => "Diesel Boy") do |band, options|
@@ -37,113 +37,113 @@ module JsonTest
37
37
  assert_equal "Diesel Boy", @band.new_name
38
38
  end
39
39
  end
40
-
41
-
40
+
41
+
42
42
  describe ".from_hash" do
43
43
  it "is delegated to #from_hash not passing the block" do
44
44
  block = lambda {|*args|}
45
45
  @Band.any_instance.expects(:from_hash).with("{document}", "options") # FIXME: how to NOT expect block?
46
46
  @Band.from_hash("{document}", "options", &block)
47
47
  end
48
-
48
+
49
49
  it "yields new object and options to block" do
50
50
  @Band.class_eval { attr_accessor :new_name }
51
51
  @band = @Band.from_hash({}, :new_name => "Diesel Boy") do |band, options|
52
52
  band.new_name= options[:new_name]
53
53
  end
54
-
54
+
55
55
  assert_equal "Diesel Boy", @band.new_name
56
56
  end
57
57
  end
58
-
59
-
58
+
59
+
60
60
  describe "#from_json" do
61
61
  before do
62
62
  @band = @Band.new
63
63
  @json = {:name => "Nofx", :label => "NOFX"}.to_json
64
64
  end
65
-
65
+
66
66
  it "parses JSON and assigns properties" do
67
67
  @band.from_json(@json)
68
68
  assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
69
69
  end
70
70
  end
71
-
72
-
71
+
72
+
73
73
  describe "#from_hash" do
74
74
  before do
75
75
  @band = @Band.new
76
76
  @hash = {"name" => "Nofx", "label" => "NOFX"}
77
77
  end
78
-
78
+
79
79
  it "receives hash and assigns properties" do
80
80
  @band.from_hash(@hash)
81
81
  assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
82
82
  end
83
-
83
+
84
84
  it "respects :wrap option" do
85
85
  @band.from_hash({"band" => {"name" => "This Is A Standoff"}}, :wrap => :band)
86
86
  assert_equal "This Is A Standoff", @band.name
87
87
  end
88
-
88
+
89
89
  it "respects :wrap option over representation_wrap" do
90
90
  @Band.class_eval do
91
- self.representation_wrap = :group
91
+ self.representation_wrap = :group
92
92
  end
93
93
  @band.from_hash({"band" => {"name" => "This Is A Standoff"}}, :wrap => :band)
94
94
  assert_equal "This Is A Standoff", @band.name
95
95
  end
96
96
  end
97
-
98
-
97
+
98
+
99
99
  describe "#to_json" do
100
100
  it "delegates to #to_hash and returns string" do
101
- assert_equal "{\"name\":\"Rise Against\"}", @Band.new("Rise Against").to_json
101
+ assert_json "{\"name\":\"Rise Against\"}", @Band.new("Rise Against").to_json
102
102
  end
103
103
  end
104
-
105
-
104
+
105
+
106
106
  describe "#to_hash" do
107
107
  it "returns unwrapped hash" do
108
108
  hash = @Band.new("Rise Against").to_hash
109
109
  assert_equal({"name"=>"Rise Against"}, hash)
110
110
  end
111
-
111
+
112
112
  it "respects #representation_wrap=" do
113
113
  @Band.representation_wrap = :group
114
114
  assert_equal({:group=>{"name"=>"Rise Against"}}, @Band.new("Rise Against").to_hash)
115
115
  end
116
-
116
+
117
117
  it "respects :wrap option" do
118
118
  assert_equal({:band=>{"name"=>"NOFX"}}, @Band.new("NOFX").to_hash(:wrap => :band))
119
119
  end
120
-
120
+
121
121
  it "respects :wrap option over representation_wrap" do
122
122
  @Band.class_eval do
123
- self.representation_wrap = :group
123
+ self.representation_wrap = :group
124
124
  end
125
125
  assert_equal({:band=>{"name"=>"Rise Against"}}, @Band.new("Rise Against").to_hash(:wrap => :band))
126
126
  end
127
127
  end
128
-
128
+
129
129
  describe "#binding_for_definition" do
130
130
  it "returns ObjectBinding" do
131
131
  assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :class => Hash))
132
132
  end
133
-
133
+
134
134
  it "returns TextBinding" do
135
135
  assert_kind_of Json::PropertyBinding, Json.binding_for_definition(Def.new(:band))
136
136
  end
137
-
137
+
138
138
  it "returns HashBinding" do
139
139
  assert_kind_of Json::HashBinding, Json.binding_for_definition(Def.new(:band, :hash => true))
140
140
  end
141
-
141
+
142
142
  it "returns CollectionBinding" do
143
143
  assert_kind_of Json::CollectionBinding, Json.binding_for_definition(Def.new(:band, :collection => true))
144
144
  end
145
145
  end
146
-
146
+
147
147
  describe "#representable_bindings" do
148
148
  it "returns bindings for each property" do
149
149
  assert_equal 2, @band.send(:representable_bindings_for, Json).size
@@ -151,27 +151,27 @@ module JsonTest
151
151
  end
152
152
  end
153
153
  end
154
-
155
-
154
+
155
+
156
156
  describe "DCI" do
157
157
  module SongRepresenter
158
158
  include Representable::JSON
159
159
  property :name
160
160
  end
161
-
161
+
162
162
  module AlbumRepresenter
163
163
  include Representable::JSON
164
164
  property :best_song, :class => Song, :extend => SongRepresenter
165
165
  collection :songs, :class => Song, :extend => [SongRepresenter]
166
166
  end
167
-
168
-
167
+
168
+
169
169
  it "allows adding the representer by using #extend" do
170
170
  module BandRepresenter
171
171
  include Representable::JSON
172
172
  property :name
173
173
  end
174
-
174
+
175
175
  civ = Object.new
176
176
  civ.instance_eval do
177
177
  def name; "CIV"; end
@@ -179,30 +179,30 @@ module JsonTest
179
179
  @name = v
180
180
  end
181
181
  end
182
-
182
+
183
183
  civ.extend(BandRepresenter)
184
- assert_equal "{\"name\":\"CIV\"}", civ.to_json
184
+ assert_json "{\"name\":\"CIV\"}", civ.to_json
185
185
  end
186
-
186
+
187
187
  it "extends contained models when serializing" do
188
188
  @album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
189
189
  @album.extend(AlbumRepresenter)
190
-
191
- assert_equal "{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}", @album.to_json
190
+
191
+ assert_json "{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}", @album.to_json
192
192
  end
193
-
193
+
194
194
  it "extends contained models when deserializing" do
195
195
  #@album = Album.new(Song.new("I Hate My Brain"), Song.new("Mr. Charisma"))
196
196
  @album = Album.new
197
197
  @album.extend(AlbumRepresenter)
198
-
198
+
199
199
  @album.from_json("{\"best_song\":{\"name\":\"Mr. Charisma\"},\"songs\":[{\"name\":\"I Hate My Brain\"},{\"name\":\"Mr. Charisma\"}]}")
200
200
  assert_equal "Mr. Charisma", @album.best_song.name
201
201
  end
202
202
  end
203
203
  end
204
-
205
-
204
+
205
+
206
206
  class PropertyTest < MiniTest::Spec
207
207
  describe "property :name" do
208
208
  class Band
@@ -210,45 +210,45 @@ module JsonTest
210
210
  property :name
211
211
  attr_accessor :name
212
212
  end
213
-
213
+
214
214
  it "#from_json creates correct accessors" do
215
215
  band = Band.from_json({:name => "Bombshell Rocks"}.to_json)
216
216
  assert_equal "Bombshell Rocks", band.name
217
217
  end
218
-
218
+
219
219
  it "#to_json serializes correctly" do
220
220
  band = Band.new
221
221
  band.name = "Cigar"
222
-
223
- assert_equal '{"name":"Cigar"}', band.to_json
222
+
223
+ assert_json '{"name":"Cigar"}', band.to_json
224
224
  end
225
225
  end
226
-
226
+
227
227
  describe ":class => Item" do
228
228
  class Label
229
229
  include Representable::JSON
230
230
  property :name
231
231
  attr_accessor :name
232
232
  end
233
-
233
+
234
234
  class Album
235
235
  include Representable::JSON
236
236
  property :label, :class => Label
237
237
  attr_accessor :label
238
238
  end
239
-
239
+
240
240
  it "#from_json creates one Item instance" do
241
241
  album = Album.from_json('{"label":{"name":"Fat Wreck"}}')
242
242
  assert_equal "Fat Wreck", album.label.name
243
243
  end
244
-
244
+
245
245
  it "#to_json serializes" do
246
246
  label = Label.new; label.name = "Fat Wreck"
247
247
  album = Album.new; album.label = label
248
-
249
- assert_equal '{"label":{"name":"Fat Wreck"}}', album.to_json
248
+
249
+ assert_json '{"label":{"name":"Fat Wreck"}}', album.to_json
250
250
  end
251
-
251
+
252
252
  describe ":different_name, :class => Label" do
253
253
  before do
254
254
  @Album = Class.new do
@@ -257,34 +257,34 @@ module JsonTest
257
257
  attr_accessor :seller
258
258
  end
259
259
  end
260
-
260
+
261
261
  it "#to_xml respects the different name" do
262
262
  label = Label.new; label.name = "Fat Wreck"
263
263
  album = @Album.new; album.seller = label
264
-
265
- assert_equal "{\"seller\":{\"name\":\"Fat Wreck\"}}", album.to_json(:wrap => false)
264
+
265
+ assert_json "{\"seller\":{\"name\":\"Fat Wreck\"}}", album.to_json(:wrap => false)
266
266
  end
267
267
  end
268
268
  end
269
-
269
+
270
270
  describe ":from => :songName" do
271
271
  class Song
272
272
  include Representable::JSON
273
273
  property :name, :from => :songName
274
274
  attr_accessor :name
275
275
  end
276
-
276
+
277
277
  it "respects :from in #from_json" do
278
278
  song = Song.from_json({:songName => "Run To The Hills"}.to_json)
279
279
  assert_equal "Run To The Hills", song.name
280
280
  end
281
-
281
+
282
282
  it "respects :from in #to_json" do
283
283
  song = Song.new; song.name = "Run To The Hills"
284
- assert_equal '{"songName":"Run To The Hills"}', song.to_json
284
+ assert_json '{"songName":"Run To The Hills"}', song.to_json
285
285
  end
286
286
  end
287
-
287
+
288
288
  describe ":default => :value" do
289
289
  before do
290
290
  @Album = Class.new do
@@ -293,39 +293,39 @@ module JsonTest
293
293
  attr_accessor :name
294
294
  end
295
295
  end
296
-
296
+
297
297
  describe "#from_json" do
298
298
  it "uses default when property nil in doc" do
299
299
  album = @Album.from_json({}.to_json)
300
300
  assert_equal "30 Years Live", album.name
301
301
  end
302
-
302
+
303
303
  it "uses value from doc when present" do
304
304
  album = @Album.from_json({:name => "Live At The Wireless"}.to_json)
305
305
  assert_equal "Live At The Wireless", album.name
306
306
  end
307
-
307
+
308
308
  it "uses value from doc when empty string" do
309
309
  album = @Album.from_json({:name => ""}.to_json)
310
310
  assert_equal "", album.name
311
311
  end
312
312
  end
313
-
313
+
314
314
  describe "#to_json" do
315
315
  it "uses default when not available in object" do
316
- assert_equal "{\"name\":\"30 Years Live\"}", @Album.new.to_json
316
+ assert_json "{\"name\":\"30 Years Live\"}", @Album.new.to_json
317
317
  end
318
-
318
+
319
319
  it "uses value from represented object when present" do
320
320
  album = @Album.new
321
321
  album.name = "Live At The Wireless"
322
- assert_equal "{\"name\":\"Live At The Wireless\"}", album.to_json
322
+ assert_json "{\"name\":\"Live At The Wireless\"}", album.to_json
323
323
  end
324
-
324
+
325
325
  it "uses value from represented object when emtpy string" do
326
326
  album = @Album.new
327
327
  album.name = ""
328
- assert_equal "{\"name\":\"\"}", album.to_json
328
+ assert_json "{\"name\":\"\"}", album.to_json
329
329
  end
330
330
  end
331
331
  end
@@ -339,37 +339,37 @@ end
339
339
  collection :songs
340
340
  attr_accessor :songs
341
341
  end
342
-
342
+
343
343
  it "#from_json creates correct accessors" do
344
344
  cd = CD.from_json({:songs => ["Out in the cold", "Microphone"]}.to_json)
345
345
  assert_equal ["Out in the cold", "Microphone"], cd.songs
346
346
  end
347
-
347
+
348
348
  it "#to_json serializes correctly" do
349
349
  cd = CD.new
350
350
  cd.songs = ["Out in the cold", "Microphone"]
351
-
352
- assert_equal '{"songs":["Out in the cold","Microphone"]}', cd.to_json
351
+
352
+ assert_json '{"songs":["Out in the cold","Microphone"]}', cd.to_json
353
353
  end
354
354
  end
355
-
355
+
356
356
  describe "collection :name, :class => Band" do
357
357
  class Band
358
358
  include Representable::JSON
359
359
  property :name
360
360
  attr_accessor :name
361
-
361
+
362
362
  def initialize(name="")
363
363
  self.name = name
364
364
  end
365
365
  end
366
-
366
+
367
367
  class Compilation
368
368
  include Representable::JSON
369
369
  collection :bands, :class => Band
370
370
  attr_accessor :bands
371
371
  end
372
-
372
+
373
373
  describe "#from_json" do
374
374
  it "pushes collection items to array" do
375
375
  cd = Compilation.from_json({:bands => [
@@ -377,43 +377,43 @@ end
377
377
  {:name => "Diesel Boy"}]}.to_json)
378
378
  assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
379
379
  end
380
-
380
+
381
381
  it "creates emtpy array from default if configured" do
382
382
  cd = Compilation.from_json({}.to_json)
383
383
  assert_equal [], cd.bands
384
384
  end
385
385
  end
386
-
386
+
387
387
  it "responds to #to_json" do
388
388
  cd = Compilation.new
389
389
  cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
390
-
391
- assert_equal '{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}', cd.to_json
390
+
391
+ assert_json '{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}', cd.to_json
392
392
  end
393
393
  end
394
-
395
-
394
+
395
+
396
396
  describe ":from => :songList" do
397
397
  class Songs
398
398
  include Representable::JSON
399
399
  collection :tracks, :from => :songList
400
400
  attr_accessor :tracks
401
401
  end
402
-
402
+
403
403
  it "respects :from in #from_json" do
404
404
  songs = Songs.from_json({:songList => ["Out in the cold", "Microphone"]}.to_json)
405
405
  assert_equal ["Out in the cold", "Microphone"], songs.tracks
406
406
  end
407
-
407
+
408
408
  it "respects option in #to_json" do
409
409
  songs = Songs.new
410
410
  songs.tracks = ["Out in the cold", "Microphone"]
411
-
412
- assert_equal '{"songList":["Out in the cold","Microphone"]}', songs.to_json
411
+
412
+ assert_json '{"songList":["Out in the cold","Microphone"]}', songs.to_json
413
413
  end
414
414
  end
415
415
  end
416
-
416
+
417
417
  class HashTest < MiniTest::Spec
418
418
  describe "hash :songs" do
419
419
  before do
@@ -421,34 +421,34 @@ end
421
421
  include Representable::JSON
422
422
  hash :songs
423
423
  end
424
-
424
+
425
425
  class SongList
426
426
  attr_accessor :songs
427
427
  end
428
-
428
+
429
429
  @list = SongList.new.extend(representer)
430
430
  end
431
-
431
+
432
432
  it "renders with #to_json" do
433
433
  @list.songs = {:one => "65", :two => "Emo Boy"}
434
- assert_equal "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", @list.to_json
434
+ assert_json "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", @list.to_json
435
435
  end
436
-
436
+
437
437
  it "parses with #from_json" do
438
438
  assert_equal({"one" => "65", "two" => ["Emo Boy"]}, @list.from_json("{\"songs\":{\"one\":\"65\",\"two\":[\"Emo Boy\"]}}").songs)
439
439
  end
440
440
  end
441
-
441
+
442
442
  end
443
-
444
-
443
+
444
+
445
445
  require 'representable/json/collection'
446
446
  class CollectionRepresenterTest < MiniTest::Spec
447
447
  module SongRepresenter
448
448
  include Representable::JSON
449
449
  property :name
450
450
  end
451
-
451
+
452
452
  describe "JSON::Collection" do
453
453
  describe "with contained objects" do
454
454
  before do
@@ -457,42 +457,42 @@ end
457
457
  items :class => Song, :extend => SongRepresenter
458
458
  end
459
459
  end
460
-
460
+
461
461
  it "renders objects with #to_json" do
462
- assert_equal "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", [Song.new("Days Go By"), Song.new("Can't Take Them All")].extend(@songs_representer).to_json
462
+ assert_json "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", [Song.new("Days Go By"), Song.new("Can't Take Them All")].extend(@songs_representer).to_json
463
463
  end
464
-
464
+
465
465
  it "returns objects array from #from_json" do
466
466
  assert_equal [Song.new("Days Go By"), Song.new("Can't Take Them All")], [].extend(@songs_representer).from_json("[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]")
467
467
  end
468
468
  end
469
-
469
+
470
470
  describe "with contained text" do
471
471
  before do
472
472
  @songs_representer = Module.new do
473
473
  include Representable::JSON::Collection
474
474
  end
475
475
  end
476
-
476
+
477
477
  it "renders contained items #to_json" do
478
- assert_equal "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
478
+ assert_json "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
479
479
  end
480
-
480
+
481
481
  it "returns objects array from #from_json" do
482
482
  assert_equal ["Days Go By", "Can't Take Them All"], [].extend(@songs_representer).from_json("[\"Days Go By\",\"Can't Take Them All\"]")
483
483
  end
484
484
  end
485
485
  end
486
486
  end
487
-
488
-
487
+
488
+
489
489
  require 'representable/json/hash'
490
490
  class HashRepresenterTest < MiniTest::Spec
491
491
  module SongRepresenter
492
492
  include Representable::JSON
493
493
  property :name
494
494
  end
495
-
495
+
496
496
  describe "JSON::Hash" do # TODO: move to HashTest.
497
497
  describe "with contained objects" do
498
498
  before do
@@ -501,47 +501,47 @@ end
501
501
  values :class => Song, :extend => SongRepresenter
502
502
  end
503
503
  end
504
-
504
+
505
505
  describe "#to_json" do
506
506
  it "renders objects" do
507
- assert_equal "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json
507
+ assert_json "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json
508
508
  end
509
-
509
+
510
510
  it "respects :exclude" do
511
- assert_equal "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:exclude => [:one])
511
+ assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:exclude => [:one])
512
512
  end
513
-
513
+
514
514
  it "respects :include" do
515
- assert_equal "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:include => [:two])
515
+ assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:include => [:two])
516
516
  end
517
517
  end
518
-
518
+
519
519
  describe "#from_json" do
520
520
  it "returns objects array" do
521
521
  assert_equal({"one" => Song.new("Days Go By"), "two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}"))
522
522
  end
523
-
523
+
524
524
  it "respects :exclude" do
525
525
  assert_equal({"two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", :exclude => [:one]))
526
526
  end
527
-
527
+
528
528
  it "respects :include" do
529
529
  assert_equal({"one" => Song.new("Days Go By")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", :include => [:one]))
530
530
  end
531
531
  end
532
532
  end
533
-
533
+
534
534
  describe "with contained text" do
535
535
  before do
536
536
  @songs_representer = Module.new do
537
537
  include Representable::JSON::Collection
538
538
  end
539
539
  end
540
-
540
+
541
541
  it "renders contained items #to_json" do
542
- assert_equal "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
542
+ assert_json "[\"Days Go By\",\"Can't Take Them All\"]", ["Days Go By", "Can't Take Them All"].extend(@songs_representer).to_json
543
543
  end
544
-
544
+
545
545
  it "returns objects array from #from_json" do
546
546
  assert_equal ["Days Go By", "Can't Take Them All"], [].extend(@songs_representer).from_json("[\"Days Go By\",\"Can't Take Them All\"]")
547
547
  end
@@ -63,7 +63,7 @@ class RepresentableTest < MiniTest::Spec
63
63
  end.new
64
64
  vd.name = "Vention Dention"
65
65
  vd.street_cred = 1
66
- assert_equal "{\"name\":\"Vention Dention\",\"street_cred\":1}", vd.to_json
66
+ assert_json "{\"name\":\"Vention Dention\",\"street_cred\":1}", vd.to_json
67
67
  end
68
68
 
69
69
  #it "allows including the concrete representer module only" do
@@ -113,7 +113,7 @@ class RepresentableTest < MiniTest::Spec
113
113
  band = Bodyjar.new
114
114
  band.name = "Bodyjar"
115
115
 
116
- assert_equal "{\"band\":{\"name\":\"Bodyjar\"}}", band.to_json
116
+ assert_json "{\"band\":{\"name\":\"Bodyjar\"}}", band.to_json
117
117
  assert_xml_equal "<band><name>Bodyjar</name></band>", band.to_xml
118
118
  end
119
119
 
@@ -130,7 +130,7 @@ class RepresentableTest < MiniTest::Spec
130
130
 
131
131
  @song = Song.new("Days Go By")
132
132
  assert_xml_equal "<song name=\"Days Go By\"/>", @song.extend(SongXmlRepresenter).to_xml
133
- assert_equal "{\"name\":\"Days Go By\"}", @song.extend(SongJsonRepresenter).to_json
133
+ assert_json "{\"name\":\"Days Go By\"}", @song.extend(SongJsonRepresenter).to_json
134
134
  end
135
135
  end
136
136
 
@@ -310,17 +310,32 @@ class RepresentableTest < MiniTest::Spec
310
310
  assert_equal({"name"=>"No One's Choice","groupies"=>false}, @band.send(:create_representation_with, {}, {}, Representable::JSON))
311
311
  end
312
312
 
313
- it "includes nil attribute when :render_nil is true" do
314
- mod = Module.new do
315
- include Representable::JSON
316
- property :name
317
- property :groupies, :render_nil => true
313
+ describe "when :render_nil is true" do
314
+ it "includes nil attribute" do
315
+ mod = Module.new do
316
+ include Representable::JSON
317
+ property :name
318
+ property :groupies, :render_nil => true
319
+ end
320
+
321
+ @band.extend(mod) # FIXME: use clean object.
322
+ @band.groupies = nil
323
+ hash = @band.send(:create_representation_with, {}, {}, Representable::JSON)
324
+ assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
318
325
  end
319
326
 
320
- @band.extend(mod) # FIXME: use clean object.
321
- @band.groupies = nil
322
- hash = @band.send(:create_representation_with, {}, {}, Representable::JSON)
323
- assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
327
+ it "includes nil attribute without extending" do
328
+ mod = Module.new do
329
+ include Representable::JSON
330
+ property :name
331
+ property :groupies, :render_nil => true, :extend => BandRepresentation
332
+ end
333
+
334
+ @band.extend(mod) # FIXME: use clean object.
335
+ @band.groupies = nil
336
+ hash = @band.send(:create_representation_with, {}, {}, Representable::JSON)
337
+ assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
338
+ end
324
339
  end
325
340
  end
326
341
 
@@ -24,7 +24,7 @@ class Song
24
24
  def initialize(name=nil)
25
25
  @name = name
26
26
  end
27
-
27
+
28
28
  def ==(other)
29
29
  name == other.name
30
30
  end
@@ -36,6 +36,16 @@ module XmlHelper
36
36
  end
37
37
  end
38
38
 
39
+ module AssertJson
40
+ module Assertions
41
+ def assert_json(expected, actual, msg=nil)
42
+ msg = message(msg, "") { diff expected, actual }
43
+ assert(expected.split("").sort == actual.split("").sort, msg)
44
+ end
45
+ end
46
+ end
47
+
39
48
  MiniTest::Spec.class_eval do
49
+ include AssertJson::Assertions
40
50
  include XmlHelper
41
51
  end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class TestHelperTest < MiniTest::Spec
4
+ describe "#assert_json" do
5
+ it "tests for equality" do
6
+ assert_json "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}"
7
+ end
8
+
9
+ it "allows different key orders" do
10
+ assert_json "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}"
11
+ end
12
+
13
+ it "complains when expected hash is subset" do
14
+ assert_raises MiniTest::Assertion do
15
+ assert_json "{\"songs\":{\"one\":\"65\"}}", "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}"
16
+ end
17
+ end
18
+
19
+ it "complains when source hash is subset" do
20
+ assert_raises MiniTest::Assertion do
21
+ assert_json "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}", "{\"songs\":{\"one\":\"65\"}}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -11,7 +11,9 @@ class Band
11
11
  end
12
12
  end
13
13
 
14
-
14
+ class Album
15
+ attr_accessor :songs
16
+ end
15
17
 
16
18
 
17
19
  class XmlTest < MiniTest::Spec
@@ -319,14 +321,14 @@ class CollectionTest < MiniTest::Spec
319
321
 
320
322
 
321
323
  describe ":from" do
322
- class Album
323
- include Representable::XML
324
- collection :songs, :from => :song
325
- attr_accessor :songs
326
- end
324
+ let(:xml) {
325
+ Module.new do
326
+ include Representable::XML
327
+ collection :songs, :from => :song
328
+ end }
327
329
 
328
330
  it "collects untyped items" do
329
- album = Album.from_xml(%{
331
+ album = Album.new.extend(xml).from_xml(%{
330
332
  <album>
331
333
  <song>Two Kevins</song>
332
334
  <song>Wright and Rong</song>
@@ -336,6 +338,46 @@ class CollectionTest < MiniTest::Spec
336
338
  assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
337
339
  end
338
340
  end
341
+
342
+
343
+ describe ":wrap" do
344
+ let (:album) { Album.new.extend(xml) }
345
+ let (:xml) {
346
+ Module.new do
347
+ include Representable::XML
348
+ collection :songs, :from => :song, :wrap => :songs
349
+ end }
350
+
351
+ describe "#from_xml" do
352
+ it "finds items in wrapped collection" do
353
+ album.from_xml(%{
354
+ <album>
355
+ <songs>
356
+ <song>Two Kevins</song>
357
+ <song>Wright and Rong</song>
358
+ <song>Laundry Basket</song>
359
+ </songs>
360
+ </album>
361
+ })
362
+ assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
363
+ end
364
+ end
365
+
366
+ describe "#to_xml" do
367
+ it "wraps items" do
368
+ album.songs = ["Laundry Basket", "Two Kevins", "Wright and Rong"]
369
+ assert_xml_equal %{
370
+ <album>
371
+ <songs>
372
+ <song>Laundry Basket</song>
373
+ <song>Two Kevins</song>
374
+ <song>Wright and Rong</song>
375
+ </songs>
376
+ </album>
377
+ }, album.to_xml
378
+ end
379
+ end
380
+ end
339
381
 
340
382
  require 'representable/xml/hash'
341
383
  describe "XML::AttributeHash" do # TODO: move to HashTest.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-09 00:00:00.000000000 Z
12
+ date: 2012-08-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -155,6 +155,7 @@ files:
155
155
  - LICENSE
156
156
  - README.rdoc
157
157
  - Rakefile
158
+ - gemfiles/Gemfile.mongoid-2.4
158
159
  - lib/representable.rb
159
160
  - lib/representable/binding.rb
160
161
  - lib/representable/bindings/json_bindings.rb
@@ -179,6 +180,7 @@ files:
179
180
  - test/polymorphic_test.rb
180
181
  - test/representable_test.rb
181
182
  - test/test_helper.rb
183
+ - test/test_helper_test.rb
182
184
  - test/xml_bindings_test.rb
183
185
  - test/xml_test.rb
184
186
  homepage: http://representable.apotomo.de