representable 2.4.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -12
  3. data/CHANGES.md +6 -27
  4. data/README.md +28 -1326
  5. data/lib/representable.rb +4 -14
  6. data/lib/representable/binding.rb +3 -7
  7. data/lib/representable/definition.rb +1 -2
  8. data/lib/representable/populator.rb +35 -0
  9. data/lib/representable/version.rb +1 -1
  10. data/test/definition_test.rb +0 -7
  11. data/test/exec_context_test.rb +2 -2
  12. data/test/instance_test.rb +0 -19
  13. data/test/realistic_benchmark.rb +0 -13
  14. data/test/representable_test.rb +0 -16
  15. data/test/skip_test.rb +1 -1
  16. data/test/test_helper.rb +0 -2
  17. metadata +3 -65
  18. data/lib/representable/deprecations.rb +0 -127
  19. data/lib/representable/parse_strategies.rb +0 -93
  20. data/test-with-deprecations/as_test.rb +0 -65
  21. data/test-with-deprecations/benchmarking.rb +0 -83
  22. data/test-with-deprecations/binding_test.rb +0 -46
  23. data/test-with-deprecations/blaaaaaaaa_test.rb +0 -69
  24. data/test-with-deprecations/cached_test.rb +0 -147
  25. data/test-with-deprecations/class_test.rb +0 -119
  26. data/test-with-deprecations/coercion_test.rb +0 -52
  27. data/test-with-deprecations/config/inherit_test.rb +0 -135
  28. data/test-with-deprecations/config_test.rb +0 -122
  29. data/test-with-deprecations/decorator_scope_test.rb +0 -28
  30. data/test-with-deprecations/decorator_test.rb +0 -96
  31. data/test-with-deprecations/default_test.rb +0 -34
  32. data/test-with-deprecations/defaults_options_test.rb +0 -93
  33. data/test-with-deprecations/definition_test.rb +0 -264
  34. data/test-with-deprecations/example.rb +0 -310
  35. data/test-with-deprecations/examples/object.rb +0 -31
  36. data/test-with-deprecations/exec_context_test.rb +0 -93
  37. data/test-with-deprecations/features_test.rb +0 -70
  38. data/test-with-deprecations/filter_test.rb +0 -57
  39. data/test-with-deprecations/for_collection_test.rb +0 -74
  40. data/test-with-deprecations/generic_test.rb +0 -116
  41. data/test-with-deprecations/getter_setter_test.rb +0 -21
  42. data/test-with-deprecations/hash_bindings_test.rb +0 -87
  43. data/test-with-deprecations/hash_test.rb +0 -160
  44. data/test-with-deprecations/heritage_test.rb +0 -62
  45. data/test-with-deprecations/if_test.rb +0 -79
  46. data/test-with-deprecations/include_exclude_test.rb +0 -88
  47. data/test-with-deprecations/inherit_test.rb +0 -159
  48. data/test-with-deprecations/inline_test.rb +0 -272
  49. data/test-with-deprecations/instance_test.rb +0 -266
  50. data/test-with-deprecations/is_representable_test.rb +0 -77
  51. data/test-with-deprecations/json_test.rb +0 -355
  52. data/test-with-deprecations/lonely_test.rb +0 -239
  53. data/test-with-deprecations/mongoid_test.rb +0 -31
  54. data/test-with-deprecations/nested_test.rb +0 -115
  55. data/test-with-deprecations/object_test.rb +0 -60
  56. data/test-with-deprecations/parse_pipeline_test.rb +0 -64
  57. data/test-with-deprecations/parse_strategy_test.rb +0 -279
  58. data/test-with-deprecations/pass_options_test.rb +0 -27
  59. data/test-with-deprecations/pipeline_test.rb +0 -277
  60. data/test-with-deprecations/populator_test.rb +0 -105
  61. data/test-with-deprecations/prepare_test.rb +0 -67
  62. data/test-with-deprecations/private_options_test.rb +0 -18
  63. data/test-with-deprecations/reader_writer_test.rb +0 -19
  64. data/test-with-deprecations/realistic_benchmark.rb +0 -115
  65. data/test-with-deprecations/render_nil_test.rb +0 -21
  66. data/test-with-deprecations/represent_test.rb +0 -88
  67. data/test-with-deprecations/representable_test.rb +0 -511
  68. data/test-with-deprecations/schema_test.rb +0 -148
  69. data/test-with-deprecations/serialize_deserialize_test.rb +0 -33
  70. data/test-with-deprecations/skip_test.rb +0 -81
  71. data/test-with-deprecations/stringify_hash_test.rb +0 -41
  72. data/test-with-deprecations/test_helper.rb +0 -135
  73. data/test-with-deprecations/test_helper_test.rb +0 -25
  74. data/test-with-deprecations/uncategorized_test.rb +0 -67
  75. data/test-with-deprecations/user_options_test.rb +0 -15
  76. data/test-with-deprecations/wrap_test.rb +0 -152
  77. data/test-with-deprecations/xml_bindings_test.rb +0 -62
  78. data/test-with-deprecations/xml_test.rb +0 -503
  79. data/test-with-deprecations/yaml_test.rb +0 -162
  80. data/test/parse_strategy_test.rb +0 -279
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b19d4a33624ba313d7946747a4f78b16122b8d02
4
- data.tar.gz: 382bd53131b372323d58b33b9c1682daad3491ae
3
+ metadata.gz: 5caccf4a60cbe2ca6066a047755a13a741b488b1
4
+ data.tar.gz: aa57e2ec29209f936b58642ac84cfdf223e6d09b
5
5
  SHA512:
6
- metadata.gz: 2afc311d2eab1ceae04791a29cb1e5e6985ada191fd1011e20793b32a99fe81a41bbe481cbd5768846bea7218a9905f69765cbba0ae18f48b6a154fd36495261
7
- data.tar.gz: 938e62bcd83be59c3ec8f1158a1e603a28ebb5e047da24acff853bbdc98a29fdc7298e535d8ff9aac5ccec1f6c96d8358d24e26581cdf4363623959b78423fda
6
+ metadata.gz: 9c5047e0c91d61a43677bd92cc64fbee0e34292b3b24ea59d10e2962075626c8c6dddd4b64c21092f65ed4d3abcfa1458f318bfd4d179085052c53565eb1d8ec
7
+ data.tar.gz: be1d272ce967973dbac844beb02f7d1418adb9f1c59b02ca9472a912bde994ffa7543158c3f8543ae5a172dda49c1f838ce963e5413e5c83b204bddd8aa9366c
@@ -1,12 +1,7 @@
1
- notifications:
2
- # irc: "irc.freenode.org#cells"
3
- matrix:
4
- include:
5
- - rvm: 1.9.3
6
- - rvm: 2.0.0
7
- - rvm: 2.1.1
8
- - rvm: 2.2.0
9
- # FIXME: add rbx and jruby, again, fix ruby-prof.
10
- # - rvm: rbx-2.2.10
11
- # - rvm: jruby-19mode
12
-
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.1
5
+ - 2.2.3
6
+ before_install:
7
+ - gem install bundler
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 3.0.0
2
+
3
+ * Removed deprecations from 2.4.
4
+ * Removed `:parse_strategy` in favor of `:populator`.
5
+ * Removed `:binding` in favor of your own pipeline.
6
+
1
7
  # 2.4.1
2
8
 
