representable 1.7.0 → 1.7.1
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 +5 -0
- data/README.md +55 -0
- data/TODO +3 -1
- data/lib/representable/binding.rb +0 -9
- data/lib/representable/bindings/hash_bindings.rb +18 -30
- data/lib/representable/bindings/xml_bindings.rb +14 -25
- data/lib/representable/bindings/yaml_bindings.rb +15 -26
- data/lib/representable/config.rb +5 -0
- data/lib/representable/serializer.rb +4 -1
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +19 -2
- data/test/config_test.rb +8 -0
- data/test/json_test.rb +0 -4
- data/test/xml_test.rb +12 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57ca6d3739a88e3c3e38ecb6b4aa0b33b5da43a4
|
4
|
+
data.tar.gz: fc6f1765a152cbbb9300f01c60ae8f55c169ec6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff20b22f94094d5bd9b99927c1f9ed28f82b74270d30d9eab5f8cab40facf22a8a9dbe9ccbb258ec68145489c8f81bd03766c6082f6c0a16c5f54d1f45610b61
|
7
|
+
data.tar.gz: 1c6c950c144548a986f55cdff935348ae253eee59339a3219416aba556aef22e59f707bf8ad9a30d3a3104314eb13ad1b67911ef2ccb10489bf6c21d0dda3f7c
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
h2. 1.7.1
|
2
|
+
|
3
|
+
* Introduce `Config#options` hash to store per-representer configuration.
|
4
|
+
* The XML representer can now automatically remove namespaces when parsing. Use `XML::remove_namespaces!` in your representer. This is a work-around until namespaces are properly implemented in representable.
|
5
|
+
|
1
6
|
h2. 1.7.0
|
2
7
|
|
3
8
|
* The actual serialization and deserialization (that is, calling `to_hash` etc on the object) now happens in dedicated classes: `ObjectDeserializer` and friends. If you used to override stuff in `Binding`, I'm sorry.
|
data/README.md
CHANGED
@@ -174,6 +174,35 @@ Album.new.extend(AlbumRepresenter).
|
|
174
174
|
#=> #<Album name="Offspring", songs=[#<Song title="Genocide">, #<Song title="Nitro", composers=["Offspring"]>]>
|
175
175
|
```
|
176
176
|
|
177
|
+
## Syncing Objects
|
178
|
+
|
179
|
+
Usually, representable creates a new nested object when parsing. If you want to update an existing object, use the `parse_strategy` option.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
module AlbumRepresenter
|
183
|
+
include Representable::JSON
|
184
|
+
|
185
|
+
collection :songs, extend: SongRepresenter, parse_strategy: :sync
|
186
|
+
```
|
187
|
+
|
188
|
+
When parsing an album, it will now call `from_json` on the existing songs in the collection.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
album = Album.find(1)
|
192
|
+
album.songs.first #=> #<Song:0x999 title: "Panama">
|
193
|
+
```
|
194
|
+
|
195
|
+
Note that the album already contains a song instance.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
album.extend(AlbumRepresenter).
|
199
|
+
from_json('{songs: [{title: "Eruption"}]}')
|
200
|
+
|
201
|
+
album.songs.first #=> #<Song:0x999 title: "Eruption">
|
202
|
+
```
|
203
|
+
|
204
|
+
Now, representable didn't create a new `Song` instance but updated the existing, resulting in renaming the song.
|
205
|
+
|
177
206
|
## Inline Representers
|
178
207
|
|
179
208
|
If you don't want to maintain two separate modules when nesting representations you can define the `SongRepresenter` inline.
|
@@ -389,6 +418,21 @@ Song.new(:title => "Truth Hits Everybody", :copyright => "The Police").
|
|
389
418
|
#=> {"title":"Truth Hits Everybody","copyright":"The Police"}
|
390
419
|
```
|
391
420
|
|
421
|
+
## Overriding Properties
|
422
|
+
|
423
|
+
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.
|
424
|
+
|
425
|
+
```ruby
|
426
|
+
module CoverSongRepresenter
|
427
|
+
include Representable::JSON
|
428
|
+
|
429
|
+
include SongRepresenter # defines property :title
|
430
|
+
property :title, as: :known_as # overrides that definition.
|
431
|
+
end
|
432
|
+
```
|
433
|
+
|
434
|
+
This behaviour was added in 1.7.
|
435
|
+
|
392
436
|
|
393
437
|
## Polymorphic Extend
|
394
438
|
|
@@ -594,6 +638,17 @@ Album.new.to_xml #=>
|
|
594
638
|
</album>
|
595
639
|
```
|
596
640
|
|
641
|
+
### Namespaces
|
642
|
+
|
643
|
+
Support for namespaces are not yet implemented. However, if an incoming parsed document contains namespaces, you can automatically remove them.
|
644
|
+
|
645
|
+
```ruby
|
646
|
+
module AlbumRepresenter
|
647
|
+
include Representable::XML
|
648
|
+
|
649
|
+
remove_namespaces!
|
650
|
+
```
|
651
|
+
|
597
652
|
## Avoiding Modules
|
598
653
|
|
599
654
|
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.
|
data/TODO
CHANGED
@@ -35,4 +35,6 @@ module ReaderWriter
|
|
35
35
|
* make it easy to override Binding#options via #to_hash(whatever: {hit: {decorator: HitDecorator}})
|
36
36
|
|
37
37
|
* DISCUSS: should inline representers be created at runtime, so we don't need ::representer_engine?
|
38
|
-
* deprecate `Decorator::Coercion`.
|
38
|
+
* deprecate `Decorator::Coercion`.
|
39
|
+
|
40
|
+
* cleanup XML so it matches the current #serialize standard.
|
@@ -23,15 +23,6 @@ module Representable
|
|
23
23
|
|
24
24
|
attr_reader :user_options, :represented # TODO: make private/remove.
|
25
25
|
|
26
|
-
# Main entry point for rendering/parsing a property object.
|
27
|
-
def serialize(value)
|
28
|
-
value
|
29
|
-
end
|
30
|
-
|
31
|
-
def deserialize(fragment, *args)
|
32
|
-
fragment
|
33
|
-
end
|
34
|
-
|
35
26
|
# Retrieve value and write fragment to the doc.
|
36
27
|
def compile_fragment(doc)
|
37
28
|
represented_exec_for(:writer, doc) do
|
@@ -2,74 +2,62 @@ require 'representable/binding'
|
|
2
2
|
|
3
3
|
module Representable
|
4
4
|
module Hash
|
5
|
-
|
5
|
+
class PropertyBinding < Representable::Binding
|
6
6
|
include Binding::Object
|
7
7
|
|
8
|
-
def serialize_method
|
9
|
-
:to_hash
|
10
|
-
end
|
11
|
-
|
12
|
-
def deserialize_method
|
13
|
-
:from_hash
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
class PropertyBinding < Representable::Binding
|
19
8
|
def self.build_for(definition, *args) # TODO: remove default arg.
|
20
9
|
return CollectionBinding.new(definition, *args) if definition.array?
|
21
10
|
return HashBinding.new(definition, *args) if definition.hash?
|
22
11
|
new(definition, *args)
|
23
12
|
end
|
24
13
|
|
25
|
-
def initialize(*args) # FIXME. make generic.
|
26
|
-
super
|
27
|
-
extend ObjectBinding if typed?
|
28
|
-
end
|
29
|
-
|
30
14
|
def read(hash)
|
31
15
|
return FragmentNotFound unless hash.has_key?(from) # DISCUSS: put it all in #read for performance. not really sure if i like returning that special thing.
|
32
16
|
|
33
17
|
fragment = hash[from]
|
34
|
-
|
18
|
+
deserialize(fragment)
|
35
19
|
end
|
36
20
|
|
37
21
|
def write(hash, value)
|
38
|
-
hash[from] =
|
39
|
-
end
|
40
|
-
|
41
|
-
def serialize_for(value)
|
42
|
-
serialize(value)
|
22
|
+
hash[from] = serialize(value)
|
43
23
|
end
|
44
24
|
|
45
25
|
def deserialize_from(fragment)
|
46
26
|
deserialize(fragment)
|
47
27
|
end
|
28
|
+
|
29
|
+
def serialize_method
|
30
|
+
:to_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def deserialize_method
|
34
|
+
:from_hash
|
35
|
+
end
|
48
36
|
end
|
49
37
|
|
50
38
|
class CollectionBinding < PropertyBinding
|
51
|
-
def
|
39
|
+
def serialize(value)
|
52
40
|
# value.enum_for(:each_with_index).collect { |obj, i| serialize(obj, i) } # DISCUSS: provide ary index/hash key for representer_module_for?
|
53
|
-
value.collect { |item|
|
41
|
+
value.collect { |item| super(item) } # TODO: i don't want Array but Forms here - what now?
|
54
42
|
end
|
55
43
|
|
56
|
-
def
|
44
|
+
def deserialize(fragment)
|
57
45
|
CollectionDeserializer.new(self).deserialize(fragment)
|
58
46
|
end
|
59
47
|
end
|
60
48
|
|
61
49
|
|
62
50
|
class HashBinding < PropertyBinding
|
63
|
-
def
|
51
|
+
def serialize(value)
|
64
52
|
# requires value to respond to #each with two block parameters.
|
65
53
|
{}.tap do |hsh|
|
66
|
-
value.each { |key, obj| hsh[key] =
|
54
|
+
value.each { |key, obj| hsh[key] = super(obj) }
|
67
55
|
end
|
68
56
|
end
|
69
57
|
|
70
|
-
def
|
58
|
+
def deserialize(fragment)
|
71
59
|
{}.tap do |hsh|
|
72
|
-
fragment.each { |key, item_fragment| hsh[key] =
|
60
|
+
fragment.each { |key, item_fragment| hsh[key] = super(item_fragment) }
|
73
61
|
end
|
74
62
|
end
|
75
63
|
end
|
@@ -3,28 +3,9 @@ require 'representable/bindings/hash_bindings.rb'
|
|
3
3
|
|
4
4
|
module Representable
|
5
5
|
module XML
|
6
|
-
|
6
|
+
class PropertyBinding < Binding
|
7
7
|
include Binding::Object
|
8
8
|
|
9
|
-
def serialize_method
|
10
|
-
:to_node
|
11
|
-
end
|
12
|
-
|
13
|
-
def deserialize_method
|
14
|
-
:from_node
|
15
|
-
end
|
16
|
-
|
17
|
-
def content_for(node) # TODO: move to ScalarDecorator.
|
18
|
-
node
|
19
|
-
end
|
20
|
-
|
21
|
-
def serialize_node(node, value)
|
22
|
-
serialize(value)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
class PropertyBinding < Binding
|
28
9
|
def self.build_for(definition, *args)
|
29
10
|
return CollectionBinding.new(definition, *args) if definition.array?
|
30
11
|
return HashBinding.new(definition, *args) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
|
@@ -33,11 +14,6 @@ module Representable
|
|
33
14
|
new(definition, *args)
|
34
15
|
end
|
35
16
|
|
36
|
-
def initialize(*args)
|
37
|
-
super
|
38
|
-
extend ObjectBinding if typed? # FIXME.
|
39
|
-
end
|
40
|
-
|
41
17
|
def write(parent, value)
|
42
18
|
wrap_node = parent
|
43
19
|
|
@@ -63,6 +39,8 @@ module Representable
|
|
63
39
|
end
|
64
40
|
|
65
41
|
def serialize_node(node, value)
|
42
|
+
return serialize(value) if typed?
|
43
|
+
|
66
44
|
node.content = serialize(value)
|
67
45
|
node
|
68
46
|
end
|
@@ -72,6 +50,15 @@ module Representable
|
|
72
50
|
#deserialize(nodes.first)
|
73
51
|
end
|
74
52
|
|
53
|
+
# DISCUSS: why is this public?
|
54
|
+
def serialize_method
|
55
|
+
:to_node
|
56
|
+
end
|
57
|
+
|
58
|
+
def deserialize_method
|
59
|
+
:from_node
|
60
|
+
end
|
61
|
+
|
75
62
|
private
|
76
63
|
def xpath
|
77
64
|
from
|
@@ -88,6 +75,8 @@ module Representable
|
|
88
75
|
end
|
89
76
|
|
90
77
|
def content_for(node) # TODO: move this into a ScalarDecorator.
|
78
|
+
return node if typed?
|
79
|
+
|
91
80
|
node.content
|
92
81
|
end
|
93
82
|
end
|
@@ -2,57 +2,46 @@ require 'representable/binding'
|
|
2
2
|
|
3
3
|
module Representable
|
4
4
|
module YAML
|
5
|
-
module ObjectBinding
|
6
|
-
include Binding::Object
|
7
|
-
|
8
|
-
def serialize_method
|
9
|
-
:to_ast
|
10
|
-
end
|
11
|
-
|
12
|
-
def deserialize_method
|
13
|
-
:from_hash
|
14
|
-
end
|
15
|
-
|
16
|
-
def write_scalar(value)
|
17
|
-
value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
5
|
class PropertyBinding < Representable::Hash::PropertyBinding
|
22
6
|
def self.build_for(definition, *args)
|
23
7
|
return CollectionBinding.new(definition, *args) if definition.array?
|
24
8
|
new(definition, *args)
|
25
9
|
end
|
26
10
|
|
27
|
-
def initialize(*args) # FIXME. make generic.
|
28
|
-
super
|
29
|
-
extend ObjectBinding if typed?
|
30
|
-
end
|
31
|
-
|
32
11
|
def write(map, value)
|
33
12
|
map.children << Psych::Nodes::Scalar.new(from)
|
34
|
-
map.children <<
|
13
|
+
map.children << serialize(value) # FIXME: should be serialize.
|
35
14
|
end
|
36
15
|
|
37
|
-
def
|
38
|
-
write_scalar
|
16
|
+
def serialize(value)
|
17
|
+
write_scalar super(value)
|
39
18
|
end
|
40
19
|
|
41
20
|
def write_scalar(value)
|
21
|
+
return value if typed?
|
22
|
+
|
42
23
|
Psych::Nodes::Scalar.new(value.to_s)
|
43
24
|
end
|
25
|
+
|
26
|
+
def serialize_method
|
27
|
+
:to_ast
|
28
|
+
end
|
29
|
+
|
30
|
+
def deserialize_method
|
31
|
+
:from_hash
|
32
|
+
end
|
44
33
|
end
|
45
34
|
|
46
35
|
|
47
36
|
class CollectionBinding < PropertyBinding
|
48
|
-
def
|
37
|
+
def serialize(value)
|
49
38
|
Psych::Nodes::Sequence.new.tap do |seq|
|
50
39
|
seq.style = Psych::Nodes::Sequence::FLOW if options[:style] == :flow
|
51
40
|
value.each { |obj| seq.children << super(obj) }
|
52
41
|
end
|
53
42
|
end
|
54
43
|
|
55
|
-
def
|
44
|
+
def deserialize(fragment) # FIXME: redundant from Hash::Bindings
|
56
45
|
CollectionDeserializer.new(self).deserialize(fragment)
|
57
46
|
end
|
58
47
|
end
|
data/lib/representable/config.rb
CHANGED
@@ -2,7 +2,7 @@ require "representable/deserializer"
|
|
2
2
|
|
3
3
|
module Representable
|
4
4
|
class ObjectSerializer < ObjectDeserializer
|
5
|
-
def call
|
5
|
+
def call # TODO: make typed? switch here!
|
6
6
|
return @object if @object.nil?
|
7
7
|
|
8
8
|
representable = prepare(@object)
|
@@ -12,6 +12,9 @@ module Representable
|
|
12
12
|
|
13
13
|
private
|
14
14
|
def serialize(object, user_options)
|
15
|
+
# TODO: this used to be handled in #serialize where Object added it's behaviour. treat scalars as objects to remove this switch:
|
16
|
+
return object unless @binding.typed?
|
17
|
+
|
15
18
|
object.send(@binding.serialize_method, user_options.merge!({:wrap => false}))
|
16
19
|
end
|
17
20
|
end
|
data/lib/representable/xml.rb
CHANGED
@@ -14,6 +14,10 @@ module Representable
|
|
14
14
|
|
15
15
|
|
16
16
|
module ClassMethods
|
17
|
+
def remove_namespaces!
|
18
|
+
representable_attrs.options[:remove_namespaces] = true
|
19
|
+
end
|
20
|
+
|
17
21
|
# Creates a new Ruby object from XML using mapping information declared in the class.
|
18
22
|
#
|
19
23
|
# Accepts a block yielding the currently iterated Definition. If the block returns false
|
@@ -35,9 +39,9 @@ module Representable
|
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
42
|
def from_xml(doc, *args)
|
40
|
-
node =
|
43
|
+
node = parse_xml(doc, *args)
|
44
|
+
|
41
45
|
from_node(node, *args)
|
42
46
|
end
|
43
47
|
|
@@ -55,5 +59,18 @@ module Representable
|
|
55
59
|
def to_xml(*args)
|
56
60
|
to_node(*args).to_s
|
57
61
|
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def remove_namespaces?
|
65
|
+
# TODO: make local Config easily extendable so you get Config#remove_ns? etc.
|
66
|
+
representable_attrs.options[:remove_namespaces]
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_xml(doc, *args)
|
70
|
+
node = Nokogiri::XML(doc)
|
71
|
+
|
72
|
+
node.remove_namespaces! if remove_namespaces?
|
73
|
+
node.root
|
74
|
+
end
|
58
75
|
end
|
59
76
|
end
|
data/test/config_test.rb
CHANGED
@@ -45,6 +45,14 @@ class ConfigTest < MiniTest::Spec
|
|
45
45
|
it { subject["title"].must_equal definition }
|
46
46
|
end
|
47
47
|
|
48
|
+
describe "#options" do
|
49
|
+
it { subject.options.must_equal({}) }
|
50
|
+
it do
|
51
|
+
subject.options[:namespacing] = true
|
52
|
+
subject.options[:namespacing].must_equal true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
48
56
|
describe "Config inheritance" do
|
49
57
|
# TODO: this section will soon be moved to uber.
|
50
58
|
describe "inheritance when including" do
|
data/test/json_test.rb
CHANGED
@@ -127,10 +127,6 @@ module JsonTest
|
|
127
127
|
end
|
128
128
|
|
129
129
|
describe "#build_for" do
|
130
|
-
it "returns ObjectBinding" do
|
131
|
-
assert_kind_of Representable::Hash::ObjectBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :class => Hash), nil)
|
132
|
-
end
|
133
|
-
|
134
130
|
it "returns TextBinding" do
|
135
131
|
assert_kind_of Representable::Hash::PropertyBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band), nil)
|
136
132
|
end
|
data/test/xml_test.rb
CHANGED
@@ -80,6 +80,18 @@ class XmlTest < MiniTest::Spec
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
+
describe "#from_xml with remove_namespaces! and xmlns present" do
|
84
|
+
before do
|
85
|
+
@Band.remove_namespaces!
|
86
|
+
@band = @Band.new
|
87
|
+
@xml = %{<band xmlns="exists"><name>Nofx</name><label>NOFX</label></band>}
|
88
|
+
end
|
89
|
+
|
90
|
+
it "parses with xmlns present" do
|
91
|
+
@band.from_xml(@xml)
|
92
|
+
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
93
|
+
end
|
94
|
+
end
|
83
95
|
|
84
96
|
describe "#from_node" do
|
85
97
|
before do
|
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: 1.7.
|
4
|
+
version: 1.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|