representable 1.2.1 → 1.2.2

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