representable 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.textile +5 -0
- data/README.rdoc +3 -1
- data/lib/representable/bindings/xml_bindings.rb +18 -0
- data/lib/representable/hash_methods.rb +30 -0
- data/lib/representable/json/hash.rb +5 -16
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +4 -3
- data/lib/representable/xml/hash.rb +27 -14
- data/test/json_test.rb +25 -5
- data/test/xml_test.rb +38 -0
- metadata +3 -2
data/CHANGES.textile
CHANGED
@@ -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@.
|
data/README.rdoc
CHANGED
@@ -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
|
21
|
-
|
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
|
data/lib/representable/xml.rb
CHANGED
@@ -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)
|
9
|
-
return HashBinding.new(definition)
|
10
|
-
return
|
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
|
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
|
21
|
-
|
22
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
|
40
|
+
module ClassMethods
|
41
|
+
def values(options)
|
42
|
+
hash :_self, options
|
43
|
+
end
|
29
44
|
end
|
30
45
|
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
data/test/json_test.rb
CHANGED
@@ -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
|
-
|
506
|
-
|
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
|
-
|
510
|
-
|
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
|
|
data/test/xml_test.rb
CHANGED
@@ -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.
|
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-
|
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
|