representable 1.7.7 → 1.8.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +42 -8
  3. data/README.md +208 -55
  4. data/Rakefile +0 -6
  5. data/lib/representable.rb +39 -43
  6. data/lib/representable/binding.rb +59 -37
  7. data/lib/representable/bindings/hash_bindings.rb +3 -4
  8. data/lib/representable/bindings/xml_bindings.rb +10 -10
  9. data/lib/representable/bindings/yaml_bindings.rb +2 -2
  10. data/lib/representable/coercion.rb +1 -1
  11. data/lib/representable/config.rb +11 -5
  12. data/lib/representable/definition.rb +67 -35
  13. data/lib/representable/deserializer.rb +23 -27
  14. data/lib/representable/hash.rb +15 -4
  15. data/lib/representable/hash/allow_symbols.rb +27 -0
  16. data/lib/representable/json.rb +0 -1
  17. data/lib/representable/json/collection.rb +0 -2
  18. data/lib/representable/mapper.rb +6 -13
  19. data/lib/representable/parse_strategies.rb +57 -0
  20. data/lib/representable/readable_writeable.rb +29 -0
  21. data/lib/representable/serializer.rb +9 -4
  22. data/lib/representable/version.rb +1 -1
  23. data/lib/representable/xml.rb +1 -1
  24. data/lib/representable/xml/collection.rb +0 -2
  25. data/lib/representable/yaml.rb +0 -1
  26. data/representable.gemspec +1 -0
  27. data/test/as_test.rb +43 -0
  28. data/test/class_test.rb +124 -0
  29. data/test/config_test.rb +13 -3
  30. data/test/decorator_scope_test.rb +28 -0
  31. data/test/definition_test.rb +46 -35
  32. data/test/exec_context_test.rb +93 -0
  33. data/test/generic_test.rb +0 -154
  34. data/test/getter_setter_test.rb +28 -0
  35. data/test/hash_bindings_test.rb +35 -35
  36. data/test/hash_test.rb +0 -20
  37. data/test/if_test.rb +78 -0
  38. data/test/inherit_test.rb +21 -1
  39. data/test/inheritance_test.rb +1 -1
  40. data/test/inline_test.rb +40 -2
  41. data/test/instance_test.rb +286 -0
  42. data/test/is_representable_test.rb +77 -0
  43. data/test/json_test.rb +6 -29
  44. data/test/nested_test.rb +30 -0
  45. data/test/parse_strategy_test.rb +249 -0
  46. data/test/pass_options_test.rb +27 -0
  47. data/test/prepare_test.rb +67 -0
  48. data/test/reader_writer_test.rb +19 -0
  49. data/test/representable_test.rb +25 -265
  50. data/test/stringify_hash_test.rb +41 -0
  51. data/test/test_helper.rb +12 -4
  52. data/test/wrap_test.rb +48 -0
  53. data/test/xml_bindings_test.rb +37 -37
  54. data/test/xml_test.rb +14 -14
  55. metadata +94 -30
  56. data/lib/representable/deprecations.rb +0 -4
  57. data/lib/representable/feature/readable_writeable.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 453857338b438b7d68ffcdc22c4a673132d3193e
4
- data.tar.gz: 19635dc3d647a965fcfe0af872db31f3eb305db3
3
+ metadata.gz: b00de985635f525b8a01171885aed414e6204034
4
+ data.tar.gz: dc62dd3c718b5969a663da9587ef815ddb4eb6e8
5
5
  SHA512:
