representable 1.7.7 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|