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.
- checksums.yaml +4 -4
- data/CHANGES.md +42 -8
- data/README.md +208 -55
- data/Rakefile +0 -6
- data/lib/representable.rb +39 -43
- data/lib/representable/binding.rb +59 -37
- data/lib/representable/bindings/hash_bindings.rb +3 -4
- data/lib/representable/bindings/xml_bindings.rb +10 -10
- data/lib/representable/bindings/yaml_bindings.rb +2 -2
- data/lib/representable/coercion.rb +1 -1
- data/lib/representable/config.rb +11 -5
- data/lib/representable/definition.rb +67 -35
- data/lib/representable/deserializer.rb +23 -27
- data/lib/representable/hash.rb +15 -4
- data/lib/representable/hash/allow_symbols.rb +27 -0
- data/lib/representable/json.rb +0 -1
- data/lib/representable/json/collection.rb +0 -2
- data/lib/representable/mapper.rb +6 -13
- data/lib/representable/parse_strategies.rb +57 -0
- data/lib/representable/readable_writeable.rb +29 -0
- data/lib/representable/serializer.rb +9 -4
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +1 -1
- data/lib/representable/xml/collection.rb +0 -2
- data/lib/representable/yaml.rb +0 -1
- data/representable.gemspec +1 -0
- data/test/as_test.rb +43 -0
- data/test/class_test.rb +124 -0
- data/test/config_test.rb +13 -3
- data/test/decorator_scope_test.rb +28 -0
- data/test/definition_test.rb +46 -35
- data/test/exec_context_test.rb +93 -0
- data/test/generic_test.rb +0 -154
- data/test/getter_setter_test.rb +28 -0
- data/test/hash_bindings_test.rb +35 -35
- data/test/hash_test.rb +0 -20
- data/test/if_test.rb +78 -0
- data/test/inherit_test.rb +21 -1
- data/test/inheritance_test.rb +1 -1
- data/test/inline_test.rb +40 -2
- data/test/instance_test.rb +286 -0
- data/test/is_representable_test.rb +77 -0
- data/test/json_test.rb +6 -29
- data/test/nested_test.rb +30 -0
- data/test/parse_strategy_test.rb +249 -0
- data/test/pass_options_test.rb +27 -0
- data/test/prepare_test.rb +67 -0
- data/test/reader_writer_test.rb +19 -0
- data/test/representable_test.rb +25 -265
- data/test/stringify_hash_test.rb +41 -0
- data/test/test_helper.rb +12 -4
- data/test/wrap_test.rb +48 -0
- data/test/xml_bindings_test.rb +37 -37
- data/test/xml_test.rb +14 -14
- metadata +94 -30
- data/lib/representable/deprecations.rb +0 -4
- data/lib/representable/feature/readable_writeable.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b00de985635f525b8a01171885aed414e6204034
|
4
|
+
data.tar.gz: dc62dd3c718b5969a663da9587ef815ddb4eb6e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a7cc3fb19f4319e69cbf66e566ae70b9a8ede10bc5b9666756128ab7795eec2c35315f31603ff3ad3fa8b78ac170270389ba33e128b6848496d45fd1cec615f
|
7
|
+
data.tar.gz: d2eb8e52c0485aebea0fe956fa98ef9d9c06f87a8d00a0d027dbea804b573982b364d1bcc06f3db65bbeb33e89e814a594300302675776be11024ce335569d69
|
data/CHANGES.md
CHANGED
@@ -1,11 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
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,
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
608
|
+
Using the `:inherit`, this will result in a property having the following options.
|
504
609
|
|
505
610
|
```ruby
|
506
|
-
|
507
|
-
|
508
|
-
|
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
|
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,
|
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
|
-
|
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
|
-
|
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
|
1081
|
+
This would work with a representer as the following.
|
963
1082
|
|
964
1083
|
```ruby
|
965
|
-
|
1084
|
+
property :song, :class => ParsingSong, prepare: lambda { |object| object }
|
966
1085
|
```
|
967
1086
|
|
968
|
-
|
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 :
|
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.
|