6
- metadata.gz: e20ce7e46c5a94ab219a8936c04fa1a6ed7c1f77f2be36fab23685b80c1fda109c950dd011ae35c5a638ba04e0ce739f64d612d4a01c826bc75bcdee16524e2b
7
- data.tar.gz: 58791da0eef9d17e2c3f24c82fc9c532cbfdea78d159cd09be33bf91755257adb0dbf9cb889c4facc81a17a81995ae8d5e4712063aaf4549ab571db12d9426c4
6
+ metadata.gz: 9a7cc3fb19f4319e69cbf66e566ae70b9a8ede10bc5b9666756128ab7795eec2c35315f31603ff3ad3fa8b78ac170270389ba33e128b6848496d45fd1cec615f
7
+ data.tar.gz: d2eb8e52c0485aebea0fe956fa98ef9d9c06f87a8d00a0d027dbea804b573982b364d1bcc06f3db65bbeb33e89e814a594300302675776be11024ce335569d69
data/CHANGES.md CHANGED
@@ -1,11 +1,45 @@
1
- h2. 1.8.0
2
-
3
- * Major API change: Remove defaults for collections. This fixes a major design flaw - when parsing a document a collection would be reset to `[]` even if it is not present in the parsed document.
4
- * Minor API change: Rename `Definition#sought_type` to `#deserialize_class`.
5
- -> constantize :class etc (requires AS)
6
- -> make all options lambda-able
7
- -> make major steps lambda-able
8
- -> strategies for deserialization (lambda-able!)
1
+ # 1.8.0
2
+
3
+ ## Major Breakage
4
+
5
+ * `:if` receives block arguments just like any other dynamic options. Refer to **Dynamic Options**.
6
+ * Remove defaults for collections. This fixes a major design flaw - when parsing a document a collection would be reset to `[]` even if it is not present in the parsed document.
7
+ * The number of arguments per block might have changed. Generally, if you're not interested in block arguments, use `Proc.new` or `lambda { |*| }`. See **Dynamic Options**.
8
+
9
+
10
+ ## Dynamic Options
11
+
12
+ * The following options are dynamic now and can either be a static value, a lambda or an instance method symbol: `:as`, `:getter`, `:setter`, `:class`, `:instance`, `:reader`, `:writer`, `:extend`, `:prepare`, `:if`. Please refer to the README to see their signatures.
13
+ * `representation_wrap` is dynamic, too, allowing you to change the wrap per instance.
14
+
15
+
16
+ ## Cool New Stuff
17
+
18
+ * When unsure about the number of arguments passed into an option lambda, use `:pass_options`. This passes all general options in a dedicated `Options` object that responds to `binding`, `decorator`, `represented` and `user_options`. It's always the last argument for the block.
19
+ * Added `parse_strategy: :find_or_instantiate`. More to come.
20
+ * Use `representable: false` to prevent calling `to_*/from_*` on a represented object even if the property is `typed?` (`:extend`, `:class` or `:instance` set).
21
+ * Introduced `:use_decorator` option to force an inline representer to be implemented with a Decorator even in a module. This fixes a bug since we used the `:decorate` option in earlier versions, which was already used for something else.
22
+ * Autoload `Representable::Hash*` and `Representable::Decorator`.
23
+ * Added `Representable::Hash::AllowSymbols` to convert symbol keys to strings in `from_hash`.
24
+
25
+
26
+ ## Deprecations
27
+ * `decorator_scope: true` is deprecated, use `exec_context: :decorator` instead.
28
+ * Using `:extend` in combination with an inline representer is deprecated. Include the module in the block.
29
+ * `instance: lambda { true }` is deprecated. Use `parse_strategy: :sync`.
30
+ * Removed `Config#wrap`. Only way to retrieve the evaluated wrap is `Config#wrap_for`.
31
+ * `class: lambda { nil }` is deprecated. To return the fragment from parsing, use `instance: lambda { |fragment, *args| fragment }` instead.
32
+
33
+ ## Definition
34
+
35
+ * Make `Definition < Hash`, all options can/should now be accessed with `Definition#[]`.
36
+ * Make `Definition::new` and `#merge!` the only entry points so that a `Definition` becomes an almost *immutual* object. If you happened to modify a definition using `options[..]=` this will break now. Use `definition.merge!(..)` to change it after creation.
37
+ * Deprecated `#options` as the definition itself is a hash (e.g. `definition[:default]`).
38
+ * Removed `#sought_type`, `#default`, `#attribute`, `#content`.
39
+ * `#from` is replaced by `#as` and hardcore deprecated.
40
+ * `#name` and `#as` are _always_ strings.
41
+ * A Definition is considered typed as soon as [`:extend`|`:class`|`:instance`] is set. In earlier versions, `property :song, class: Song` was considered typed, whereas `property :song, class: lambda { Song }` was static.
42
+
9
43
 