3
9
  * No need to use Uber::Callable in Pipeline as this object is always invoked via `#call`.
@@ -13,33 +19,6 @@ render_filter: val, doc, options
13
19
  representer_class.representable_attrs is definitions
14
20
  * Removed `:use_decorator` option. Use a decorator instead.
15
21
 
16
- TODO: DEPRECATE 4-args? also, allow options[:fragment] instead of options[:result] ?
17
-
18
-
19
- TODO: :representable should be removing Deserialize.
20
- deprecate Coercion, it sucks
21
- deprecate parse/render filter
22
-
23
- pipeline
24
- less ifs, instead: simply remove the "middleware"
25
- read_fragment
26
- represented.set instance.prepare.from_json
27
- Populator.(fragment)
28
- not_found?.default.deserialize.parse_filter.set
29
- Deserializer.(fragment)
30
- instance.class.prepare.deserialize=from_json
31
-
32
- OverwriteOnNil vs. StopOnNil
33
- [Instance, ->(){..}, Bla]
34
-
35
- did you ever overwrite #create_object in a binding? you can now simply pass a callable object into the pipeline.
36
- TODO: deprecate parse strategies in favour of :populator (analogue to Reform)
37
-
38
- elegant, easier to understand, and extend
39
- you know, the whole deserialize stack (same for rendering): you pass in the json, it finds the fragments for each property, passes it to the binding, the binding deserializes, builds/finds a model, assigns values to it, and so on
40
- you can partially hook in with :instance or :deserialize or :parse_filter, but it's really awkward. i am now changing the architecture to a pipeline, where you plug in the features you want (e.g. "do not call :prepare, :instance, :class, etc. but run my own :populator")
41
- and it suddenly is super simple to understand
42
-
43
22
  * Added `Representable.deprecations = false` to disable slow and weird deprecation code.
44
23
 
45
24
  * Removed `Binding#user_options`. This is now available via `->(options[:user_options])`.
