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