representable 2.1.1 → 2.1.3
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 +9 -1
- data/README.md +60 -1
- data/lib/representable/binding.rb +4 -3
- data/lib/representable/definition.rb +1 -1
- data/lib/representable/populator.rb +1 -0
- data/lib/representable/serializer.rb +19 -4
- data/lib/representable/version.rb +1 -1
- data/representable.gemspec +1 -1
- data/test/skip_test.rb +39 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00d2530b55af3101dbb2526089519f0bdb549b83
|
4
|
+
data.tar.gz: ed33d0ffceab14486efa0a0e61edc84e3de03de7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fb790c37b9c9bd1abe2f6caeff855031b5a263c44f58e8b43f37f1ada791a2e1a87efdf6dabd26a95c1e184e3d88a5049296c0459b0b95c1ac42bdd96937e72
|
7
|
+
data.tar.gz: 597b5614752170094138b69d7c02ef70deb5a23c300d35e22bfa3f9232b5d8572ac53dd84dd1e7500f35940f3263cd96687a981878e046f3b29a94fba3cef22a
|
data/CHANGES.md
CHANGED
@@ -1,8 +1,16 @@
|
|
1
|
+
# 2.1.3
|
2
|
+
|
3
|
+
* Like 2.1.2 (got yanked) because I thought it's buggy but it's not. What has changed is that `Serializer::Collection#serialize` no longer does `collection.collect` but `collection.each` since this allows filtering out unwanted elements.
|
4
|
+
|
5
|
+
# 2.1.2
|
6
|
+
|
7
|
+
* Added `:skip_render` options.
|
8
|
+
|
1
9
|
# 2.1.1
|
2
10
|
|
3
11
|
* Added `Definition#delete!` to remove options.
|
4
12
|
* Added `Representable::apply` do iterate and change schemas.
|
5
|
-
* Added `Config
|
13
|
+
* Added `Config#remove` to remove properties.
|
6
14
|
* Added `Representable::Debug` which just has to be included into your represented object.
|
7
15
|
|
8
16
|
```ruby
|
data/README.md
CHANGED
@@ -62,6 +62,8 @@ song = Song.new.extend(SongRepresenter).from_json(%{ {"title":"Roxanne"} })
|
|
62
62
|
#=> #<Song title="Roxanne", track=nil>
|
63
63
|
```
|
64
64
|
|
65
|
+
Note that parsing hashes per default does [require string keys](#symbol-keys-vs-string-keys) and does _not_ pick up symbol keys.
|
66
|
+
|
65
67
|
## Extend vs. Decorator
|
66
68
|
|
67
69
|
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.
|
@@ -528,6 +530,7 @@ Here's a list of all dynamic options and their argument signature.
|
|
528
530
|
* `reader: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
|
529
531
|
* `writer: lambda { |document, args| }` ([see Read And Write](#overriding-read-and-write))
|
530
532
|
* `skip_parse: lambda { |fragment, args| }` ([see Skip Parsing](#skip-parsing))
|
533
|
+
* `skip_render: lambda { |object, args| }` ([see Skip Rendering](#skip-rendering))
|
531
534
|
* `parse_filter: lambda { |fragment, document, args| }` ([see Filters](#filters)))
|
532
535
|
* `render_filter: lambda { |value, document, args| }` ([see Filters](#filters))
|
533
536
|
* `if: lambda { |args| }` ([see Conditions](#conditions))
|
@@ -590,6 +593,17 @@ end
|
|
590
593
|
|
591
594
|
This won't parse empty incoming songs in the collection.
|
592
595
|
|
596
|
+
## Skip Rendering
|
597
|
+
|
598
|
+
The exact same also works for rendering. You can skip rendering properties and items of collections.
|
599
|
+
|
600
|
+
```ruby
|
601
|
+
property :title, skip_render: lambda { |object, options| options[:skip_title] == true }
|
602
|
+
```
|
603
|
+
|
604
|
+
In collections, this will be run per item.
|
605
|
+
|
606
|
+
|
593
607
|
## Callable Options
|
594
608
|
|
595
609
|
While lambdas are one option for dynamic options, you might also pass a "callable" object to a directive.
|
@@ -1300,12 +1314,57 @@ end
|
|
1300
1314
|
|
1301
1315
|
This is an implementation detail most people shouldn't worry about.
|
1302
1316
|
|
1317
|
+
## Symbol Keys vs. String Keys
|
1318
|
+
|
1319
|
+
When parsing representable reads properties from hashes by using their string keys.
|
1320
|
+
|
1321
|
+
```ruby
|
1322
|
+
song.from_hash("title" => "Road To Never")
|
1323
|
+
```
|
1324
|
+
|
1325
|
+
To allow symbol keys also include the `AllowSymbols` module.
|
1326
|
+
|
1327
|
+
```ruby
|
1328
|
+
module SongRepresenter
|
1329
|
+
include Representable::Hash
|
1330
|
+
include Representable::Hash::AllowSymbols
|
1331
|
+
# ..
|
1332
|
+
end
|
1333
|
+
```
|
1334
|
+
|
1335
|
+
This will give you a behavior close to Rails' `HashWithIndifferentAccess` by stringifying the incoming hash internally.
|
1336
|
+
|
1337
|
+
|
1338
|
+
## Debugging
|
1339
|
+
|
1340
|
+
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.
|
1341
|
+
|
1342
|
+
You can extend objects on the run to see what they're doing.
|
1343
|
+
|
1344
|
+
```ruby
|
1345
|
+
song.extend(SongRepresenter).extend(Representable::Debug).from_json("..")
|
1346
|
+
song.extend(SongRepresenter).extend(Representable::Debug).to_json
|
1347
|
+
```
|
1348
|
+
|
1349
|
+
`Debug` can also be included statically into your representer or decorator.
|
1350
|
+
|
1351
|
+
```ruby
|
1352
|
+
class SongRepresenter < Representable::Decorator
|
1353
|
+
include Representable::JSON
|
1354
|
+
include Representable::Debug
|
1355
|
+
|
1356
|
+
property :title
|
1357
|
+
end
|
1358
|
+
```
|
1359
|
+
|
1360
|
+
It's probably a good idea not to do this in production.
|
1361
|
+
|
1303
1362
|
|
1304
1363
|
## Copyright
|
1305
1364
|
|
1306
1365
|
Representable started as a heavily simplified fork of the ROXML gem. Big thanks to Ben Woosley for his inspiring work.
|
1307
1366
|
|
1308
|
-
* Copyright (c) 2011-
|
1367
|
+
* Copyright (c) 2011-2015 Nick Sutterer <apotonick@gmail.com>
|
1309
1368
|
* ROXML is Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom.
|
1310
1369
|
|
1311
1370
|
Representable is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
@@ -55,7 +55,8 @@ module Representable
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def render_fragment(value, doc)
|
58
|
-
|
58
|
+
# DISCUSS: should we return a Skip object instead of this block trick? (same in Populator?)
|
59
|
+
fragment = serialize(value) { return } # render fragments of hash, xml, yaml.
|
59
60
|
|
60
61
|
write(doc, fragment)
|
61
62
|
end
|
@@ -173,8 +174,8 @@ module Representable
|
|
173
174
|
|
174
175
|
attr_reader :exec_context, :decorator
|
175
176
|
|
176
|
-
def serialize(object)
|
177
|
-
serializer.call(object)
|
177
|
+
def serialize(object, &block)
|
178
|
+
serializer.call(object, &block)
|
178
179
|
end
|
179
180
|
|
180
181
|
def serializer_class
|
@@ -111,7 +111,7 @@ module Representable
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def dynamic_options
|
114
|
-
[:as, :getter, :setter, :class, :instance, :reader, :writer, :extend, :prepare, :if, :deserialize, :serialize, :render_filter, :parse_filter, :skip_parse]
|
114
|
+
[:as, :getter, :setter, :class, :instance, :reader, :writer, :extend, :prepare, :if, :deserialize, :serialize, :render_filter, :parse_filter, :skip_parse, :skip_render]
|
115
115
|
end
|
116
116
|
|
117
117
|
def handle_extend!(options)
|
@@ -17,6 +17,7 @@ module Representable
|
|
17
17
|
return unless @binding.has_default?
|
18
18
|
value = @binding[:default]
|
19
19
|
else
|
20
|
+
# DISCUSS: should we return a Skip object instead of this block trick? (same in Binding#serialize?)
|
20
21
|
value = deserialize(fragment) { return } # stop here if skip_parse?
|
21
22
|
end
|
22
23
|
|
@@ -1,16 +1,23 @@
|
|
1
1
|
require "representable/deserializer"
|
2
2
|
|
3
3
|
module Representable
|
4
|
+
# serialize -> serialize! -> marshal. # TODO: same flow in deserialize.
|
4
5
|
class Serializer < Deserializer
|
5
|
-
def call(object)
|
6
|
+
def call(object, &block)
|
6
7
|
return object if object.nil? # DISCUSS: move to Object#serialize ?
|
7
8
|
|
8
|
-
serialize(object, @binding.user_options)
|
9
|
+
serialize(object, @binding.user_options, &block)
|
9
10
|
end
|
10
11
|
|
11
12
|
private
|
13
|
+
def serialize(object, user_options, &block)
|
14
|
+
return yield if @binding.evaluate_option(:skip_render, object) # this will jump out of #render_fragment. introduce Skip object here.
|
15
|
+
|
16
|
+
serialize!(object, user_options)
|
17
|
+
end
|
18
|
+
|
12
19
|
# Serialize one object by calling to_json etc. on it.
|
13
|
-
def serialize(object, user_options)
|
20
|
+
def serialize!(object, user_options)
|
14
21
|
object = prepare(object)
|
15
22
|
|
16
23
|
return object unless @binding.representable?
|
@@ -27,7 +34,15 @@ module Representable
|
|
27
34
|
|
28
35
|
class Collection < self
|
29
36
|
def serialize(array, *args)
|
30
|
-
|
37
|
+
collection = [] # TODO: unify with Deserializer::Collection.
|
38
|
+
|
39
|
+
array.each do |item|
|
40
|
+
next if @binding.evaluate_option(:skip_render, item) # TODO: allow skipping entire collections? same for deserialize.
|
41
|
+
|
42
|
+
collection << serialize!(item, *args)
|
43
|
+
end # TODO: i don't want Array but Forms here - what now?
|
44
|
+
|
45
|
+
collection
|
31
46
|
end
|
32
47
|
end
|
33
48
|
|
data/representable.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_dependency "uber", "~> 0.0.7"
|
25
25
|
|
26
26
|
s.add_development_dependency "rake"
|
27
|
-
s.add_development_dependency "test_xml", "
|
27
|
+
s.add_development_dependency "test_xml", "0.1.6"
|
28
28
|
s.add_development_dependency "minitest", ">= 5.4.1"
|
29
29
|
s.add_development_dependency "mocha", ">= 0.13.0"
|
30
30
|
s.add_development_dependency "mongoid"
|
data/test/skip_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class SkipParseTest < MiniTest::Spec
|
4
4
|
representer! do
|
5
5
|
property :title
|
6
6
|
property :band,
|
@@ -25,4 +25,42 @@ class SkipTest < MiniTest::Spec
|
|
25
25
|
# skip_parse is _per item_.
|
26
26
|
let (:airplay) { OpenStruct.new(station: "JJJ") }
|
27
27
|
it { song.from_hash({"airplays" => [{"station" => "JJJ"}, {}]}, skip?: true).airplays.must_equal [airplay] }
|
28
|
+
|
29
|
+
# it skips parsing of items as if they hadn't been in the document.
|
30
|
+
it { song.from_hash({"airplays" => [{"station" => "JJJ"}, {}, {"station" => "JJJ"}]}, skip?: true).airplays.must_equal [airplay, airplay] }
|
31
|
+
end
|
32
|
+
|
33
|
+
class SkipRenderTest < MiniTest::Spec
|
34
|
+
representer! do
|
35
|
+
property :title
|
36
|
+
property :band,
|
37
|
+
skip_render: lambda { |object, opts| opts[:skip?] and object.name == "Rancid" } do
|
38
|
+
property :name
|
39
|
+
end
|
40
|
+
|
41
|
+
collection :airplays,
|
42
|
+
skip_render: lambda { |object, opts| puts object.inspect; opts[:skip?] and object.station == "Radio Dreyeckland" } do
|
43
|
+
property :station
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
let (:song) { OpenStruct.new(title: "Black Night", band: OpenStruct.new(name: "Time Again")).extend(representer) }
|
48
|
+
let (:skip_song) { OpenStruct.new(title: "Time Bomb", band: OpenStruct.new(name: "Rancid")).extend(representer) }
|
49
|
+
|
50
|
+
# do render.
|
51
|
+
it { song.to_hash(skip?: true).must_equal({"title"=>"Black Night", "band"=>{"name"=>"Time Again"}}) }
|
52
|
+
# skip.
|
53
|
+
it { skip_song.to_hash(skip?: true).must_equal({"title"=>"Time Bomb"}) }
|
54
|
+
|
55
|
+
# do render all collection items.
|
56
|
+
it do
|
57
|
+
song = OpenStruct.new(airplays: [OpenStruct.new(station: "JJJ"), OpenStruct.new(station: "ABC")]).extend(representer)
|
58
|
+
song.to_hash(skip?: true).must_equal({"airplays"=>[{"station"=>"JJJ"}, {"station"=>"ABC"}]})
|
59
|
+
end
|
60
|
+
|
61
|
+
# skip middle item.
|
62
|
+
it do
|
63
|
+
song = OpenStruct.new(airplays: [OpenStruct.new(station: "JJJ"), OpenStruct.new(station: "Radio Dreyeckland"), OpenStruct.new(station: "ABC")]).extend(representer)
|
64
|
+
song.to_hash(skip?: true).must_equal({"airplays"=>[{"station"=>"JJJ"}, {"station"=>"ABC"}]})
|
65
|
+
end
|
28
66
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: representable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -70,14 +70,14 @@ dependencies:
|
|
70
70
|
name: test_xml
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 0.1.6
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.1.6
|
83
83
|
- !ruby/object:Gem::Dependency
|