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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8702c711c2230d338b5390e3e10cd7af901d41cf
4
- data.tar.gz: fb5fd9498c58b6047a3ec221950f76c39d633c42
3
+ metadata.gz: 57ca6d3739a88e3c3e38ecb6b4aa0b33b5da43a4
4
+ data.tar.gz: fc6f1765a152cbbb9300f01c60ae8f55c169ec6d
5
5
  SHA512:
6
- metadata.gz: 1a66be6a8a40fa284c674ae0ab63aa77e48cf9257be1de100a474cc0a14bffe2271172104b190f7fc9987eb6927402db5e9f24953d670a3733b619fef3ac2ae9
7
- data.tar.gz: 7b89c5a30d057d6e47ebd34f2881acc7d94df6e25e4a42692ce38b92e457aff7a96b1be12bb25312d997fdf6c58cbad46af2906487b7badf78e58e99925a47b5
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
- module ObjectBinding
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
- deserialize_from(fragment)
18
+ deserialize(fragment)
35
19
  end
36
20
 
37
21
  def write(hash, value)
38
- hash[from] = serialize_for(value)
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 serialize_for(value)
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| serialize(item) } # TODO: i don't want Array but Forms here - what now?
41
+ value.collect { |item| super(item) } # TODO: i don't want Array but Forms here - what now?
54
42
  end
55
43
 
56
- def deserialize_from(fragment)
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 serialize_for(value)
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] = serialize(obj) }
54
+ value.each { |key, obj| hsh[key] = super(obj) }
67
55
  end
68
56
  end
69
57
 
70
- def deserialize_from(fragment)
58
+ def deserialize(fragment)
71
59
  {}.tap do |hsh|
72
- fragment.each { |key, item_fragment| hsh[key] = deserialize(item_fragment) }
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
- module ObjectBinding
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 << serialize_for(value) # FIXME: should be serialize.
13
+ map.children << serialize(value) # FIXME: should be serialize.
35
14
  end
36
15
 
37
- def serialize_for(value)
38
- write_scalar serialize(value)
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 serialize_for(value)
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 deserialize_from(fragment) # FIXME: redundant from Hash::Bindings
44
+ def deserialize(fragment) # FIXME: redundant from Hash::Bindings
56
45
  CollectionDeserializer.new(self).deserialize(fragment)
57
46
  end
58
47
  end
@@ -40,6 +40,11 @@ module Representable
40
40
  wrap
41
41
  end
42
42
 
43
+ # Write representer configuration into this hash.
44
+ def options
45
+ @options ||= {}
46
+ end
47
+
43
48
  module InheritMethods
44
49
  def cloned
45
50
  collect { |d| d.clone }
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.7.0"
2
+ VERSION = "1.7.1"
3
3
  end
@@ -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 = Nokogiri::XML(doc).root
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.0
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-08-31 00:00:00.000000000 Z
11
+ date: 2013-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri