representable 1.2.9 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -146,8 +146,9 @@ module JsonTest
146
146
 
147
147
  describe "#representable_bindings_for" do
148
148
  it "returns bindings for each property" do
149
- assert_equal 2, @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding).size
150
- assert_equal "name", @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding).first.name
149
+ bins = @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding, {})
150
+ assert_equal 2, bins.size
151
+ assert_equal "name", bins.first.name
151
152
  end
152
153
  end
153
154
  end
@@ -1,15 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class RepresentableTest < MiniTest::Spec
4
- def self.representer!(name=:representer, &block)
5
- let(name) do
6
- Module.new do
7
- include Representable::Hash
8
- instance_exec(&block)
9
- end
10
- end
11
- end
12
-
13
4
  class Band
14
5
  include Representable
15
6
  property :name
@@ -257,10 +248,6 @@ class RepresentableTest < MiniTest::Spec
257
248
  assert_equal nil, @band.groupies
258
249
  end
259
250
 
260
- it "still accepts deprecated :except option" do # FIXME: remove :except option.
261
- assert_equal @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:except => [:groupies]}, Representable::Hash::PropertyBinding), @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
262
- end
263
-
264
251
  it "accepts :include option" do
265
252
  @band.update_properties_from({"name"=>"No One's Choice", "groupies"=>2}, {:include => [:groupies]}, Representable::Hash::PropertyBinding)
266
253
  assert_equal 2, @band.groupies
@@ -282,6 +269,13 @@ class RepresentableTest < MiniTest::Spec
282
269
  @band.update_properties_from({"groupies"=>false}, {}, Representable::Hash::PropertyBinding)
283
270
  assert_equal false, @band.groupies
284
271
  end
272
+
273
+ it "ignores properties not present in the incoming document" do
274
+ @band.instance_eval do
275
+ def name=(*); raise "I should never be called!"; end
276
+ end
277
+ @band.update_properties_from({}, {}, Representable::Hash::PropertyBinding)
278
+ end
285
279
 
286
280
  it "ignores (no-default) properties not present in the incoming document" do