10
44
  h2. 1.7.7
11
45
 
data/README.md CHANGED
@@ -174,9 +174,28 @@ Album.new.extend(AlbumRepresenter).
174
174
  #=> #<Album name="Offspring", songs=[#<Song title="Genocide">, #<Song title="Nitro", composers=["Offspring"]>]>
175
175
  ```
176
176
 
177
+
178
+ ## Parse Strategies
179
+
180
+ When parsing `collection`s (also applies to single `property`s), representable usually iterates the incoming list and creates a new object per array item.
181
+
182
+ Parse strategies let you do that manually.
183
+
184
+ ```ruby
185
+ collection :songs, :parse_strategy => lambda { |fragment, i, options|
186
+ songs << song = Song.new
187
+ song
188
+ }
189
+ ```
190
+
191
+ 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).
192
+
193
+ This gives you all the freedom you need for your nested parsing.
194
+
195
+
177
196
  ## Syncing Objects
178
197
 
179
- Usually, representable creates a new nested object when parsing. If you want to update an existing object, use the `parse_strategy` option.
198
+ Usually, representable creates a new nested object when parsing. If you want to update an existing object, use the `parse_strategy: :sync` option.
180
199
 
181
200
  ```ruby
182
201
  module AlbumRepresenter
@@ -203,6 +222,32 @@ album.songs.first #=> #<Song:0x999 title: "Eruption">
203
222
 
204
223
  Now, representable didn't create a new `Song` instance but updated the existing, resulting in renaming the song.
205
224
 
225
+
226
+ ## Find-or-Create For Incoming Objects
227
+
228
+ Representable comes with another strategy called `:find_or_instantiate` which allows creating a property or collection from the incoming document.
229
+
230
+ Consider the following incoming hash.
231
+
232
+ ```ruby
233
+ {"songs" => [{"id" => 1, "title" => "American Paradox"}, {"title" => "Uncoil"}}
234
+ ```
235
+
236
+ And this representer setup.
237
+
238
+ ```ruby
239
+ collection :songs, class: Song, parse_strategy: :find_or_instantiate
240
+ ```
241
+
242
+ 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.
243
+
244
+ **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.
245
+
246
+ Check out the `ParsingStrategy` module to write your own strategy. If you find it useful, please commit it to the core library (with tests).
247
+
248
+ The current state of the `:find_or_instantiate` strategy is subject to change.
249
+
250
+
206
251
  ## Inline Representers
207
252
 
208
253
  If you don't want to maintain two separate modules when nesting representations you can define the `SongRepresenter` inline.
@@ -222,13 +267,15 @@ module AlbumRepresenter
222
267
 
223
268
  This works both for representer modules and decorators.
224
269
 
225
- You can use an inline representer along with `:extend`. The latter will automatically be included in the inline representer. This is handy if you want to inline-extend a base decorator.
270
+ An inline representer is just a Ruby module. You can include other representer modules. This is handy when having a base representer that needs to be extended in the inline block.
226
271
 
227
272
  ```ruby
228
273
  module AlbumRepresenter
229
274
  include Representable::JSON
230
275
 
231
- property :hit, extend: SongRepresenter do
276
+ property :hit do
277
+ include SongRepresenter
278
+
232
279
  property :numbers_sold
233
280
  end
234
281
  ```
@@ -315,7 +362,7 @@ That works as the method is mixed into the represented object. When adding a hel
315
362
 
