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 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