representable 1.2.9 → 1.3.0

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