316
363
  ```ruby
317
364
  class SongRepresenter < Representable::Decorator
318
- property :title, decorator_scope: true
365
+ property :title, exec_context: :decorator
319
366
 
320
367
  def title
321
368
  represented.name
@@ -323,7 +370,9 @@ class SongRepresenter < Representable::Decorator
323
370
  end
324
371
  ```
325
372
 
326
- 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 is ignored.
373
+ 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.
374
+
375
+ 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.
327
376
 
328
377
  Or use `:getter` or `:setter` to dynamically add a method for the represented object.
329
378
 
@@ -333,33 +382,6 @@ class SongRepresenter < Representable::Decorator
333
382
  ```
334
383
  As always, the block is executed in the represented object's context.
335
384
 
336
- ## XML Support
337
-
338
- While representable does a great job with JSON, it also features support for XML, YAML and pure ruby hashes.
339
-
340
- ```ruby
341
- require 'representable/xml'
342
-
343
- module SongRepresenter
344
- include Representable::XML
345
-
346
- property :title
347
- property :track
348
- collection :composers
349
- end
350
- ```
351
-
352
- For XML we just include the `Representable::XML` module.
353
-
354
- ```xml
355
- Song.new(title: "Fallout", composers: ["Steward Copeland", "Sting"]).
356
- extend(SongRepresenter).to_xml #=>
357
- <song>
358
- <title>Fallout</title>
359
- <composers>Steward Copeland</composers>
360
- <composers>Sting</composers>
361
- </song>
362
- ```
363
385
 
364
386
  ## Passing Options
365
387
 
@@ -398,6 +420,87 @@ property :title, :getter => lambda { |*| @name }
398
420
 
399
421
  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.
400
422
 
423
+
424
+ ## Dynamic Options
425
+
426
+ 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.
427
+
428
+ All user options are passed to the lambdas, e.g. when you call
429
+
430
+ ```ruby
431
+ song.to_hash(volume: 9)
432
+ ```
433
+
434
+ the lambda invocation for `:as` would look like this.
435
+
436
+ ```ruby
437
+ property :name, as: lambda do |args|
438
+ args #=> {:volume=>9}
439
+ end
440
+ ```
441
+
442
+ ### Available Options
443
+
444
+ Here's a list of all dynamic options and their argument signature.
445
+
446
+ * `as: lambda { |args| }` ([see Aliasing](#aliasing))
447
+ * `getter: lambda { |args| }` ([see docs](#passing-options))
448
+ * `setter: lambda { |value, args| }` ([see docs](#passing-options))
449
+ * `class: lambda { |fragment, [i], args| }` ([see Nesting](#nesting))
450
+ * `extend: lambda { |object, args| }` ([see Nesting](#nesting))
451
+ * `instance: lambda { |fragment, [i], args| }` ([see Object Creation](#polymorphic-object-creation))
452
+ * `reader: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
453
+ * `writer: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
454
+ * `if: lambda { |args| }` ([see Conditions](#conditions))
455
+ * `prepare: lambda { |object, args| }` ([see docs](#rendering-and-parsing-without-extend))
456
+ * `representation_wrap` is a dynamic option, too: `self.representation_wrap = lambda do { |args| }` ([see Wrapping](#wrapping))
457
+
458
+
459
+ ### Option Arguments
460
+
461
+ 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.
462
+
463
+ ```ruby
464
+ property :title, pass_options: true, getter: lambda do |args|
465
+ args #=> <#Options>
466
+ args.binding # etc.
467
+ end
468
+ ```
469
+
470
+ The `Options` instance exposes the following readers: `#binding`, `#represented`, `#decorator` and `#user_options` which is the hash you usually have as `args`.
471
+
472
+ Option-specific arguments (e.g. `fragment`, [see here](#available-options)) are still prepended, making the `Options` object always the *last* argument.
473
+
474
+
475
+ ## XML Support
476
+
477
+ While representable does a great job with JSON, it also features support for XML, YAML and pure ruby hashes.
478
+
479
+ ```ruby
480
+ require 'representable/xml'
481
+
482
+ module SongRepresenter
483
+ include Representable::XML
484
+
485
+ property :title
486
+ property :track
487
+ collection :composers
488
+ end
489
+ ```
490
+
491
+ For XML we just include the `Representable::XML` module.
492
+
493
+ ```xml
494
+ Song.new(title: "Fallout", composers: ["Steward Copeland", "Sting"]).
495
+ extend(SongRepresenter).to_xml #=>
496
+ <song>
497
+ <title>Fallout</title>
498
+ <composers>Steward Copeland</composers>
499
+ <composers>Sting</composers>
500
+ </song>
501
+ ```
502
+
503
+
401
504
  ## Using Helpers
402
505
 
403
506
  Sometimes it's useful to override accessors to customize output or parsing.
@@ -432,7 +535,9 @@ end
432
535
 
433
536
  ## Inheritance
434
537
 
435
- To reuse existing representers you can inherit from those modules.
538
+ To reuse existing representers use inheritance.
539
+
540
+ Inheritance works by `include`ing already defined representers.
436
541
 
437
542
  ```ruby
438
543
  module CoverSongRepresenter
@@ -443,13 +548,14 @@ module CoverSongRepresenter
443
548
  end
444
549
  ```
445
550
 
446
- Inheritance works by `include`ing already defined representers.
551
+ This results in a representer with the following properties.
552
+
553
+
447
554
 
448
- ```ruby
449
- Song.new(:title => "Truth Hits Everybody", :copyright => "The Police").
450
- extend(CoverSongRepresenter).to_json
451
555
 
452
- #=> {"title":"Truth Hits Everybody","copyright":"The Police"}
556
+ ```ruby
557
+ property :title # inherited from SongRepresenter.
558
+ property :copyright
453
559
  ```
454
560
 
455
561
  With decorators, you - surprisingly - use class inheritance.
@@ -460,7 +566,6 @@ class HitRepresenter < SongRepresenter
460
566
  ```
461
567
 
462
568
 
463
-
464
569
  ## Overriding Properties
465
570
 
466
571
  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.
@@ -489,7 +594,7 @@ module SongRepresenter
489
594
  end
490
595
  ```
491
596
 
492
- You can now inherit but still override or add options.
597
+ You can now inherit properties but still override or add options.
493
598
 
494
599
  ```ruby
495
600
  module CoverSongRepresenter
@@ -500,13 +605,12 @@ module CoverSongRepresenter
500
605
  end
501
606
  ```
502
607
 
503
- This will result in a property having the following options.
608
+ Using the `:inherit`, this will result in a property having the following options.
504
609
 
505
610
  ```ruby
506
- property :title,
507
- as: :known_as, # inherited from SongRepresenter
508
- getter: lambda { .. } # added in inheriting representer.
509
- end
611
+ property :title,
612
+ as: :known_as, # inherited from SongRepresenter.
613
+ getter: lambda { .. } # added in inheriting representer.
510
614
  ```
511
615
 
512
616
  ## Inheritance With Inline Representers
@@ -546,6 +650,8 @@ property :label do
546
650
  end
547
651
  ```
548
652
 
653
+ Naturally, `:inherit` can be used within the inline representer block.
654
+
549
655
  Note that the following also works.
550
656
 
551
657
  ```ruby
@@ -559,7 +665,7 @@ end
559
665
 
560
666
  This renames the property but still inherits all the inlined configuration.
561
667
 
562
- Basically, `:inherit` copies the configuration from the parent property, then merges it 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.
668
+ 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.
563
669
 
564
670
  ## Polymorphic Extend
565
671
 
@@ -724,7 +830,7 @@ composers: [Steward Copeland, Sting]
724
830
 
725
831
  ### Mapping Tag Attributes
726
832
 
727
- You can also map properties to tag attributes in representable. This works only for the top-level node, thou (seen from the representer's perspective).
833
+ 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).
728
834
 
729
835
  ```ruby
730
836
  module SongRepresenter
@@ -921,6 +1027,7 @@ end
921
1027
 
922
1028
  Coercing values only happens when rendering or parsing a document. Representable does not create accessors in your model as `virtus` does.
923
1029
 
1030
+
924
1031
  ## Undocumented Features
925
1032
 
926
1033
  *(Please don't read this section!)*
@@ -933,6 +1040,7 @@ If you need a special binding for a property you're free to create it using the
933
1040
  property :title, :binding => lambda { |*args| JSON::TitleBinding.new(*args) }
934
1041
  ```
935
1042
 
1043
+
936
1044
  ### Syncing Parsing
937
1045
 
938
1046
  You can use the parsed document fragment directly as a representable instance by returning `nil` in `:class`.
@@ -951,32 +1059,77 @@ Representable will not attempt to create a `Song` instance for you but use the p
951
1059
 
952
1060
  Note that this is now the [official option](#syncing-objects) `:parse_strategy`.
953
1061
 
954
- ### Rendering Without Extend
955
1062
 
956
- The same goes the other way when rendering. Just provide an empty `:instance` block.
1063
+ ### Rendering And Parsing Without Extend
1064
+
1065
+ Sometimes you wanna skip the preparation step when rendering and parsing, for instance, when the object already exposes a `#to_hash`/`#from_hash` method.
957
1066
 
958
1067
  ```ruby
959
- property :song, :instance => lambda { |*| nil }
1068
+ class ParsingSong
1069
+ def from_hash(hash, *args)
1070
+ # do whatever
1071
+
1072
+ self
1073
+ end
1074
+
1075
+ def to_hash(*args)
1076
+ {}
1077
+ end
1078
+ end
960
1079
  ```
961
1080
 
962
- This will treat the `song` property instance as a representable object.
1081
+ This would work with a representer as the following.
963
1082
 
964
1083
  ```ruby
965
- hit.to_json # this will call hit.song.to_json
1084
+ property :song, :class => ParsingSong, prepare: lambda { |object| object }
966
1085
  ```
967
1086
 
968
- Rendering `collection`s works the same. Parsing doesn't work out-of-the-box, currently, as we're still unsure how to map items to fragments.
1087
+ 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.
1088
+
1089
+
1090
+ ### Skipping Rendering Or Parsing
1091
+
1092
+ You can skip to call to `#to_hash`/`#from_hash` on the prepared object by using `:representable`.
1093
+
1094
+ ```ruby
1095
+ property :song, :representable => false
1096
+ ```
1097
+
1098
+ This will run the entire serialization/deserialization _without_ calling the actual representing method on the object.
1099
+
1100
+ Extremely helpful if you wanna use representable as a data mapping tool with filtering, aliasing, etc., without the rendering and parsing part.
1101
+
1102
+
1103
+ ### Returning Arbitrary Objects When Parsing
1104
+
1105
+ 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.
1106
+
1107
+ ```ruby
1108
+ class ParsingSong
1109
+ def from_hash(hash, *args)
1110
+ [1,2,3,4]
1111
+ end
1112
+ end
1113
+ ```
1114
+
1115
+ Album.extend(AlbumRepresenter).from_hash(..).song #=> [1,2,3,4]
1116
+
1117
+ This also works with `:extend` where the specified module overwrites the parsing method (e.g. `#from_hash`).
1118
+
969
1119
 
970
1120
  ### Decorator In Module
971
1121
 
972
1122
  Inline representers defined in a module can be implemented as a decorator, thus wrapping the represented object without pollution.
973
1123
 
974
1124
  ```ruby
975
- property :label, decorator: true do
976
- ...
1125
+ property :song, use_decorator: true do
1126
+ property :title
977
1127
  end
978
1128
  ```
979
1129
 
1130
+ This is an implementation detail most people shouldn't worry about.
1131
+
1132
+
980
1133
  ## Copyright
981
1134
 
982
1135
  Representable started as a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his inspiring work.