data/README.md CHANGED
@@ -12,30 +12,9 @@ In other words: Take an object and decorate it with a representer module. This w
12
12
  Representable is helpful for all kind of mappings, rendering and parsing workflows. However, it is mostly useful in API code. Are you planning to write a real REST API with representable? Then check out the [Roar](http://github.com/apotonick/roar) gem first, save work and time and make the world a better place instead.
13
13
 
14
14
 
15
- ## Installation
16
-
17
- The representable gem runs with all Ruby versions >= 1.9.3.
18
-
19
- ```ruby
20
- gem 'representable'
21
- ```
22
-
23
- ### Dependencies
24
-
25
- Representable does a great job with JSON, it also features support for XML, YAML and pure ruby
26
- hashes. But Representable did not bundle dependencies for JSON and XML.
27
-
28
- If you want to use JSON, add the following to your Gemfile:
29
-
30
- ```ruby
31
- gem 'multi_json'
32
- ```
33
-
34
- If you want to use XML, add the following to your Gemfile:
15
+ ## Full Documentation
35
16
 
36
- ```ruby
37
- gem 'nokogiri'
38
- ```
17
+ Representable comes with a rich set of options and semantics for parsing and rendering documents. Its [full documentation](http://trailblazer.to/gems/representable/3.0/api.html) can be found on the Trailblazer site.
39
18
 
40
19
  ## Example
41
20
 
@@ -50,12 +29,14 @@ song = Song.new(title: "Fallout", track: 1)
50
29
 
51
30
  ## Defining Representations
52
31
 
53
- Representations are defined using representer modules.
32
+ Representations are defined using representer classes, called _decorator, or modules.
33
+
34
+ In these examples, let's use decorators
54
35
 
55
36
  ```ruby
56
37
  require 'representable/json'
57
38
 
58
- module SongRepresenter
39
+ class SongRepresenter < Representable::Decorator
59
40
  include Representable::JSON
60
41
 
61
42
  property :title
@@ -71,7 +52,7 @@ In the representer the #property method allows declaring represented attributes
71
52
  Mixing in the representer into the object adds a rendering method.
72
53
 
73
54
  ```ruby
74
- song.extend(SongRepresenter).to_json
55
+ SongRepresenter.new(song).to_json
75
56
  #=> {"title":"Fallout","track":1}
76
57
  ```
77
58
 
@@ -80,56 +61,11 @@ song.extend(SongRepresenter).to_json
80
61
  It also adds support for parsing.
81
62
 
82
63
  ```ruby
83
- song = Song.new.extend(SongRepresenter).from_json(%{ {"title":"Roxanne"} })
64
+ song = SongRepresenter.new(song).from_json(%{ {"title":"Roxanne"} })
84
65
  #=> #<Song title="Roxanne", track=nil>
85
66
  ```
86
67
 
87
- Note that parsing hashes per default does [require string keys](#symbol-keys-vs-string-keys) and does _not_ pick up symbol keys.
88
-
89
- ## Extend vs. Decorator
90
-
91
- If you don't want representer modules to be mixed into your objects (using `#extend`) you can use the `Decorator` strategy [described below](#decorator-vs-extend). Decorating instead of extending was introduced in 1.4.
92
-
93
-
94
- ## Aliasing
95
-
96
- If your property name doesn't match the name in the document, use the `:as` option.
97
-
98
- ```ruby
99
- module SongRepresenter
100
- include Representable::JSON
101
-
102
- property :title, as: :name
103
- property :track
104
- end
105
-
106
- song.to_json #=> {"name":"Fallout","track":1}
107
- ```
108
-
109
-
110
- ## Wrapping
111
-
112
- Let the representer know if you want wrapping.
113
-
114
- ```ruby
115
- module SongRepresenter
116
- include Representable::JSON
117
-
118
- self.representation_wrap= :hit
119
-
120
- property :title
121
- property :track
122
- end
123
- ```
124
-
125
- This will add a container for rendering and consuming.
126
-
127
- ```ruby
128
- song.extend(SongRepresenter).to_json
129
- #=> {"hit":{"title":"Fallout","track":1}}
130
- ```
131
-
132
- Setting `self.representation_wrap = true` will advice representable to figure out the wrap itself by inspecting the represented object class.
68
+ Note that parsing hashes per default does [require string keys](http://trailblazer.to/gems/representable/3.0/api.html#symbol-keys) and does _not_ pick up symbol keys.
133
69
 
134
70
 
135
71
  ## Collections
@@ -137,7 +73,7 @@ Setting `self.representation_wrap = true` will advice representable to figure ou
137
73
  Let's add a list of composers to the song representation.
138
74
 
139
75
  ```ruby
140
- module SongRepresenter
76
+ class SongRepresenter < Representable::Decorator
141
77
  include Representable::JSON
142
78
 
143
79
  property :title
@@ -172,143 +108,20 @@ album = Album.new(name: "The Police", songs: [song, Song.new(title: "Synchronici
172
108
  Here comes the representer that defines the composition.
173
109
 
174
110
  ```ruby
175
- module AlbumRepresenter
111
+ class AlbumRepresenter < Representable::Decorator
176
112
  include Representable::JSON
177
113
 
178
114
  property :name
179
- collection :songs, extend: SongRepresenter, class: Song
180
- end
181
- ```
182
-
183
- Note that nesting works with both plain `#property` and `#collection`.
184
-
185
- When rendering, the `:extend` module is used to extend the attribute(s) with the correct representer module.
186
-
187
- ```ruby
188
- album.extend(AlbumRepresenter).to_json
189
- #=> {"name":"The Police","songs":[{"title":"Fallout","composers":["Stewart Copeland","Sting"]},{"title":"Synchronicity","composers":[]}]}
190
- ```
191
-
192
- Parsing a documents needs both `:extend` and the `:class` option as the parser requires knowledge what kind of object to create from the nested composition.
193
-
194
- ```ruby
195
- Album.new.extend(AlbumRepresenter).
196
- from_json(%{{"name":"Offspring","songs":[{"title":"Genocide"},{"title":"Nitro","composers":["Offspring"]}]}})
197
-
198
- #=> #<Album name="Offspring", songs=[#<Song title="Genocide">, #<Song title="Nitro", composers=["Offspring"]>]>
199
- ```
200
-
201
- ## Suppressing Nested Wraps
202
-
203
- When reusing a representer for a nested document, you might want to suppress the wrap for the nested fragment.
204
-
205
- ```ruby
206
- module SongRepresenter
207
- include Representable::JSON
208
-
209
- self.representation_wrap = :songs
210
- property :title
211
- end
212
- ```
213
-
214
- When reusing `SongRepresenter` in a nested setup you can suppress the wrapping using the `:wrap` option.
215
-
216
- ```ruby
217
- module AlbumRepresenter
218
- include Representable::JSON
219
-
220
- collection :songs, extend: SongRepresenter, wrap: false
115
+ collection :songs, decorator: SongRepresenter, class: Song
221
116
  end
222
117
  ```
223
118
 
224
- The `representation_wrap` from the nested representer now won't be rendered and parsed.
225
-
226
- ```ruby
227
- album.extend(AlbumRepresenter).to_json #=> "{\"songs\": [{\"name\": \"Roxanne\"}]}"
228
- ```
229
-
230
- Note that this only works for JSON and Hash at the moment.
231
-
232
-
233
- ## Parse Strategies
234
-
235
- When parsing `collection`s (also applies to single `property`s), representable usually iterates the incoming list and creates a new object per array item.
236
-
237
- Parse strategies let you do that manually.
238
-
239
- ```ruby
240
- collection :songs, :parse_strategy => lambda { |fragment, i, options|
241
- songs << song = Song.new
242
- song
243
- }
244
- ```
245
-
246
- The above code will *add* a new `Song` per incoming item. Each instance will still be extended and populated with attributes (note that you can [change that](#skipping-rendering-or-parsing) as well).
247
-
248
- This gives you all the freedom you need for your nested parsing.
249
-
250
-
251
- ## Syncing Objects
252
-
253
- Usually, representable creates a new nested object when parsing. If you want to update an existing object, use the `parse_strategy: :sync` option.
254
-
255
- ```ruby
256
- module AlbumRepresenter
257
- include Representable::JSON
258
-
259
- collection :songs, extend: SongRepresenter, parse_strategy: :sync
260
- ```
261
-
262
- When parsing an album, it will now call `from_json` on the existing songs in the collection.
263
-
264
- ```ruby
265
- album = Album.find(1)
266
- album.songs.first #=> #<Song:0x999 title: "Panama">
267
- ```
268
-
269
- Note that the album already contains a song instance.
270
-
271
- ```ruby
272
- album.extend(AlbumRepresenter).
273
- from_json('{songs: [{title: "Eruption"}]}')
274
-
275
- album.songs.first #=> #<Song:0x999 title: "Eruption">
276
- ```
277
-
278
- Now, representable didn't create a new `Song` instance but updated the existing, resulting in renaming the song.
279
-
280
-
281
- ## Find-or-Create For Incoming Objects
282
-
283
- Representable comes with another strategy called `:find_or_instantiate` which allows creating a property or collection from the incoming document.
284
-
285
- Consider the following incoming hash.
286
-
287
- ```ruby
288
- {"songs" => [{"id" => 1, "title" => "American Paradox"}, {"title" => "Uncoil"}}
289
- ```
290
-
291
- And this representer setup.
292
-
293
- ```ruby
294
- collection :songs, class: Song, parse_strategy: :find_or_instantiate
295
- ```
296
-
297
- In `album.from_hash(..)`, representable will try to call `Song.find(1)` for the first `songs` collection element and `Song.new` for the second (as it doesn't has any `id`), resulting in an array of two `Song` instances, the first an existing, the second a new object.
298
-
299
- **Note**: the various parsing strategies are a collection of "best practices" people find useful. Such a strategy is basically just a set of configuration options, mainly utilizing the `:instance` option.
300
-
301
- Check out the `ParsingStrategy` module to write your own strategy. If you find it useful, please commit it to the core library (with tests).
302
-
303
- The current state of the `:find_or_instantiate` strategy is subject to change.
304
-
305
-
306
119
  ## Inline Representers
307
120
 
308
121
  If you don't want to maintain two separate modules when nesting representations you can define the `SongRepresenter` inline.
309
122
 
310
123
  ```ruby
311
- module AlbumRepresenter
124
+ class AlbumRepresenter < Representable::Decorator
312
125
  include Representable::JSON
313
126
 
314
127
  property :name
@@ -320,1154 +133,43 @@ module AlbumRepresenter
320
133
  end
321
134
  ```
322
135
 
323
- This works both for representer modules and decorators.
324
-
325
- An inline representer is just a Ruby module (or a `Decorator` class). You can include other representer modules. This is handy when having a base representer that needs to be extended in the inline block.
326
-
327
- ```ruby
328
- module AlbumRepresenter
329
- include Representable::JSON
330
-
331
- property :hit do
332
- include SongRepresenter
333
-
334
- property :numbers_sold
335
- end
336
- ```
337
-
338
- If you need to include modules in all inline representers automatically, register it as a feature.
339
-
340
- ```ruby
341
- module AlbumRepresenter
342
- include Representable::JSON
343
- feature Link # imports ::link
344
-
345
- link "/album/1"
346
-
347
- property :hit do
348
- link "/hit/1" # link method imported automatically.
349
- end
350
- ```
351
-
352
-
353
- ## Representing Singular Models And Collections
354
-
355
- You can explicitly define representers for collections of models using a ["Lonely Collection"](#lonely-collections). Or you can let representable do that for you.
356
-
357
- Rendering a collection of objects comes for free, using `::for_collection`.
358
-
359
- ```ruby
360
- Song.all.extend(SongRepresenter.for_collection).to_hash
361
- #=> [{title: "Sevens"}, {title: "Eric"}]
362
- ```
363
-
364
- For parsing, you need to provide the class constant to which the items should be deserialized to.
365
-
366
- ```ruby
367
- module SongRepresenter
368
- include Representable::Hash
369
- property :title
370
-
371
- collection_representer class: Song
372
- end
373
- ```
374
-
375
- You can now parse collections to `Song` instances.
376
-
377
- ```ruby
378
- [].extend(SongRepresenter.for_collection).from_hash([{title: "Sevens"}, {title: "Eric"}])
379
- ```
380
-
381
- As always, this works for decorators _and_ modules.
382
-
383
- In case you don't want to know whether or not you're working with a collection or singular model, use `::represent`.
384
-
385
- ```ruby
386
- # singular
387
- SongRepresenter.represent(Song.find(1)).to_hash #=> {title: "Sevens"}
388
-
389
- # collection
390
- SongRepresenter.represent(Song.all).to_hash #=> [{title: "Sevens"}, {title: "Eric"}]
391
- ```
392
-
393
- As you can see, `::represent` figures out the correct representer for you (works also for parsing!).
394
-
395
- Note: the implicit collection representer internally is implemented using a lonely collection. Everything you pass to `::collection_representer` is simply provided to the `::items` call in the lonely collection. That allows you to use `:parse_strategy` and all the other goodies, too.
396
-
397
-
398
- ## Document Nesting
399
-
400
- Not always does the structure of the desired document map to your objects. The `::nested` method allows you to structure properties in a separate section while still mapping the properties to the outer object.
401
-
402
- Imagine the following document.
403
-
404
- ```json
405
- {"title": "Roxanne",
406
- "details":
407
- {"track": 3,
408
- "length": "4:10"}
409
- }
410
- ```
411
-
412
- However, both `track` and `length` are properties of the song object `<Song#0x999 title: "Roxanne", track: 3 ...>`, there is no such concept as `details` in the `Song` class. Representable gives you `::nested` to achieve this.
413
-
414
- ```ruby
415
- class SongRepresenter < Representable::Decorator
416
- include Representable::JSON
417
-
418
- property :title
419
-
420
- nested :details do
421
- property :track
422
- property :length
423
- end
424
- end
425
- ```
426
-
427
- Just use an inline representer or the `extend:` option to define nested properties. Accessors for nested properties will still be called on the outer object (here, `song`). And as always, this works both ways for rendering and parsing.
428
-
429
- Note that `::nested` internally is implemented using `Decorator`. When adding methods inside the `nested` block, make sure to use `represented` (`self` will point to the decorator instance).
430
-
431
-
432
- ## Decorator vs. Extend
433
-
434
- People who dislike `:extend` go use the `Decorator` strategy!
435
-
436
- ```ruby
437
- class SongRepresentation < Representable::Decorator
438
- include Representable::JSON
439
-
440
- property :title
441
- property :track
442
- end
443
- ```
444
-
445
- The `Decorator` constructor requires the represented object.
446
-
447
- ```ruby
448
- SongRepresentation.new(song).to_json
449
- ```
450
-
451
- This will leave the `song` instance untouched as the decorator just uses public accessors to represent the hit.
452
-
453
- In compositions you need to specify the decorators for the nested items using the `:decorator` option where you'd normally use `:extend`.
454
-
455
- ```ruby
456
- class AlbumRepresentation < Representable::Decorator
457
- include Representable::JSON
458
-
459
- collection :songs, :class => Song, :decorator => SongRepresentation
460
- end
461
- ```
462
-
463
- ### Methods In Modules
464
-
465
- You can define methods in representers in case they aren't defined on the represented object.
466
-
467
- ```ruby
468
- module SongRepresenter
469
- property :title
470
-
471
- def title
472
- @name
473
- end
474
- ```
475
-
476
- That works as the method is mixed into the represented object.
136
+ ## More
477
137
 
478
- Note that this also goes for inline representers.
138
+ Representable has many more features and can literally parse and render any kind of document to an arbitrary Ruby object graph.
479
139
 
480
- ```ruby
481
- property :song do
482
- property :title
483
-
484
- def title
485
- "Static titles are better"
486
- end
487
- end
488
- ```
489
-
490
- ### Methods In Decorators
491
-
492
- When adding a method to a decorator, representable will still invoke accessors on the represented instance - unless you tell it the scope.
493
-
494
- ```ruby
495
- class SongRepresenter < Representable::Decorator
496
- property :title, exec_context: :decorator
497
-
498
- def title
499
- represented.name
500
- end
501
- end
502
- ```
140
+ Please check the [official documentation for more](http://trailblazer.to/gems/representable/).
503
141
 
504
- This will call `title` getter and setter on the decorator instance, not on the represented object. You can still access the represented object in the decorator method using `represented`. BTW, in a module representer this option setting is ignored.
505
142
 
506
- Possible values for this switch (formerly known as `:decorator_scope`) are `:binding`, `:decorator` and `nil`, which is the default setting where lambdas and methods are invoked in the represented context.
143
+ ## Installation
507
144
 
508
- Or use `:getter` or `:setter` to dynamically add a method for the represented object.
145
+ The representable gem runs with all Ruby versions >= 1.9.3.
509
146
 
510
147
  ```ruby
511
- class SongRepresenter < Representable::Decorator
512
- property :title, getter: lambda { |*| @name }
148
+ gem 'representable'
513
149
  ```
514
- As always, the block is executed in the represented object's context.
515
150
 
151
+ ### Dependencies
516
152
 
517
- ## Passing Options
153
+ Representable does a great job with JSON, it also features support for XML, YAML and pure ruby
154
+ hashes. But Representable did not bundle dependencies for JSON and XML.
518
155
 
519
- You're free to pass an options hash into the rendering or parsing.
156
+ If you want to use JSON, add the following to your Gemfile:
520
157
 
521
158
  ```ruby
522
- song.to_json(:append => "SOLD OUT!")
159
+ gem 'multi_json'
523
160
  ```
524
161
 
525
- If you want to append the "SOLD OUT!" to the song's `title` when rendering, use the `:getter` option.
162
+ If you want to use XML, add the following to your Gemfile:
526
163
 
527
164
  ```ruby
528
- SongRepresenter
529
- include Representable::JSON
530
-
531
- property :title, :getter => lambda { |args| title + args[:append] }
532
- end
165
+ gem 'nokogiri'
533
166
  ```
534
167
 
535
- Note that the block is executed in the represented model context which allows using accessors and instance variables.
536
-
537
-
538
- The same works for parsing using the `:setter` method.
539
-
540
- ```ruby
541
- property :title, :setter => lambda { |val, args| self.title= val + args[:append] }
542
- ```
543
-
544
- Here, the block retrieves two arguments: the parsed value and your user options.
545
-
546
- You can also use the `:getter` option instead of writing a reader method. Even when you're not interested in user options you can still use this technique.
547
-
548
- ```ruby
549
- property :title, :getter => lambda { |*| @name }
550
- ```
551
-
552
- This hash will also be available in the `:if` block, documented [here](https://github.com/apotonick/representable/#conditions) and will be passed to nested objects.
553
-
554
-
555
- ## Dynamic Options
556
-
557
- Most of `property`'s options are dynamic, meaning the can be either a static value, a lambda or a :symbol refering to an instance method to be called.
558
-
559
- All user options are passed to the lambdas, e.g. when you call
560
-
561
- ```ruby
562
- song.to_hash(volume: 9)
563
- ```
564
-
565
- the lambda invocation for `:as` would look like this.
566
-
567
- ```ruby
568
- property :name, as: lambda do |args|
569
- args #=> {:volume=>9}
570
- end
571
- ```
572
-
573
- ### Available Options
574
-
575
- Here's a list of all dynamic options and their argument signature.
576
-
577
- * `as: lambda { |args| }` ([see Aliasing](#aliasing))
578
- * `getter: lambda { |args| }` ([see docs](#passing-options))
579
- * `setter: lambda { |value, args| }` ([see docs](#passing-options))
580
- * `class: lambda { |fragment, [i], args| }` ([see Nesting](#nesting))
581
- * `extend: lambda { |object, args| }` ([see Nesting](#nesting))
582
- * `instance: lambda { |fragment, [i], args| }` ([see Object Creation](#polymorphic-object-creation))
583
- * `reader: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
584
- * `writer: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
585
- * `skip_parse: lambda { |fragment, args| }` ([see Skip Parsing](#skip-parsing))
586
- * `skip_render: lambda { |object, args| }` ([see Skip Rendering](#skip-rendering))
587
- * `parse_filter: lambda { |fragment, document, args| }` ([see Filters](#filters)))
588
- * `render_filter: lambda { |value, document, args| }` ([see Filters](#filters))
589
- * `if: lambda { |args| }` ([see Conditions](#conditions))
590
- * `prepare: lambda { |object, args| }` ([see docs](#rendering-and-parsing-without-extend))
591
- * `serialize: lambda { |object, args| }` ([see docs](#overriding-serialize-and-deserialize))
592
- * `deserialize: lambda { |object, fragment, args| }` ([see docs](#overriding-serialize-and-deserialize))
593
- * `representation_wrap` is a dynamic option, too: `self.representation_wrap = lambda do { |args| }` ([see Wrapping](#wrapping))
594
-
595
-
596
- ### Option Arguments
597
-
598
- The `pass_options: true` option instructs representable to pass a special `Options` instance into lambdas or methods. This is handy if you need access to the other stakeholder objects involved in representing objects.
599
-
600
- ```ruby
601
- property :title, pass_options: true, getter: lambda do |args|
602
- args #=> <#Options>
603
- args.binding # etc.
604
- end
605
- ```
606
-
607
- The `Options` instance exposes the following readers: `#binding`, `#represented`, `#decorator` and `#user_options` which is the hash you usually have as `args`.
608
-
609
- Option-specific arguments (e.g. `fragment`, [see here](#available-options)) are still prepended, making the `Options` object always the *last* argument.
610
-
611
-
612
- ## Filters
613
-
614
- Representabe offers you `:render_filter` and `:parse_filter` to modify the value to be rendered or parsed.
615
-
616
- Filters are implemented using `Pipeline`, which means you can add as many as you want. The result from the former filter will be passed to the next.
617
-
618
- ```ruby
619
- property :title, render_filter: lambda { |value, doc, *args| value.html_safe }
620
- ```
621
-
622
- This will be executed right before the fragment gets rendered into the document.
623
-
624
- ```ruby
625
- property :title, parse_filter: lambda { |fragment, doc, *args| Sanitizer.call(fragment) }
626
- ```
627
-
628
- Just before setting the fragment to the object via the `:setter`, the `:parse_filter` is called.
629
-
630
-
631
- ## Skip Parsing
632
-
633
- You can skip parsing for particular fragments which will completely ignore them as if they weren't present in the parsed document.
634
-
635
- ```ruby
636
- property :title, skip_parse: lambda { |fragment, options| fragment.blank? }
637
- ```
638
-
639
- Note that when used with collections, this is evaluated per item.
640
-
641
- ```ruby
642
- collection :songs, skip_parse: lambda { |fragment, options| fragment["title"].blank? } do
643
- property :title
644
- end
645
- ```
646
-
647
- This won't parse empty incoming songs in the collection.
648
-
649
- ## Skip Rendering
650
-
651
- The exact same also works for rendering. You can skip rendering properties and items of collections.
652
-
653
- ```ruby
654
- property :title, skip_render: lambda { |object, options| options[:skip_title] == true }
655
- ```
656
-
657
- In collections, this will be run per item.
658
-
659
-
660
- ## Callable Options
661
-
662
- While lambdas are one option for dynamic options, you might also pass a "callable" object to a directive.
663
-
664
- ```ruby
665
- class Sanitizer
666
- include Uber::Callable
667
-
668
- def call(represented, fragment, doc, *args)
669
- fragment.sanitize
670
- end
671
- end
672
- ```
673
-
674
- Note how including `Uber::Callable` marks instances of this class as callable. No `respond_to?` or other magic takes place here.
675
-
676
- ```ruby
677
- property :title, parse_filter: Santizer.new
678
- ```
679
-
680
- This is enough to have the `Sanitizer` class run with all the arguments that are usually passed to the lambda (preceded by the represented object as first argument).
681
-
682
-
683
- ## XML Support
684
-
685
- While representable does a great job with JSON, it also features support for XML, YAML and pure ruby hashes.
686
-
687
- ```ruby
688
- require 'representable/xml'
689
-
690
- module SongRepresenter
691
- include Representable::XML
692
-
693
- property :title
694
- property :track
695
- collection :composers
696
- end
697
- ```
698
-
699
- For XML we just include the `Representable::XML` module.
700
-
701
- ```xml
702
- Song.new(title: "Fallout", composers: ["Stewart Copeland", "Sting"]).
703
- extend(SongRepresenter).to_xml #=>
704
- <song>
705
- <title>Fallout</title>
706
- <composers>Stewart Copeland</composers>
707
- <composers>Sting</composers>
708
- </song>
709
- ```
710
-
711
-
712
- ## Using Helpers
713
-
714
- Sometimes it's useful to override accessors to customize output or parsing.
715
-
716
- ```ruby
717
- module AlbumRepresenter
718
- include Representable::JSON
719
-
720
- property :name
721
- collection :songs
722
-
723
- def name
724
- super.upcase
725
- end
726
- end
727
-
728
- Album.new(:name => "The Police").
729
- extend(AlbumRepresenter).to_json
730
-
731
- #=> {"name":"THE POLICE","songs":[]}
732
- ```
733
-
734
- Note how the representer allows calling `super` in order to access the original attribute method of the represented object.
735
-
736
- To change the parsing process override the setter.
737
-
738
- ```ruby
739
- def name=(value)
740
- super(value.downcase)
741
- end
742
- ```
743
-
744
- ## Inheritance
745
-
746
- To reuse existing representers use inheritance.
747
-
748
- Inheritance works by `include`ing already defined representers.
749
-
750
- ```ruby
751
- module CoverSongRepresenter
752
- include Representable::JSON
753
- include SongRepresenter
754
-
755
- property :copyright
756
- end
757
- ```
758
-
759
- This results in a representer with the following properties.
760
-
761
-
762
-
763
-
764
- ```ruby
765
- property :title # inherited from SongRepresenter.
766
- property :copyright
767
- ```
768
-
769
- With decorators, you - surprisingly - use class inheritance.
770
-
771
- ```ruby
772
- class HitRepresenter < SongRepresenter
773
- collection :airplays
774
- ```
775
-
776
-
777
- ## Overriding Properties
778
-
779
- You might want to override a particular property in an inheriting representer. Successively calling `property(name)` will override the former definition for `name` just as you know it from overriding methods.
780
-
781
- ```ruby
782
- module CoverSongRepresenter
783
- include Representable::JSON
784
-
785
- include SongRepresenter # defines property :title
786
- property :title, as: :known_as # overrides that definition.
787
- end
788
- ```
789
-
790
- This behaviour was added in 1.7.
791
-
792
-
793
- ## Partly Overriding Properties
794
-
795
- If you wanna override only certain options of the property, use `:inherit`.
796
-
797
- ```ruby
798
- module SongRepresenter
799
- include Representable::JSON
800
-
801
- property :title, as: :known_as
802
- end
803
- ```
804
-
805
- You can now inherit properties but still override or add options.
806
-
807
- ```ruby
808
- module CoverSongRepresenter
809
- include Representable::JSON
810
- include SongRepresenter
811
-
812
- property :title, getter: lambda { Title.random }, inherit: true
813
- end
814
- ```
815
-
816
- Using the `:inherit`, this will result in a property having the following options.
817
-
818
- ```ruby
819
- property :title,
820
- as: :known_as, # inherited from SongRepresenter.
821
- getter: lambda { .. } # added in inheriting representer.
822
- ```
823
-
824
- ## Inheritance With Inline Representers
825
-
826
- Inheriting also works for inline representers.
827
-
828
- ```ruby
829
- module SongRepresenter
830
- include Representable::JSON
831
-
832
- property :title
833
- property :label do
834
- property :name
835
- end
836
- end
837
- ```
838
-
839
- You can now override or add properties with the inline representer.
840
-
841
- ```ruby
842
- module HitRepresenter
843
- include Representable::JSON
844
- include SongRepresenter
845
-
846
- property :label, inherit: true do
847
- property :country
848
- end
849
- end
850
- ```
851
-
852
- Results in a combined inline representer as it inherits.
853
-
854
- ```ruby
855
- property :label do
856
- property :name
857
- property :country
858
- end
859
- ```
860
-
861
- Naturally, `:inherit` can be used within the inline representer block.
862
-
863
- Note that the following also works.
864
-
865
- ```ruby
866
- module HitRepresenter
867
- include Representable::JSON
868
- include SongRepresenter
869
-
870
- property :label, as: :company, inherit: true
871
- end
872
- ```
873
-
874
- This renames the property but still inherits all the inlined configuration.
875
-
876
- Basically, `:inherit` copies the configuration from the parent property, then merges in your options from the inheriting representer. It exposes the same behaviour as `super` in Ruby - when using `:inherit` the property must exist in the parent representer.
877
-
878
- ## Polymorphic Extend
879
-
880
- Sometimes heterogenous collections of objects from different classes must be represented. Or you don't know which representer to use at compile-time and need to delay the computation until runtime. This is why `:extend` accepts a lambda, too.
881
-
882
- Given we not only have songs, but also cover songs.
883
-
884
- ```ruby
885
- class CoverSong < Song
886
- end
887
- ```
888
-
889
- And a non-homogenous collection of songs.
890
-
891
- ```ruby
892
- songs = [ Song.new(title: "Weirdo", track: 5),
893
- CoverSong.new(title: "Truth Hits Everybody", track: 6, copyright: "The Police")]
894
-
895
- album = Album.new(name: "Incognito", songs: songs)
896
- ```
897
-
898
- The `CoverSong` instances are to be represented by their very own `CoverSongRepresenter` defined above. We can't just use a static module in the `:extend` option, so go use a dynamic lambda!
899
-
900
- ```ruby
901
- module AlbumRepresenter
902
- include Representable::JSON
903
-
904
- property :name
905
- collection :songs, :extend => lambda { |song, *| song.is_a?(CoverSong) ? CoverSongRepresenter : SongRepresenter }
906
- end
907
- ```
908
-
909
- Note that the lambda block is evaluated in the represented object context which allows to access helpers or whatever in the block. This works for single properties, too.
910
-
911
-
912
- ## Polymorphic Object Creation
913
-
914
- Rendering heterogenous collections usually implies that you also need to parse those. Luckily, `:class` also accepts a lambda.
915
-
916
- ```ruby
917
- module AlbumRepresenter
918
- include Representable::JSON
919
-
920
- property :name
921
- collection :songs,
922
- :extend => ...,
923
- :class => lambda { |hsh, *| hsh.has_key?("copyright") ? CoverSong : Song }
924
- end
925
- ```
926
-
927
- The block for `:class` receives the currently parsed fragment. Here, this might be something like `{"title"=>"Weirdo", "track"=>5}`.
928
-
929
- If this is not enough, you may override the entire object creation process using `:instance`.
930
-
931
- ```ruby
932
- module AlbumRepresenter
933
- include Representable::JSON
934
-
935
- property :name
936
- collection :songs,
937
- :extend => ...,
938
- :instance => lambda { |hsh, *| hsh.has_key?("copyright") ? CoverSong.new : Song.new(original: true) }
939
- end
940
- ```
941
-
942
- ## Hashes
943
-
944
- As an addition to single properties and collections representable also offers to represent hash attributes.
945
-
946
- ```ruby
947
- module SongRepresenter
948
- include Representable::JSON
949
-
950
- property :title
951
- hash :ratings
952
- end
953
-
954
- Song.new(title: "Bliss", ratings: {"Rolling Stone" => 4.9, "FryZine" => 4.5}).
955
- extend(SongRepresenter).to_json
956
-
957
- #=> {"title":"Bliss","ratings":{"Rolling Stone":4.9,"FryZine":4.5}}
958
- ```
959
-
960
- ## Lonely Hashes
961
-
962
- Need to represent a bare hash without any container? Use the `JSON::Hash` representer (or XML::Hash).
963
-
964
- ```ruby
965
- require 'representable/json/hash'
966
-
967
- module FavoriteSongsRepresenter
968
- include Representable::JSON::Hash
969
- end
970
-
971
- {"Nick" => "Hyper Music", "El" => "Blown In The Wind"}.extend(FavoriteSongsRepresenter).to_json
972
- #=> {"Nick":"Hyper Music","El":"Blown In The Wind"}
973
- ```
974
-
975
- Works both ways. The values are configurable and might be self-representing objects in turn. Tell the `Hash` by using `#values`.
976
-
977
- ```ruby
978
- module FavoriteSongsRepresenter
979
- include Representable::JSON::Hash
980
-
981
- values extend: SongRepresenter, class: Song
982
- end
983
-
984
- {"Nick" => Song.new(title: "Hyper Music")}.extend(FavoriteSongsRepresenter).to_json
985
- ```
986
-
987
- In XML, if you want to store hash attributes in tag attributes instead of dedicated nodes, use `XML::AttributeHash`.
988
-
989
- ## Lonely Collections
990
-
991
- Same goes with arrays.
992
-
993
- ```ruby
994
- require 'representable/json/collection'
995
-
996
- module SongsRepresenter
997
- include Representable::JSON::Collection
998
-
999
- items extend: SongRepresenter, class: Song
1000
- end
1001
- ```
1002
-
1003
- The `#items` method lets you configure the contained entity representing here.
1004
-
1005
- ```ruby
1006
- [Song.new(title: "Hyper Music"), Song.new(title: "Screenager")].extend(SongsRepresenter.for_collection).to_json
1007
- #=> [{"title":"Hyper Music"},{"title":"Screenager"}]
1008
- ```
1009
-
1010
- Note that this also works for XML.
1011
-
1012
-
1013
- ## YAML Support
1014
-
1015
- Representable also comes with a YAML representer.
1016
-
1017
- ```ruby
1018
- module SongRepresenter
1019
- include Representable::YAML
1020
-
1021
- property :title
1022
- property :track
1023
- collection :composers, :style => :flow
1024
- end
1025
- ```
1026
-
1027
- A nice feature is that `#collection` also accepts a `:style` option which helps having nicely formatted inline (or "flow") arrays in your YAML - if you want that!
1028
-
1029
- ```ruby
1030
- song.extend(SongRepresenter).to_yaml
1031
- #=>
1032
- ---
1033
- title: Fallout
1034
- composers: [Stewart Copeland, Sting]
1035
- ```
1036
-
1037
- ## More on XML
1038
-
1039
- ### Mapping Tag Attributes
1040
-
1041
- You can also map properties to tag attributes in representable. This works only for the top-level node, though (seen from the representer's perspective).
1042
-
1043
- ```ruby
1044
- module SongRepresenter
1045
- include Representable::XML
1046
-
1047
- property :title, attribute: true
1048
- property :track, attribute: true
1049
- end
1050
-
1051
- Song.new(title: "American Idle").to_xml
1052
- #=> <song title="American Idle" />
1053
- ```
1054
-
1055
- Naturally, this works for both ways.
1056
-
1057
-
1058
- ### Mapping Content
1059
-
1060
- The same concept can also be applied to content. If you need to map a property to the top-level node's content, use the `:content` option. Again, _top-level_ refers to the document fragment that maps to the representer.
1061
-
1062
- ```ruby
1063
- module SongRepresenter
1064
- include Representable::XML
1065
-
1066
- property :title, content: true
1067
- end
1068
-
1069
- Song.new(title: "American Idle").to_xml
1070
- #=> <song>American Idle</song>
1071
- ```
1072
-
1073
-
1074
- ### Wrapping Collections
1075
-
1076
- It is sometimes unavoidable to wrap tag lists in a container tag.
1077
-
1078
- ```ruby
1079
- module AlbumRepresenter
1080
- include Representable::XML
1081
-
1082
- collection :songs, :as => :song, :wrap => :songs
1083
- end
1084
- ```
1085
-
1086
- Note that `:wrap` defines the container tag name.
1087
-
1088
- ```xml
1089
- Album.new.to_xml #=>
1090
- <album>
1091
- <songs>
1092
- <song>Laundry Basket</song>
1093
- <song>Two Kevins</song>
1094
- <song>Wright and Rong</song>
1095
- </songs>
1096
- </album>
1097
- ```
1098
-
1099
- ### Namespaces
1100
-
1101
- Support for namespaces are not yet implemented. However, if an incoming parsed document contains namespaces, you can automatically remove them.
1102
-
1103
- ```ruby
1104
- module AlbumRepresenter
1105
- include Representable::XML
1106
-
1107
- remove_namespaces!
1108
- ```
1109
-
1110
- ## Avoiding Modules
1111
-
1112
- There's been a rough discussion whether or not to use `extend` in Ruby. If you want to save that particular step when representing objects, define the representers right in your classes.
1113
-
1114
- ```ruby
1115
- class Song < OpenStruct
1116
- include Representable::JSON
1117
-
1118
- property :name
1119
- end
1120
- ```
1121
-
1122
- I do not recommend this approach as it bloats your domain classes with representation logic that is barely needed elsewhere. Use [decorators](#decorator-vs-extend) instead.
1123
-
1124
-
1125
- ## More Options
1126
-
1127
- Here's a quick overview about other available options for `#property` and its bro `#collection`.
1128
-
1129
-
1130
- ### Overriding Read And Write
1131
-
1132
- This can be handy if a property needs to be compiled from several fragments. The lambda has access to the entire object document (either hash or `Nokogiri` node) and user options.
1133
-
1134
- ```ruby
1135
- property :title, :writer => lambda { |doc, args| doc["title"] = title || original_title }
1136
- ```
1137
-
1138
- When using the `:writer` option it is up to you to add fragments to the `doc` - representable won't add anything for this property.
1139
-
1140
- The same works for parsing using `:reader`.
1141
-
1142
- ```ruby
1143
- property :title, :reader => lambda { |doc, args| self.title = doc["title"] || doc["name"] }
1144
- ```
1145
-
1146
- ### Read/Write Restrictions
1147
-
1148
- Using the `:readable` and `:writeable` options access to properties can be restricted.
1149
-
1150
- ```ruby
1151
- property :title, :readable => false
1152
- ```
1153
-
1154
- This will leave out the `title` property in the rendered document. Vice-versa, `:writeable` will skip the property when parsing and does not assign it.
1155
-
1156
-
1157
- ### Filtering
1158
-
1159
- Representable also allows you to skip and include properties using the `:exclude` and `:include` options passed directly to the respective method.
1160
-
1161
- ```ruby
1162
- song.to_json(:include => :title)
1163
- #=> {"title":"Roxanne"}
1164
- ```
1165
-
1166
- ### Conditions
1167
-
1168
- You can also define conditions on properties using `:if`, making them being considered only when the block returns a true value.
1169
-
1170
- ```ruby
1171
- module SongRepresenter
1172
- include Representable::JSON
1173
-
1174
- property :title
1175
- property :track, if: lambda { |args| track > 0 }
1176
- end
1177
- ```
1178
-
1179
- When rendering or parsing, the `track` property is considered only if track is valid. Note that the block is executed in instance context, giving you access to instance methods.
1180
-
1181
- As always, the block retrieves your options. Given this render call
1182
-
1183
- ```ruby
1184
- song.to_json(minimum_track: 2)
1185
- ```
1186
-
1187
- your `:if` may process the options.
1188
-
1189
- ```ruby
1190
- property :track, if: lambda { |opts| track > opts[:minimum_track] }
1191
- ```
1192
-
1193
- ### False And Nil Values
1194
-
1195
- Since representable-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` properties that weren't included in the rendered document will be visible now.
1196
-
1197
- If you want `nil` values to be included when rendering, use the `:render_nil` option.
1198
-
1199
- ```ruby
1200
- property :track, render_nil: true
1201
- ```
1202
-
1203
- ### Empty Collections
1204
-
1205
- Per default, empty collections are rendered (unless they're `nil`). You can suppress rendering.
1206
-
1207
- ```ruby
1208
- collection :songs, render_empty: false
1209
- ```
1210
-
1211
- This will be the default behaviour in 2.0.
1212
-
1213
-
1214
- ### Overriding Serialize And Deserialize
1215
-
1216
- When serializing, the default mechanics after preparing the object are to call `object.to_hash`.
1217
-
1218
- Override this step with `:serialize`.
1219
-
1220
- ```ruby
1221
- property :song, extend: SongRepresenter,
1222
- serialize: lambda { |object, *args| Marshal.dump(object) }
1223
- ```
1224
-
1225
- Vice-versa, parsing allows the same.
1226
-
1227
- ```ruby
1228
- property :song, extend: SongRepresenter,
1229
- deserialize: lambda { |object, fragment, *args| Marshal.load(fragment) }
1230
- ```
1231
-
1232
-
1233
- ## Coercion
1234
-
1235
- 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.
1236
-
1237
- Include virtus in your Gemfile, first. Be sure to include virtus 0.5.0 or greater.
1238
-
1239
- ```ruby
1240
- gem 'virtus', ">= 0.5.0"
1241
- ```
1242
-
1243
- Use the `:type` option to specify the conversion target. Note that `:default` still works.
1244
-
1245
- ```ruby
1246
- module SongRepresenter
1247
- include Representable::JSON
1248
- include Representable::Coercion
1249
-
1250
- property :title
1251
- property :recorded_at, :type => DateTime, :default => "May 12th, 2012"
1252
- end
1253
- ```
1254
-
1255
- In a decorator it works alike.
1256
-
1257
- ```ruby
1258
- class SongRepresenter < Representable::Decorator
1259
- include Representable::JSON
1260
- include Representable::Coercion
1261
-
1262
- property :recorded_at, :type => DateTime
1263
- end
1264
- ```
1265
-
1266
- Coercing values only happens when rendering or parsing a document. Representable does not create accessors in your model as `virtus` does.
1267
-
1268
-
1269
- ## Undocumented Features
1270
-
1271
- *(Please don't read this section!)*
1272
-
1273
- ### Custom Binding
1274
-
1275
- If you need a special binding for a property you're free to create it using the `:binding` option.
1276
-
1277
- ```ruby
1278
- property :title, :binding => lambda { |*args| JSON::TitleBinding.new(*args) }
1279
- ```
1280
-
1281
-
1282
- ### Syncing Parsing
1283
-
1284
- You can use the parsed document fragment directly as a representable instance by returning `nil` in `:class`.
1285
-
1286
- ```ruby
1287
- property :song, :class => lambda { |*| nil }
1288
- ```
1289
-
1290
- This makes sense when your parsing looks like this.
1291
-
1292
- ```ruby
1293
- hit.from_hash(song: <#Song ..>)
1294
- ```
1295
-
1296
- Representable will not attempt to create a `Song` instance for you but use the provided from the document.
1297
-
1298
- Note that this is now the [official option](#syncing-objects) `:parse_strategy`.
1299
-
1300
-
1301
- ### Rendering And Parsing Without Extend
1302
-
1303
- Sometimes you wanna skip the preparation step when rendering and parsing, for instance, when the object already exposes a `#to_hash`/`#from_hash` method.
1304
-
1305
- ```ruby
1306
- class ParsingSong
1307
- def from_hash(hash, *args)
1308
- # do whatever
1309
-
1310
- self
1311
- end
1312
-
1313
- def to_hash(*args)
1314
- {}
1315
- end
1316
- end
1317
- ```
1318
-
1319
- This would work with a representer as the following.
1320
-
1321
- ```ruby
1322
- property :song, :class => ParsingSong, prepare: lambda { |object| object }
1323
- ```
1324
-
1325
- Instead of automatically extending/decorating the object, the `:prepare` lambda is run. It's up to you to prepare you object - or simply return it, as in the above example.
1326
-
1327
-
1328
- ### Skipping Rendering Or Parsing
1329
-
1330
- You can skip to call to `#to_hash`/`#from_hash` on the prepared object by using `:representable`.
1331
-
1332
- ```ruby
1333
- property :song, :representable => false
1334
- ```
1335
-
1336
- This will run the entire serialization/deserialization _without_ calling the actual representing method on the object.
1337
-
1338
- Extremely helpful if you wanna use representable as a data mapping tool with filtering, aliasing, etc., without the rendering and parsing part.
1339
-
1340
-
1341
- ### Returning Arbitrary Objects When Parsing
1342
-
1343
- When representable parses the `song` attribute, it calls `ParsingSong#from_hash`. This method could return any object, which will then be assigned as the `song` property.
1344
-
1345
- ```ruby
1346
- class ParsingSong
1347
- def from_hash(hash, *args)
1348
- [1,2,3,4]
1349
- end
1350
- end
1351
- ```
1352
-
1353
- Album.extend(AlbumRepresenter).from_hash(..).song #=> [1,2,3,4]
1354
-
1355
- This also works with `:extend` where the specified module overwrites the parsing method (e.g. `#from_hash`).
1356
-
1357
-
1358
- ### Decorator In Module
1359
-
1360
- Inline representers defined in a module can be implemented as a decorator, thus wrapping the represented object without pollution.
1361
-
1362
- ```ruby
1363
- property :song, use_decorator: true do
1364
- property :title
1365
- end
1366
- ```
1367
-
1368
- This is an implementation detail most people shouldn't worry about.
1369
-
1370
-
1371
- ### Defaults
1372
-
1373
- Declaring default options for `property`, `nested`, `collection` and `hash` is
1374
- easy as:
1375
-
1376
- ```ruby
1377
- defaults render_nil: true
1378
- ```
1379
-
1380
- You can also pass a block for dynamic default options based on the property
1381
- name or on the property options.
1382
-
1383
- ```ruby
1384
- defaults render_nil: true do |name|
1385
- { as: name.to_s.camelize }
1386
- end
1387
- ```
1388
-
1389
- ```ruby
1390
- defaults render_nil: true do |name, options|
1391
- {
1392
- as: name.to_s.camelize,
1393
- embedded: options.fetch(:hash, false)
1394
- }
1395
- end
1396
- ```
1397
-
1398
- ## Symbol Keys vs. String Keys
1399
-
1400
- When parsing representable reads properties from hashes by using their string keys.
1401
-
1402
- ```ruby
1403
- song.from_hash("title" => "Road To Never")
1404
- ```
1405
-
1406
- To allow symbol keys also include the `AllowSymbols` module.
1407
-
1408
- ```ruby
1409
- module SongRepresenter
1410
- include Representable::Hash
1411
- include Representable::Hash::AllowSymbols
1412
- # ..
1413
- end
1414
- ```
1415
-
1416
- This will give you a behavior close to Rails' `HashWithIndifferentAccess` by stringifying the incoming hash internally.
1417
-
1418
- ## Object to Object Mapping
1419
-
1420
- Since Representable 2.1.6 we also allow mapping objects to objects directly. This is extremely fast as the source object is queried for its properties and then values are directly written to the target object.
1421
-
1422
- `````ruby
1423
- require "representable/object"
1424
-
1425
- module SongRepresenter
1426
- include Representable::Object
1427
-
1428
- property :title
1429
- end
1430
- ```
1431
-
1432
- As always, the source object needs to have readers for all properties and the target writers.
1433
-
1434
- ```ruby
1435
- song_copy = OpenStruct.new.extend(SongRepresenter).from_object(song)
1436
- song_copy.title = "Roxanne"
1437
- ```
1438
-
1439
- All kind of transformation options can be used as well as nesting.
1440
-
1441
- ## Debugging
1442
-
1443
- Representable is a generic mapper using recursions and things that might be hard to understand from the outside. That's why we got the `Debug` module which will give helpful output about what it's doing when parsing or rendering.
1444
-
1445
- You can extend objects on the run to see what they're doing.
1446
-
1447
- ```ruby
1448
- song.extend(SongRepresenter).extend(Representable::Debug).from_json("..")
1449
- song.extend(SongRepresenter).extend(Representable::Debug).to_json
1450
- ```
1451
-
1452
- `Debug` can also be included statically into your representer or decorator.
1453
-
1454
- ```ruby
1455
- class SongRepresenter < Representable::Decorator
1456
- include Representable::JSON
1457
- include Representable::Debug
1458
-
1459
- property :title
1460
- end
1461
- ```
1462
-
1463
- It's probably a good idea not to do this in production.
1464
-
1465
-
1466
168
  ## Copyright
1467
169
 
1468
- Representable started as a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his inspiring work.
170
+ Representable started as a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his extremely inspiring work.
1469
171
 
1470
- * Copyright (c) 2011-2015 Nick Sutterer <apotonick@gmail.com>
172
+ * Copyright (c) 2011-2016 Nick Sutterer <apotonick@gmail.com>
1471
173
  * ROXML is Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom.
1472
174
 
1473
175
  Representable is released under the [MIT License](http://www.opensource.org/licenses/MIT).