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