287
281
  { Representable::JSON => [{}, Representable::Hash::PropertyBinding],
@@ -302,6 +296,39 @@ class RepresentableTest < MiniTest::Spec
302
296
  assert_equal nil, @band.name, "Failed in #{format}"
303
297
  end
304
298
  end
299
+
300
+ describe "passing options" do
301
+ module TrackRepresenter
302
+ include Representable::Hash
303
+ property :nr
304
+
305
+ def to_hash(options)
306
+ @nr = options[:nr]
307
+ super
308
+ end
309
+ def from_hash(data, options)
310
+ super
311
+ @nr = options[:nr]
312
+ end
313
+ attr_accessor :nr
314
+ end
315
+
316
+ representer! do
317
+ property :track, :extend => TrackRepresenter
318
+ end
319
+
320
+ describe "#to_hash" do
321
+ it "propagates to nested objects" do
322
+ Song.new("Ocean Song", Object.new).extend(representer).to_hash(:nr => 9).must_equal({"track"=>{"nr"=>9}})
323
+ end
324
+ end
325
+
326
+ describe "#from_hash" do
327
+ it "propagates to nested objects" do
328
+ Song.new.extend(representer).from_hash({"track"=>{"nr" => "replace me"}}, :nr => 9).track.must_equal 9
329
+ end
330
+ end
331
+ end
305
332
  end
306
333
 
307
334
  describe "#create_representation_with" do
@@ -320,10 +347,6 @@ class RepresentableTest < MiniTest::Spec
320
347
  assert_equal({"name"=>"No One's Choice"}, hash)
321
348
  end
322
349
 
323
- it "still accepts deprecated :except option" do # FIXME: remove :except option.
324
- assert_equal @band.send(:create_representation_with, {}, {:except => [:groupies]}, Representable::Hash::PropertyBinding), @band.send(:create_representation_with, {}, {:exclude => [:groupies]}, Representable::Hash::PropertyBinding)
325
- end
326
-
327
350
  it "accepts :include option" do
328
351
  hash = @band.send(:create_representation_with, {}, {:include => [:groupies]}, Representable::Hash::PropertyBinding)
329
352
  assert_equal({"groupies"=>2}, hash)
@@ -375,6 +398,19 @@ class RepresentableTest < MiniTest::Spec
375
398
  assert_equal({"name"=>"No One's Choice", "groupies" => nil}, hash)
376
399
  end
377
400
  end
401
+
402
+ it "does not propagate private options to nested objects" do
403
+ cover_rpr = Module.new do
404
+ include Representable::Hash
405
+ property :title
406
+ property :original, :extend => self
407
+ end
408
+
409
+ # FIXME: we should test all representable-options (:include, :exclude, ?)
410
+
411
+ Class.new(OpenStruct).new(:title => "Roxanne", :original => Class.new(OpenStruct).new(:title => "Roxanne (Don't Put On The Red Light)")).extend(cover_rpr).
412
+ to_hash(:include => [:original]).must_equal({"original"=>{"title"=>"Roxanne (Don't Put On The Red Light)"}})
413
+ end
378
414
  end
379
415
 
380
416
  describe ":if" do
@@ -416,11 +452,11 @@ class RepresentableTest < MiniTest::Spec
416
452
  describe ":extend and :class" do
417
453
  module UpcaseRepresenter
418
454
  def to_hash(*); upcase; end
419
- def from_hash(hsh); self.class.new hsh.upcase; end # DISCUSS: from_hash must return self.
455
+ def from_hash(hsh, *args); self.class.new hsh.upcase; end # DISCUSS: from_hash must return self.
420
456
  end
421
457
  module DowncaseRepresenter
422
458
  def to_hash(*); downcase; end
423
- def from_hash(hsh); hsh.downcase; end
459
+ def from_hash(hsh, *args); hsh.downcase; end
424
460
  end
425
461
  class UpcaseString < String; end
426
462
 
@@ -487,7 +523,7 @@ class RepresentableTest < MiniTest::Spec
487
523
 
488
524
  describe "when :class lambda returns nil" do
489
525
  representer! do
490
- property :name, :extend => lambda { |name| Module.new { def from_hash(data); data; end } },
526
+ property :name, :extend => lambda { |name| Module.new { def from_hash(data, *args); data; end } },
491
527
  :class => nil
492
528
  end
493
529
 
@@ -53,4 +53,13 @@ end
53
53
  MiniTest::Spec.class_eval do
54
54
  include AssertJson::Assertions
55
55
  include XmlHelper
56
+
57
+ def self.representer!(format=Representable::Hash, name=:representer, &block)
58
+ let(name) do
59
+ Module.new do
60
+ include format
61
+ instance_exec(&block)
62
+ end
63
+ end
64
+ end
56
65
  end
@@ -378,41 +378,63 @@ class CollectionTest < MiniTest::Spec
378
378
  end
379
379
  end
380
380
  end
381
-
381
+
382
+ require 'representable/xml/collection'
383
+ class CollectionRepresenterTest < MiniTest::Spec
384
+ module SongRepresenter
385
+ include Representable::XML
386
+ property :name
387
+ end
388
+
389
+ describe "XML::Collection" do
390
+ describe "with contained objects" do
391
+ representer!(Representable::XML::Collection) do
392
+ items :class => Song, :extend => SongRepresenter
393
+ self.representation_wrap= :songs
394
+ end
395
+
396
+ it "renders objects with #to_xml" do
397
+ assert_xml_equal "<songs><song><name>Days Go By</name></song><song><name>Can't Take Them All</name></song></songs>", [Song.new("Days Go By"), Song.new("Can't Take Them All")].extend(representer).to_xml
398
+ end
399
+
400
+ it "returns objects array from #from_xml" do
401
+ assert_equal [Song.new("Days Go By"), Song.new("Can't Take Them All")], [].extend(representer).from_xml("<songs><song><name>Days Go By</name></song><song><name>Can't Take Them All</name></song></songs>")
402
+ end
403
+ end
404
+ end
405
+ end
406
+
382
407
  require 'representable/xml/hash'
383
408
  describe "XML::AttributeHash" do # TODO: move to HashTest.
384
- before do
385
- @songs_representer = Module.new do
386
- include Representable::XML::AttributeHash
387
- self.representation_wrap= :favs
388
- end
409
+ representer!(Representable::XML::AttributeHash) do
410
+ self.representation_wrap= :favs
389
411
  end
390
412
 
391
413
  describe "#to_xml" do
392
414
  it "renders values into attributes converting values to strings" do
393
- assert_xml_equal "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml
415
+ assert_xml_equal "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(representer).to_xml
394
416
  end
395
417
 
396
418
  it "respects :exclude" do
397
- assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml(:exclude => [:one])
419
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(representer).to_xml(:exclude => [:one])
398
420
  end
399
421
 
400
422
  it "respects :include" do
401
- assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml(:include => [:two])
423
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(representer).to_xml(:include => [:two])
402
424
  end
403
425
  end
404
426
 
405
427
  describe "#from_json" do
406
428
  it "returns hash" do
407
- assert_equal({"one" => "Graveyards", "two" => "Can't Take Them All"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />"))
429
+ assert_equal({"one" => "Graveyards", "two" => "Can't Take Them All"}, {}.extend(representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />"))
408
430
  end
409
431
 
410
432
  it "respects :exclude" do
411
- assert_equal({"two" => "Can't Take Them All"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :exclude => [:one]))
433
+ assert_equal({"two" => "Can't Take Them All"}, {}.extend(representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :exclude => [:one]))
412
434
  end
413
435
 
414
436
  it "respects :include" do
415
- assert_equal({"one" => "Graveyards"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :include => [:one]))
437
+ assert_equal({"one" => "Graveyards"}, {}.extend(representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :include => [:one]))
416
438
  end
417
439
  end
418
440
  end
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.9
4
+ version: 1.3.0
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: 2013-01-04 00:00:00.000000000 Z
12
+ date: 2013-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -148,12 +148,10 @@ extensions: []
148
148
  extra_rdoc_files: []
149
149
  files:
150
150
  - .gitignore
151
- - .rspec
152
151
  - .travis.yml
153
152
  - CHANGES.textile
154
153
  - Gemfile
155
- - LICENSE
156
- - README.rdoc
154
+ - README.md
157
155
  - Rakefile
158
156
  - TODO
159
157
  - gemfiles/Gemfile.mongoid-2.4
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --colour
data/LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,416 +0,0 @@
1
- = Representable
2
-
3
- <em>Maps documents to Ruby objects and back.</em>
4
-
5
-
6
- == Introduction
7
-
8
- _Representable_ maps fragments in documents to attributes in Ruby objects and back. It allows parsing representations giving an object-oriented interface to the document. But that's only half of it! Representable can also render documents from an object instance.
9
-
10
- This keeps your representation knowledge in one place when implementing REST services and clients.
11
-
12
-
13
- == Features
14
-
15
- * Bidirectional - rendering and parsing
16
- * OOP access to documents
17
- * Support for JSON, XML and YAML.
18
- * Coercion support with virtus[https://github.com/solnic/virtus]
19
-
20
-
21
- == Example
22
-
23
- Since you keep forgetting the heroes of your childhood you decide to implement a REST service for storing and querying those. You choose representable for handling representations.
24
-
25
- gem 'representable'
26
-
27
-
28
- == Defining Representations
29
-
30
- Representations are usually defined using a module. This makes them super flexibly, you'll see.
31
-
32
- require 'representable/json'
33
-
34
- module HeroRepresenter
35
- include Representable::JSON
36
-
37
- property :forename
38
- property :surename
39
- end
40
-
41
- By using #property we declare two simple attributes that should be considered when representing.
42
-
43
- To use your representer include it in the matching class. Note that you could reuse a representer in multiple classes. The represented class must have getter and setter methods for each property.
44
-
45
- class Hero
46
- attr_accessor :forename, :surename
47
-
48
- include Representable
49
- include HeroRepresenter
50
- end
51
-
52
- Many people dislike including representers on class layer. You might also extend an object at runtime.
53
-
54
- Hero.new.extend(HeroRepresenter)
55
-
56
- Alternatively, if you don't like modules (which you shouldn't), declarations can be put into classes directly. We call that inline representers.
57
-
58
- class Hero
59
- attr_accessor :forename, :surename
60
-
61
- include Representable::JSON
62
-
63
- property :forename
64
- property :surename
65
- end
66
-
67
-
68
- == Rendering
69
-
70
- Now let's create and render our first hero.
71
-
72
- peter = Hero.new
73
- peter.forename = "Peter"
74
- peter.surename = "Pan"
75
-
76
- peter.to_json
77
- #=> {"forename":"Peter","surename":"Pan"}
78
-
79
- Those two properties are considered when rendering in #to_json.
80
-
81
- == Parsing
82
-
83
- The cool thing about Representable is: it works bidirectional. The document definition in your representer module can also be used for parsing documents and assigning property values.
84
-
85
- hook = Hero.from_json('{"forename":"Captain","surename":"Hook"}')
86
- hook.forename #=> "Captain"
87
-
88
- See how easy this is? You can use an object-oriented method to read from the document.
89
-
90
- == Nesting
91
-
92
- You need a second domain object. Every hero has a place it comes from.
93
-
94
- class Location
95
- attr_accessor :title
96
-
97
- include Representable::JSON
98
-
99
- property :title
100
- end
101
-
102
- Peter, where ya' from?
103
-
104
- neverland = Location.new
105
- neverland.title = "Neverland"
106
-
107
- It makes sense to embed the location in the hero's document.
108
-
109
- module HeroRepresenter
110
- property :origin, :class => Location
111
- end
112
-
113
- Using the +:class+ option allows you to include other representable objects.
114
-
115
- peter.origin = neverland
116
- peter.to_json
117
- #=> {"forename":"Peter","surename":"Pan","origin":{"title":"Neverland"}}
118
-
119
-
120
- == Parsing Nested Documents
121
-
122
- Don't forget how easy it is to parse nested representations.
123
-
124
- hook = Hero.from_json('{"name":"Captain","surename":"Hook","origin":{"title":"Dark Ocean"}}')
125
- hook.origin.inspect #=> #<Location:0x910d7c8 @title="Dark Ocean">
126
- hook.origin.title #=> "Dark Ocean"
127
-
128
- Representable just creates objects from the parsed document - nothing more and nothing less.
129
-
130
- == Simple Collections
131
-
132
- Heroes have features, special abilities that make 'em a superhero.
133
-
134
- module HeroRepresenter
135
- collection :features
136
- end
137
-
138
- The second representable API method is +collection+ and, well, declares a collection.
139
-
140
- peter.features = ["stays young", "can fly"]
141
- peter.to_json
142
- #=> {"forename":"Peter","surename":"Pan","origin":{"title":"Neverland"},"features":["stays young","can fly"]}
143
-
144
-
145
- == Typed Collections
146
-
147
- Ok, things start working out. Your hero has a name, an origin and a list of features so far. Why not allow adding buddies to Peter - nobody wants to be alone!
148
-
149
- module HeroRepresenter
150
- collection :friends, :class => Hero
151
- end
152
-
153
- Again, we type the collection by using the +:class+ option.
154
-
155
- nick = Hero.new
156
- nick.forename = "Nick"
157
-
158
- el = Hero.new
159
- el.forename = "El"
160
-
161
- peter.friends = [nick, el]
162
-
163
- I always wanted to be Peter's bro... in this example it is possible!
164
-
165
- peter.to_json
166
- #=> {"forename":"Peter","surename":"Pan","origin":{"title":"Neverland"},"features":["stays young","can fly"],"friends":[{"name":"Nick"},{"name":"El"}]}
167
-
168
-
169
- == Hashes
170
-
171
- Hashes can be represented the same way collections work. Here, use the #hash class method.
172
-
173
- == Lonely Collections
174
-
175
- Need an array represented without any wrapping?
176
-
177
- ["stays young", "can fly"].extend(Representable::JSON::Collection).to_json
178
- #=> "[\"stays young\", \"can fly\"]"
179
-
180
- You can use #items to configure the element representations contained in the array.
181
-
182
- module FeaturesRepresenter
183
- include Representable::JSON::Collection
184
-
185
- items :class => Hero, :extend => HeroRepresenter
186
- end
187
-
188
- Collections and hashes can also be deserialized. Note that this also works for XML.
189
-
190
- == Lonely Hashes
191
-
192
- The same goes with hashes where #values lets you configure the hash's values.
193
-
194
- module FriendsRepresenter
195
- include Representable::JSON::Hash
196
-
197
- values :class => Hero, :extend => HeroRepresenter
198
- end
199
-
200
- {:stu => Hero.new("Stu"), :clive => Hero.new("Cleavage")}.extend(FriendsRepresenter).to_json
201
-
202
- In XML, if you want to store hash attributes in tag attributes instead of dedicated nodes, use XML::AttributeHash.
203
-
204
-
205
- == Customizing
206
-
207
- === Wrapping
208
-
209
- Representable is designed to be very simple. However, a few tweaks are available. What if you want to wrap your document?
210
-
211
- module HeroRepresenter
212
- self.representation_wrap = true
213
- end
214
-
215
- peter.to_json #=> {"hero":{"name":"Peter","surename":"Pan"}}
216
-
217
- You can also provide a custom wrapper.
218
-
219
- module HeroRepresenter
220
- self.representation_wrap = :boy
221
- end
222
-
223
- peter.to_json #=> {"boy":{"name":"Peter","surename":"Pan"}}
224
-
225
-
226
- === Mapping
227
-
228
- If your accessor name doesn't match the attribute name in the document, use the +:from+ matcher.
229
-
230
- module HeroRepresenter
231
- property :forename, :from => :i_am_called
232
- end
233
-
234
- peter.to_json #=> {"i_am_called":"Peter","surename":"Pan"}
235
-
236
-
237
- === Filtering and Conditions
238
-
239
- Representable allows you to skip and include properties when rendering or parsing.
240
-
241
- peter.to_json(:include => :forename)
242
- #=> {"forename":"Peter"}
243
-
244
- It gives you convenient +:exclude+ and +:include+ options.
245
-
246
- You can also define conditions on properties on the class layer.
247
-
248
- module HeroRepresenter
249
- property :friends, :if => lambda { forename == "Peter" }
250
- end
251
-
252
- When rendering or parsing, the +friends+ property is considered only if the condition block evals to true. Note that the block is executed in instance context, giving you access to instance methods.
253
-
254
- === False and Nil Values
255
-
256
- Since 1.2 +false+ values are considered when parsing and rendering. That particularly means properties that used to be unset (i.e. +nil+) after parsing might be +false+ now. Vice versa, +false+ values that weren't included in the rendered document will be visible now.
257
-
258
- If you want +nil+ values to be included when rendering, use the +:render_nil+ option.
259
-
260
- property :surename, :render_nil => true
261
-
262
- == DCI
263
-
264
- Representers roughly follow the {DCI}[http://en.wikipedia.org/wiki/Data,_context_and_interaction] pattern when used on objects, only.
265
-
266
- Hero.new.extend(HeroRepresenter)
267
-
268
- The only difference is that you have to define which representers to use for typed properties.
269
-
270
- module HeroRepresenter
271
- property :forename
272
- property :surename
273
- collection :features
274
- property :origin, :class => Location
275
- collection :friends, :class => Hero, :extend => HeroRepresenter
276
- end
277
-
278
- There's no need to specify a representer for the +origin+ property since the +Location+ class statically includes its representation. For +friends+, we can use +:extend+ to tell representable which module to mix in dynamically.
279
-
280
- == XML support
281
-
282
- Representable allows declaring a document's syntax and structure while having different formats. Currently, it ships with JSON, XML and YAML bindings.
283
-
284
- class Hero
285
- include Representable::XML
286
- end
287
-
288
- peter.to_xml
289
- #=> <hero>
290
- <name>Peter</name>
291
- <surename>Pan</surename>
292
- <location>
293
- <title>Neverland</title>
294
- </location>
295
- <hero>
296
- <name>Nick</name>
297
- </hero>
298
- <hero>
299
- <name>El</name>
300
- </hero>
301
- </hero>
302
-
303
- The #to_xml method gives us an XML representation of Peter - great!
304
-
305
- === Mapping tag attributes
306
-
307
- You can also map properties to tag attributes in representable.
308
-
309
- class Hero
310
- attr_accessor :name
311
- include Representable::XML
312
- property :name, :attribute => true
313
- end
314
-
315
- Hero.new(:name => "Peter Pan").to_xml
316
- #=> <hero name="Peter Pan" />
317
-
318
- Naturally, this works for both ways.
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>
340
-
341
-
342
- == YAML Support
343
-
344
- Representers also come in handy if you need to render or parse YAML. The YAML module works exactly like the others.
345
-
346
- module HotBandsRepresenter
347
- include Representable::YAML
348
-
349
- property :for
350
- collection :names
351
- end
352
-
353
- Now, just call #to_yaml to render or #from_yaml to parse.
354
-
355
- HotBands.new(:for => "Nick", :names => ["Bad Religion", "Van Halen", "Mozart"]).
356
- extend(HotBandsRepresenter).
357
- to_yaml
358
-
359
- #=> ---
360
- for: Nick
361
- names:
362
- - Bad Religion
363
- - Van Halen
364
- - Mozart
365
-
366
- === Nested Objects
367
-
368
- The YAML parser does handle nested objects just like JSON and XML does it.
369
-
370
- === Flow Style Lists
371
-
372
- If you want flow style (aka inline style) lists, use the :style option. See http://www.yaml.org/spec/1.2/spec.html#id2790088 for more infos on flow sequences.
373
-
374
- module HotBandsRepresenter
375
- include Representable::YAML
376
-
377
- collection :names, :style => :flow
378
- end
379
-
380
- #=> ---
381
- names: [Bad Religion, Van Halen, Mozart]
382
-
383
- Need anything else for YAML? Let me know.
384
-
385
- == Coercion
386
-
387
- If you fancy coercion when parsing a document you can use the Coercion module which uses virtus[https://github.com/solnic/virtus] for type conversion.
388
-
389
- Include virtus in your Gemfile, first. Be sure to include virtus 0.5.0 or greater.
390
-
391
- gem 'virtus'
392
-
393
- Use the +:type+ option to specify the conversion target. Note that +:default+ still works.
394
-
395
- module HeroRepresenter
396
- include Representable::JSON
397
- include Virtus
398
- include Representable::Coercion
399
-
400
- property :born_at, :type => DateTime, :default => "May 12th, 2012"
401
- end
402
-
403
-
404
- == More
405
-
406
- Instead of spreading knowledge about your representations about the entire framework, Representable keeps rendering and parsing representations in one single, testable asset. It is a new abstraction layer missing in many "RESTful" frameworks.
407
-
408
- Representable was written with REST representations in mind. However, it is a generic module for working with documents. If you do consider using it for a REST project, check out the {Roar framework}[http://github.com/apotonick/roar], which comes with representers, built-in hypermedia support and more. It internally uses Representable and streamlines the process for building hypermedia-driven REST applications.
409
-
410
-
411
- == Copyright
412
-
413
- Representable is a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his inspiring work.
414
-
415
- * Copyright (c) 2011 Nick Sutterer <apotonick@gmail.com>
416
- * ROXML is Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom.