representable 1.2.1 → 1.2.2

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.
@@ -1,3 +1,8 @@
1
+ h2. 1.2.2
2
+
3
+ * Added @XML::AttributeHash@ to store hash key-value pairs in attributes instead of dedicated tags.
4
+ * @JSON::Hash@, @XML::Hash@ and @XML::AttributeHash@ now respect @:exclude@ and @:include@ when parsing and rendering.
5
+
1
6
  h2. 1.2.1
2
7
 
3
8
  * Deprecated @:represent_nil@ favor of @:render_nil@.
@@ -185,7 +185,7 @@ You can use #items to configure the element representations contained in the arr
185
185
  items :class => Hero, :extend => HeroRepresenter
186
186
  end
187
187
 
188
- Collections and hashes can also be deserialized.
188
+ Collections and hashes can also be deserialized. Note that this also works for XML.
189
189
 
190
190
  == Lonely Hashes
191
191
 
@@ -199,6 +199,8 @@ The same goes with hashes where #values lets you configure the hash's values.
199
199
 
200
200
  {:stu => Hero.new("Stu"), :clive => Hero.new("Cleavage")}.extend(FriendsRepresenter).to_json
201
201
 
202
+ In XML, if you want to store hash attributes in tag attributes instead of dedicated nodes, use XML::AttributeHash.
203
+
202
204
 
203
205
  == Customizing
204
206
 
@@ -110,6 +110,24 @@ module Representable
110
110
  end
111
111
  end
112
112
 
113
+ class AttributeHashBinding < CollectionBinding
114
+ # DISCUSS: use AttributeBinding here?
115
+ def write(parent, value) # DISCUSS: is it correct overriding #write here?
116
+ value.collect do |k, v|
117
+ parent[k] = serialize(v.to_s)
118
+ end
119
+ parent
120
+ end
121
+
122
+ def deserialize_from(node)
123
+ {}.tap do |hash|
124
+ node.each do |k,v|
125
+ hash[k] = deserialize(v)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
113
131
 
114
132
  # Represents a tag attribute. Currently this only works on the top-level tag.
115
133
  class AttributeBinding < PropertyBinding
@@ -0,0 +1,30 @@
1
+ module Representable
2
+ module HashMethods
3
+ # FIXME: refactor Definition so we can simply add options in #items to existing definition.
4
+ def representable_attrs
5
+ attrs = super
6
+ attrs << Definition.new(*definition_opts) if attrs.size == 0
7
+ attrs
8
+ end
9
+
10
+ def create_representation_with(doc, options, format)
11
+ bin = representable_bindings_for(format).first
12
+ hash = filter_keys_for(self, options)
13
+ bin.write(doc, hash)
14
+ end
15
+
16
+ def update_properties_from(doc, options, format)
17
+ bin = representable_bindings_for(format).first
18
+ hash = filter_keys_for(doc, options)
19
+ value = bin.deserialize_from(hash)
20
+ replace(value)
21
+ self
22
+ end
23
+
24
+ private
25
+ def filter_keys_for(hash, options)
26
+ return hash unless props = options[:exclude] || options[:include]
27
+ hash.reject { |k,v| options[:exclude] ? props.include?(k.to_sym) : !props.include?(k.to_sym) }
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,9 @@
1
+ require 'representable/hash_methods'
2
+
1
3
  module Representable::JSON
2
4
  module Hash
3
5
  include Representable::JSON
6
+ include HashMethods
4
7
 
5
8
  def self.included(base)
6
9
  base.class_eval do
@@ -17,22 +20,8 @@ module Representable::JSON
17
20
  end
18
21
 
19
22
 
20
- def create_representation_with(doc, options, format)
21
- bin = representable_bindings_for(format).first
22
- bin.serialize_for(self)
23
- end
24
-
25
- def update_properties_from(doc, options, format)
26
- bin = representable_bindings_for(format).first
27
- value = bin.deserialize_from(doc)
28
- replace(value)
29
- end
30
-
31
- # FIXME: refactor Definition so we can simply add options in #items to existing definition.
32
- def representable_attrs
33
- attrs = super
34
- attrs << Definition.new(:_self, :hash => true) if attrs.size == 0
35
- attrs
23
+ def definition_opts
24
+ [:_self, :hash => true, :use_attributes => true]
36
25
  end
37
26
  end
38
27
  end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
@@ -5,9 +5,10 @@ require 'nokogiri'
5
5
  module Representable
6
6
  module XML
7
7
  def self.binding_for_definition(definition)
8
- return CollectionBinding.new(definition) if definition.array?
9
- return HashBinding.new(definition) if definition.hash?
10
- return AttributeBinding.new(definition) if definition.attribute
8
+ return CollectionBinding.new(definition) if definition.array?
9
+ return HashBinding.new(definition) if definition.hash? and not definition.options[:use_attributes] # FIXME: hate this.
10
+ return AttributeHashBinding.new(definition) if definition.hash? and definition.options[:use_attributes]
11
+ return AttributeBinding.new(definition) if definition.attribute
11
12
  PropertyBinding.new(definition)
12
13
  end
13
14
 
@@ -1,6 +1,9 @@
1
+ require 'representable/hash_methods'
2
+
1
3
  module Representable::XML
2
- module Hash
4
+ module AttributeHash
3
5
  include Representable::XML
6
+ include Representable::HashMethods
4
7
 
5
8
  def self.included(base)
6
9
  base.class_eval do
@@ -12,27 +15,37 @@ module Representable::XML
12
15
 
13
16
  module ClassMethods
14
17
  def values(options)
15
- hash :_self, options
18
+ hash :_self, options.merge!(:use_attributes => true)
16
19
  end
17
20
  end
18
21
 
19
22
 
20
- def create_representation_with(doc, options, format)
21
- bin = representable_bindings_for(format).first
22
- bin.serialize_for(self)
23
+ def definition_opts
24
+ [:_self, :hash => true, :use_attributes => true]
25
+ end
26
+ end
27
+
28
+ module Hash
29
+ include Representable::XML
30
+ include HashMethods
31
+
32
+ def self.included(base)
33
+ base.class_eval do
34
+ include Representable
35
+ extend ClassMethods
36
+ end
23
37
  end
24
38
 
25
- def update_properties_from(doc, options, format)
26
- bin = representable_bindings_for(format).first
27
- value = bin.deserialize_from(doc)
28
- replace(value)
39
+
40
+ module ClassMethods
41
+ def values(options)
42
+ hash :_self, options
43
+ end
29
44
  end
30
45
 
31
- # FIXME: refactor Definition so we can simply add options in #items to existing definition.
32
- def representable_attrs
33
- attrs = super
34
- attrs << Definition.new(:_self, :hash => true) if attrs.size == 0
35
- attrs
46
+
47
+ def definition_opts
48
+ [:_self, :hash => true]
36
49
  end
37
50
  end
38
51
  end
@@ -493,7 +493,7 @@ end
493
493
  property :name
494
494
  end
495
495
 
496
- describe "JSON::Hash" do
496
+ describe "JSON::Hash" do # TODO: move to HashTest.
497
497
  describe "with contained objects" do
498
498
  before do
499
499
  @songs_representer = Module.new do
@@ -502,12 +502,32 @@ end
502
502
  end
503
503
  end
504
504
 
505
- it "renders objects with #to_json" do
506
- assert_equal "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json
505
+ describe "#to_json" do
506
+ it "renders objects" do
507
+ assert_equal "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json
508
+ end
509
+
510
+ it "respects :exclude" do
511
+ assert_equal "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:exclude => [:one])
512
+ end
513
+
514
+ it "respects :include" do
515
+ assert_equal "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(@songs_representer).to_json(:include => [:two])
516
+ end
507
517
  end
508
518
 
509
- it "returns objects array from #from_json" do
510
- assert_equal({"one" => Song.new("Days Go By"), "two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}"))
519
+ describe "#from_json" do
520
+ it "returns objects array" do
521
+ assert_equal({"one" => Song.new("Days Go By"), "two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}"))
522
+ end
523
+
524
+ it "respects :exclude" do
525
+ assert_equal({"two" => Song.new("Can't Take Them All")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", :exclude => [:one]))
526
+ end
527
+
528
+ it "respects :include" do
529
+ assert_equal({"one" => Song.new("Days Go By")}, {}.extend(@songs_representer).from_json("{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}", :include => [:one]))
530
+ end
511
531
  end
512
532
  end
513
533
 
@@ -336,4 +336,42 @@ class CollectionTest < MiniTest::Spec
336
336
  assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
337
337
  end
338
338
  end
339
+
340
+ require 'representable/xml/hash'
341
+ describe "XML::AttributeHash" do # TODO: move to HashTest.
342
+ before do
343
+ @songs_representer = Module.new do
344
+ include Representable::XML::AttributeHash
345
+ self.representation_wrap= :favs
346
+ end
347
+ end
348
+
349
+ describe "#to_xml" do
350
+ it "renders values into attributes converting values to strings" do
351
+ assert_xml_equal "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml
352
+ end
353
+
354
+ it "respects :exclude" do
355
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml(:exclude => [:one])
356
+ end
357
+
358
+ it "respects :include" do
359
+ assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(@songs_representer).to_xml(:include => [:two])
360
+ end
361
+ end
362
+
363
+ describe "#from_json" do
364
+ it "returns hash" do
365
+ assert_equal({"one" => "Graveyards", "two" => "Can't Take Them All"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />"))
366
+ end
367
+
368
+ it "respects :exclude" do
369
+ assert_equal({"two" => "Can't Take Them All"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :exclude => [:one]))
370
+ end
371
+
372
+ it "respects :include" do
373
+ assert_equal({"one" => "Graveyards"}, {}.extend(@songs_representer).from_xml("<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", :include => [:one]))
374
+ end
375
+ end
376
+ end
339
377
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-01 00:00:00.000000000 Z
12
+ date: 2012-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -162,6 +162,7 @@ files:
162
162
  - lib/representable/coercion.rb
163
163
  - lib/representable/definition.rb
164
164
  - lib/representable/deprecations.rb
165
+ - lib/representable/hash_methods.rb
165
166
  - lib/representable/json.rb
166
167
  - lib/representable/json/collection.rb
167
168
  - lib/representable/json/hash.rb