representable 0.0.2 → 0.9.0
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 +7 -1
- data/Rakefile +3 -0
- data/lib/representable.rb +4 -8
- data/lib/representable/bindings/json_bindings.rb +8 -8
- data/lib/representable/bindings/xml_bindings.rb +14 -30
- data/lib/representable/definition.rb +17 -37
- data/lib/representable/json.rb +5 -4
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +2 -3
- data/representable.gemspec +1 -2
- data/test/bindings_test.rb +20 -16
- data/test/json_test.rb +86 -33
- data/test/representable_test.rb +15 -0
- data/test/xml_test.rb +2 -7
- metadata +12 -24
data/CHANGES.textile
ADDED
data/README.rdoc
CHANGED
|
@@ -29,7 +29,7 @@ Now, parsing manually starts simple but gets complex soon. In the end, you have
|
|
|
29
29
|
include Representable::XML
|
|
30
30
|
|
|
31
31
|
representable_property :id
|
|
32
|
-
representable_collection :items, :
|
|
32
|
+
representable_collection :items, :from => :item, :as => Item
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
class Item
|
|
@@ -73,6 +73,12 @@ You can also render a representation from a newly created object.
|
|
|
73
73
|
|
|
74
74
|
Representable makes working with representations extremely easy and testable.
|
|
75
75
|
|
|
76
|
+
|
|
77
|
+
== Serialization Backends
|
|
78
|
+
|
|
79
|
+
This gem ships with JSON and XML support, meaning you can parse and render both media formats.
|
|
80
|
+
|
|
81
|
+
|
|
76
82
|
== More
|
|
77
83
|
|
|
78
84
|
Representable was written with REST representations in mind. However, it is a generic module for working with documents. If you do consider using it for a REST project, check out the {Roar framework}[http://github.com/apotonick/roar], which comes with representers, built-in hypermedia support and more. It internally uses Representable and streamlines the process for building hypermedia-driven REST applications.
|
data/Rakefile
CHANGED
data/lib/representable.rb
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
require 'active_support'
|
|
2
|
-
require 'active_support/core_ext/module/delegation'
|
|
3
|
-
require 'active_support/core_ext/string/inflections.rb'
|
|
4
|
-
require 'active_support/core_ext/hash/reverse_merge.rb'
|
|
5
|
-
|
|
6
1
|
require 'hooks/inheritable_attribute'
|
|
7
2
|
|
|
8
|
-
|
|
9
3
|
require 'representable/definition'
|
|
10
4
|
require 'representable/nokogiri_extensions'
|
|
11
5
|
|
|
12
|
-
#require 'representable/xml' # TODO: do that dynamically.
|
|
13
6
|
|
|
14
7
|
module Representable
|
|
15
8
|
def self.included(base)
|
|
@@ -226,7 +219,10 @@ module Representable
|
|
|
226
219
|
end
|
|
227
220
|
|
|
228
221
|
def representation_name
|
|
229
|
-
explicit_representation_name or name.split('::').last.
|
|
222
|
+
explicit_representation_name or name.split('::').last.
|
|
223
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
224
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
225
|
+
downcase
|
|
230
226
|
end
|
|
231
227
|
end
|
|
232
228
|
end
|
|
@@ -2,7 +2,7 @@ module Representable
|
|
|
2
2
|
module JSON
|
|
3
3
|
class Binding
|
|
4
4
|
attr_reader :definition
|
|
5
|
-
delegate :required?, :array?, :accessor, :
|
|
5
|
+
#delegate :required?, :array?, :accessor, :from, :to => :definition
|
|
6
6
|
|
|
7
7
|
def initialize(definition)
|
|
8
8
|
@definition = definition
|
|
@@ -18,19 +18,19 @@ module Representable
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def collect_for(hash)
|
|
21
|
-
nodes = hash[
|
|
21
|
+
nodes = hash[definition.from] or return
|
|
22
22
|
nodes = [nodes] unless nodes.is_a?(Array)
|
|
23
23
|
|
|
24
24
|
vals = nodes.collect { |node| yield node }
|
|
25
25
|
|
|
26
|
-
array? ? vals : vals.first
|
|
26
|
+
definition.array? ? vals : vals.first
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Represents plain key-value.
|
|
31
31
|
class TextBinding < Binding
|
|
32
32
|
def update_json(hash, value)
|
|
33
|
-
hash[
|
|
33
|
+
hash[definition.from] = value
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
private
|
|
@@ -43,11 +43,11 @@ module Representable
|
|
|
43
43
|
|
|
44
44
|
# Represents a tag with object binding.
|
|
45
45
|
class ObjectBinding < Binding
|
|
46
|
-
delegate :sought_type, :to => :definition
|
|
46
|
+
#delegate :sought_type, :to => :definition
|
|
47
47
|
|
|
48
48
|
def update_json(hash, value)
|
|
49
|
-
if array?
|
|
50
|
-
hash.merge! ({
|
|
49
|
+
if definition.array?
|
|
50
|
+
hash.merge! ({definition.from => value.collect {|v| v.to_hash(:wrap => false)}}) # hier name=> wech.
|
|
51
51
|
else
|
|
52
52
|
hash.merge! value.to_hash
|
|
53
53
|
end
|
|
@@ -60,7 +60,7 @@ module Representable
|
|
|
60
60
|
|
|
61
61
|
def value_from_hash(xml)
|
|
62
62
|
collect_for(xml) do |node|
|
|
63
|
-
sought_type.from_json(node, :wrap => false) # hier name=> wech.
|
|
63
|
+
definition.sought_type.from_json(node, :wrap => false) # hier name=> wech.
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -2,7 +2,6 @@ module Representable
|
|
|
2
2
|
module XML
|
|
3
3
|
class Binding
|
|
4
4
|
attr_reader :definition
|
|
5
|
-
delegate :required?, :array?, :accessor, :wrapper, :name, :to => :definition
|
|
6
5
|
|
|
7
6
|
def initialize(definition)
|
|
8
7
|
@definition = definition
|
|
@@ -20,11 +19,11 @@ module Representable
|
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
def xpath
|
|
23
|
-
|
|
22
|
+
definition.from
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def wrap(xml, opts = {:always_create => false})
|
|
27
|
-
wrap_with = @auto_vals ? auto_wrapper : wrapper
|
|
26
|
+
wrap_with = @auto_vals ? auto_wrapper : definition.wrapper
|
|
28
27
|
|
|
29
28
|
return xml if !wrap_with || xml.name == wrap_with
|
|
30
29
|
if !opts[:always_create] && (child = xml.children.find {|c| c.name == wrap_with })
|
|
@@ -37,7 +36,7 @@ module Representable
|
|
|
37
36
|
nodes = xml.search("./#{xpath}")
|
|
38
37
|
vals = nodes.collect { |node| yield node }
|
|
39
38
|
|
|
40
|
-
array? ? vals : vals.first
|
|
39
|
+
definition.array? ? vals : vals.first
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
|
|
@@ -46,35 +45,33 @@ module Representable
|
|
|
46
45
|
class AttributeBinding < Binding
|
|
47
46
|
def update_xml(xml, values)
|
|
48
47
|
wrap(xml).tap do |xml|
|
|
49
|
-
xml[
|
|
48
|
+
xml[definition.from] = values.to_s
|
|
50
49
|
end
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
private
|
|
54
53
|
def value_from_node(xml)
|
|
55
|
-
xml[
|
|
54
|
+
xml[definition.from]
|
|
56
55
|
end
|
|
57
56
|
end
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
# Represents text content in a tag. # FIXME: is this tested???
|
|
61
60
|
class TextBinding < Binding
|
|
62
|
-
delegate :cdata?, :content?, :name?, :to => :definition
|
|
63
|
-
|
|
64
61
|
# Updates the text in the given _xml_ block to
|
|
65
62
|
# the _value_ provided.
|
|
66
63
|
def update_xml(xml, value)
|
|
67
64
|
wrap(xml).tap do |xml|
|
|
68
|
-
if content?
|
|
65
|
+
if definition.content?
|
|
69
66
|
add(xml, value)
|
|
70
|
-
elsif name?
|
|
67
|
+
elsif definition.name?
|
|
71
68
|
xml.name = value
|
|
72
|
-
elsif array?
|
|
69
|
+
elsif definition.array?
|
|
73
70
|
value.each do |v|
|
|
74
|
-
add(xml.add_node(
|
|
71
|
+
add(xml.add_node(definition.from), v)
|
|
75
72
|
end
|
|
76
73
|
else
|
|
77
|
-
add(xml.add_node(
|
|
74
|
+
add(xml.add_node(definition.from), value)
|
|
78
75
|
end
|
|
79
76
|
end
|
|
80
77
|
end
|
|
@@ -87,30 +84,17 @@ module Representable
|
|
|
87
84
|
end
|
|
88
85
|
|
|
89
86
|
def add(dest, value)
|
|
90
|
-
|
|
91
|
-
dest.add_child(Nokogiri::XML::CDATA.new(dest.document, content))
|
|
92
|
-
else
|
|
93
|
-
dest.content = value.to_s
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class NamespaceBinding < Binding
|
|
100
|
-
private
|
|
101
|
-
def value_from_node(xml)
|
|
102
|
-
xml.namespace.prefix
|
|
87
|
+
dest.content = value.to_s
|
|
103
88
|
end
|
|
104
89
|
end
|
|
90
|
+
|
|
105
91
|
|
|
106
92
|
# Represents a tag with object binding.
|
|
107
93
|
class ObjectBinding < Binding
|
|
108
|
-
delegate :sought_type, :to => :definition
|
|
109
|
-
|
|
110
94
|
# Adds the ref's markup to +xml+.
|
|
111
95
|
def update_xml(xml, value)
|
|
112
96
|
wrap(xml).tap do |xml|
|
|
113
|
-
if array?
|
|
97
|
+
if definition.array?
|
|
114
98
|
update_xml_for_collection(xml, value)
|
|
115
99
|
else
|
|
116
100
|
update_xml_for_entity(xml, value)
|
|
@@ -134,7 +118,7 @@ module Representable
|
|
|
134
118
|
# Deserializes the ref's element from +xml+.
|
|
135
119
|
def value_from_node(xml)
|
|
136
120
|
collect_for(xml) do |node|
|
|
137
|
-
deserialize(sought_type, node)
|
|
121
|
+
deserialize(definition.sought_type, node)
|
|
138
122
|
end
|
|
139
123
|
end
|
|
140
124
|
|
|
@@ -1,38 +1,16 @@
|
|
|
1
|
-
class Module
|
|
2
|
-
def bool_attr_reader(*attrs)
|
|
3
|
-
attrs.each do |attr|
|
|
4
|
-
define_method :"#{attr}?" do
|
|
5
|
-
instance_variable_get(:"@#{attr}") || false
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
1
|
module Representable
|
|
12
|
-
class ContradictoryNamespaces < StandardError
|
|
13
|
-
end
|
|
14
2
|
class Definition # :nodoc:
|
|
15
|
-
attr_reader :name, :sought_type, :wrapper, :accessor, :
|
|
16
|
-
|
|
3
|
+
attr_reader :name, :sought_type, :wrapper, :accessor, :from
|
|
4
|
+
|
|
17
5
|
|
|
18
6
|
def initialize(sym, opts={})
|
|
19
|
-
@accessor
|
|
20
|
-
@namespace = opts.delete(:namespace)
|
|
7
|
+
@accessor = @name = sym.to_s
|
|
21
8
|
|
|
9
|
+
@array = true if opts[:as].is_a?(Array) # DISCUSS: move to ArrayDefinition.
|
|
10
|
+
@from = (opts[:from] || name).to_s
|
|
11
|
+
@sought_type = extract_type(opts[:as])
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
@array = true
|
|
25
|
-
@name = (opts[:tag] || @accessor).to_s
|
|
26
|
-
else
|
|
27
|
-
@name = accessor
|
|
28
|
-
@name = (opts[:from] || @name).to_s
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
@sought_type = extract_type(opts[:as])
|
|
32
|
-
if @sought_type.respond_to?(:roxml_tag_name)
|
|
33
|
-
opts[:from] ||= @sought_type.roxml_tag_name
|
|
34
|
-
end
|
|
35
|
-
|
|
13
|
+
# FIXME: move me to xml.
|
|
36
14
|
if opts[:from] == :content
|
|
37
15
|
opts[:from] = '.'
|
|
38
16
|
elsif opts[:from] == :name
|
|
@@ -40,17 +18,10 @@ module Representable
|
|
|
40
18
|
elsif opts[:from] == :attr
|
|
41
19
|
@sought_type = :attr
|
|
42
20
|
opts[:from] = nil
|
|
43
|
-
elsif opts[:from]
|
|
44
|
-
opts[:from] = '*'
|
|
45
|
-
@sought_type = :namespace
|
|
46
|
-
elsif opts[:from].to_s =~ /^@/ # FIXME: move me to xml.
|
|
21
|
+
elsif opts[:from].to_s =~ /^@/
|
|
47
22
|
@sought_type = :attr
|
|
48
23
|
opts[:from].sub!('@', '')
|
|
49
24
|
end
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#raise ContradictoryNamespaces if @name.include?(':') && (@namespace.present? || @namespace == false)
|
|
53
|
-
|
|
54
25
|
end
|
|
55
26
|
|
|
56
27
|
def instance_variable_name
|
|
@@ -73,6 +44,15 @@ module Representable
|
|
|
73
44
|
@name == '.'
|
|
74
45
|
end
|
|
75
46
|
|
|
47
|
+
def array?
|
|
48
|
+
@array
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def cdata? # FIXME: move to XML!
|
|
52
|
+
@cdata
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
76
56
|
# Applies the block to +value+ which might also be a collection.
|
|
77
57
|
def apply(value)
|
|
78
58
|
return value unless value # DISCUSS: is that ok here?
|
data/lib/representable/json.rb
CHANGED
|
@@ -26,9 +26,10 @@ module Representable
|
|
|
26
26
|
def from_json(data, options={})
|
|
27
27
|
# DISCUSS: extract #from_json call in Bindings to this place.
|
|
28
28
|
data = ::JSON[data] if data.is_a?(String) # DISCUSS: #from_json sometimes receives a string (in nestings).
|
|
29
|
-
data ||= {}
|
|
30
|
-
|
|
29
|
+
data ||= {} # DISCUSS: is this needed?
|
|
31
30
|
data = data[representation_name.to_s] unless options[:wrap] == false
|
|
31
|
+
data ||= {} # FIXME: should we fail here? generate a warning?
|
|
32
|
+
|
|
32
33
|
|
|
33
34
|
create_from_json.tap do |inst|
|
|
34
35
|
refs = representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
|
|
@@ -53,7 +54,7 @@ module Representable
|
|
|
53
54
|
refs = self.class.representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
|
|
54
55
|
|
|
55
56
|
refs.each do |ref|
|
|
56
|
-
value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
|
|
57
|
+
value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
|
|
57
58
|
ref.update_json(attrs, value) if value
|
|
58
59
|
end
|
|
59
60
|
end
|
|
@@ -67,5 +68,5 @@ module Representable
|
|
|
67
68
|
to_hash(options).to_json
|
|
68
69
|
end
|
|
69
70
|
end
|
|
70
|
-
end
|
|
71
|
+
end
|
|
71
72
|
end
|
data/lib/representable/xml.rb
CHANGED
|
@@ -6,7 +6,6 @@ module Representable
|
|
|
6
6
|
BINDING_FOR_TYPE = {
|
|
7
7
|
:attr => AttributeBinding,
|
|
8
8
|
:text => TextBinding,
|
|
9
|
-
:namespace=> NamespaceBinding,
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
def self.binding_for_definition(definition)
|
|
@@ -57,13 +56,13 @@ module Representable
|
|
|
57
56
|
module InstanceMethods # :nodoc:
|
|
58
57
|
# Returns a Nokogiri::XML object representing this object.
|
|
59
58
|
def to_xml(params={})
|
|
60
|
-
params
|
|
59
|
+
params[:name] ||= self.class.representation_name
|
|
61
60
|
|
|
62
61
|
Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
|
|
63
62
|
refs = self.class.representable_attrs.map {|attr| XML.binding_for_definition(attr) }
|
|
64
63
|
|
|
65
64
|
refs.each do |ref|
|
|
66
|
-
value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
|
|
65
|
+
value = public_send(ref.definition.accessor) # DISCUSS: eventually move back to Ref.
|
|
67
66
|
ref.update_xml(root, value) if value
|
|
68
67
|
end
|
|
69
68
|
end
|
data/representable.gemspec
CHANGED
|
@@ -19,12 +19,11 @@ Gem::Specification.new do |s|
|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
20
|
s.require_paths = ["lib"]
|
|
21
21
|
|
|
22
|
-
s.add_dependency "activesupport"
|
|
23
22
|
s.add_dependency "hooks"
|
|
24
23
|
s.add_dependency "nokogiri"
|
|
25
|
-
s.add_dependency "i18n"
|
|
26
24
|
s.add_dependency "json"
|
|
27
25
|
|
|
26
|
+
s.add_development_dependency "rake"
|
|
28
27
|
s.add_development_dependency "rspec"
|
|
29
28
|
s.add_development_dependency "test_xml"
|
|
30
29
|
end
|
data/test/bindings_test.rb
CHANGED
|
@@ -36,6 +36,26 @@ class DefinitionTest < MiniTest::Spec
|
|
|
36
36
|
assert Representable::Definition.new(:songs, :as => Hash).typed?
|
|
37
37
|
assert Representable::Definition.new(:songs, :as => [Hash]).typed?
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
it "responds to #accessor" do
|
|
41
|
+
assert_equal "songs", @def.accessor
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "responds to #name" do
|
|
45
|
+
assert_equal "songs", @def.name
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "responds to #instance_variable_name" do
|
|
49
|
+
assert_equal :"@songs", @def.instance_variable_name
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "responds to #setter" do
|
|
53
|
+
assert_equal :"songs=", @def.setter
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "responds to #sought_type" do
|
|
57
|
+
assert_equal :text, @def.sought_type
|
|
58
|
+
end
|
|
39
59
|
end
|
|
40
60
|
|
|
41
61
|
|
|
@@ -61,26 +81,10 @@ class DefinitionTest < MiniTest::Spec
|
|
|
61
81
|
@def = Representable::Definition.new(:songs, :as => [], :tag => :song)
|
|
62
82
|
end
|
|
63
83
|
|
|
64
|
-
it "responds to #accessor" do
|
|
65
|
-
assert_equal "songs", @def.accessor
|
|
66
|
-
end
|
|
67
|
-
|
|
68
84
|
it "responds to #array?" do
|
|
69
85
|
assert @def.array?
|
|
70
86
|
end
|
|
71
87
|
|
|
72
|
-
it "responds to #name" do
|
|
73
|
-
assert_equal "songs", @def.accessor
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "responds to #instance_variable_name" do
|
|
77
|
-
assert_equal :"@songs", @def.instance_variable_name
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it "responds to #setter" do
|
|
81
|
-
assert_equal :"songs=", @def.setter
|
|
82
|
-
end
|
|
83
|
-
|
|
84
88
|
it "responds to #sought_type" do
|
|
85
89
|
assert_equal :text, @def.sought_type
|
|
86
90
|
end
|
data/test/json_test.rb
CHANGED
|
@@ -6,7 +6,7 @@ module JsonTest
|
|
|
6
6
|
Json = Representable::JSON
|
|
7
7
|
Def = Representable::Definition
|
|
8
8
|
|
|
9
|
-
describe "
|
|
9
|
+
describe "JSON module" do
|
|
10
10
|
describe "#binding_for_definition" do
|
|
11
11
|
it "returns ObjectBinding" do
|
|
12
12
|
assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :as => Hash))
|
|
@@ -16,11 +16,32 @@ module JsonTest
|
|
|
16
16
|
assert_kind_of Json::TextBinding, Json.binding_for_definition(Def.new(:band))
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
describe "#from_json" do
|
|
21
|
+
class Band
|
|
22
|
+
include Json
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "raises error with emtpy string" do
|
|
26
|
+
assert_raises JSON::ParserError do
|
|
27
|
+
Band.from_json("")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "returns empty hash with inappropriate hash" do
|
|
32
|
+
assert Band.from_json({:song => "Message In A Bottle"}.to_json)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "generates warning with inappropriate hash in debugging mode" do
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
19
40
|
end
|
|
20
41
|
end
|
|
21
42
|
|
|
22
43
|
class PropertyTest < MiniTest::Spec
|
|
23
|
-
describe "
|
|
44
|
+
describe "representable_property :name" do
|
|
24
45
|
class Band
|
|
25
46
|
include Representable::JSON
|
|
26
47
|
representable_property :name
|
|
@@ -36,7 +57,6 @@ module JsonTest
|
|
|
36
57
|
assert_equal "Bombshell Rocks", band.name
|
|
37
58
|
end
|
|
38
59
|
|
|
39
|
-
|
|
40
60
|
it "#to_json serializes correctly" do
|
|
41
61
|
band = Band.new
|
|
42
62
|
band.name = "Cigar"
|
|
@@ -45,27 +65,6 @@ module JsonTest
|
|
|
45
65
|
end
|
|
46
66
|
end
|
|
47
67
|
|
|
48
|
-
describe "property :name, :as => []" do
|
|
49
|
-
class CD
|
|
50
|
-
include Representable::JSON
|
|
51
|
-
representable_property :songs, :as => []
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "#from_json creates correct accessors" do
|
|
55
|
-
cd = CD.from_json({:cd => {:songs => ["Out in the cold", "Microphone"]}}.to_json)
|
|
56
|
-
assert_equal ["Out in the cold", "Microphone"], cd.songs
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "#to_json serializes correctly" do
|
|
60
|
-
cd = CD.new
|
|
61
|
-
cd.songs = ["Out in the cold", "Microphone"]
|
|
62
|
-
|
|
63
|
-
assert_equal '{"cd":{"songs":["Out in the cold","Microphone"]}}', cd.to_json
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
class TypedPropertyTest < MiniTest::Spec
|
|
69
68
|
describe ":as => Item" do
|
|
70
69
|
class Label
|
|
71
70
|
include Representable::JSON
|
|
@@ -82,20 +81,54 @@ module JsonTest
|
|
|
82
81
|
assert_equal "Bad Religion", album.label.name
|
|
83
82
|
end
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
it "#to_json serializes" do
|
|
85
|
+
label = Label.new; label.name = "Fat Wreck"
|
|
86
|
+
album = Album.new; album.label = label
|
|
87
|
+
|
|
88
|
+
assert_equal '{"album":{"label":{"name":"Fat Wreck"}}}', album.to_json
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe ":from => :songName" do
|
|
93
|
+
class Song
|
|
94
|
+
include Representable::JSON
|
|
95
|
+
representable_property :name, :from => :songName
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "respects :from in #from_json" do
|
|
99
|
+
song = Song.from_json({:song => {:songName => "Run To The Hills"}}.to_json)
|
|
100
|
+
assert_equal "Run To The Hills", song.name
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "respects :from in #to_json" do
|
|
104
|
+
song = Song.new; song.name = "Run To The Hills"
|
|
105
|
+
assert_equal '{"song":{"songName":"Run To The Hills"}}', song.to_json
|
|
92
106
|
end
|
|
93
107
|
end
|
|
94
108
|
end
|
|
95
109
|
|
|
96
110
|
|
|
97
111
|
class CollectionTest < MiniTest::Spec
|
|
98
|
-
describe ":
|
|
112
|
+
describe "representable_collection :name" do
|
|
113
|
+
class CD
|
|
114
|
+
include Representable::JSON
|
|
115
|
+
representable_collection :songs
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "#from_json creates correct accessors" do
|
|
119
|
+
cd = CD.from_json({:cd => {:songs => ["Out in the cold", "Microphone"]}}.to_json)
|
|
120
|
+
assert_equal ["Out in the cold", "Microphone"], cd.songs
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "#to_json serializes correctly" do
|
|
124
|
+
cd = CD.new
|
|
125
|
+
cd.songs = ["Out in the cold", "Microphone"]
|
|
126
|
+
|
|
127
|
+
assert_equal '{"cd":{"songs":["Out in the cold","Microphone"]}}', cd.to_json
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "representable_collection :name, :as => Band" do
|
|
99
132
|
class Band
|
|
100
133
|
include Representable::JSON
|
|
101
134
|
representable_property :name
|
|
@@ -107,7 +140,7 @@ module JsonTest
|
|
|
107
140
|
|
|
108
141
|
class Compilation
|
|
109
142
|
include Representable::JSON
|
|
110
|
-
|
|
143
|
+
representable_collection :bands, :as => Band
|
|
111
144
|
end
|
|
112
145
|
|
|
113
146
|
describe "#from_json" do
|
|
@@ -131,5 +164,25 @@ module JsonTest
|
|
|
131
164
|
assert_equal '{"compilation":{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}}', cd.to_json
|
|
132
165
|
end
|
|
133
166
|
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
describe ":from => :songList" do
|
|
170
|
+
class Songs
|
|
171
|
+
include Representable::JSON
|
|
172
|
+
representable_collection :tracks, :from => :songList
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "respects :from in #from_json" do
|
|
176
|
+
songs = Songs.from_json({:songs => {:songList => ["Out in the cold", "Microphone"]}}.to_json)
|
|
177
|
+
assert_equal ["Out in the cold", "Microphone"], songs.tracks
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "respects option in #to_json" do
|
|
181
|
+
songs = Songs.new
|
|
182
|
+
songs.tracks = ["Out in the cold", "Microphone"]
|
|
183
|
+
|
|
184
|
+
assert_equal '{"songs":{"songList":["Out in the cold","Microphone"]}}', songs.to_json
|
|
185
|
+
end
|
|
186
|
+
end
|
|
134
187
|
end
|
|
135
188
|
end
|
data/test/representable_test.rb
CHANGED
|
@@ -24,12 +24,27 @@ class RepresentableTest < MiniTest::Spec
|
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
|
|
28
|
+
|
|
27
29
|
describe "#representable_property" do
|
|
28
30
|
it "creates accessors for the attribute" do
|
|
29
31
|
@band = PunkBand.new
|
|
30
32
|
assert @band.name = "Bad Religion"
|
|
31
33
|
assert_equal "Bad Religion", @band.name
|
|
32
34
|
end
|
|
35
|
+
|
|
36
|
+
describe ":from" do
|
|
37
|
+
# TODO: do this with all options.
|
|
38
|
+
it "can be set explicitly" do
|
|
39
|
+
band = Class.new(Band) { representable_property :friends, :from => :friend }
|
|
40
|
+
assert_equal "friend", band.representable_attrs.last.from
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "is infered from the name implicitly" do
|
|
44
|
+
band = Class.new(Band) { representable_property :friends }
|
|
45
|
+
assert_equal "friends", band.representable_attrs.last.from
|
|
46
|
+
end
|
|
47
|
+
end
|
|
33
48
|
end
|
|
34
49
|
|
|
35
50
|
describe "#representable_collection" do
|
data/test/xml_test.rb
CHANGED
|
@@ -125,18 +125,13 @@ class CollectionTest < MiniTest::Spec
|
|
|
125
125
|
describe ":as => [Band], :tag => :band" do
|
|
126
126
|
class Compilation
|
|
127
127
|
include Representable::XML
|
|
128
|
-
representable_collection :bands, :as => Band, :
|
|
128
|
+
representable_collection :bands, :as => Band, :from => :band
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
describe "#representable_collection" do
|
|
132
132
|
it "declares a collection" do
|
|
133
133
|
assert Compilation.representable_attrs.first.array?
|
|
134
134
|
end
|
|
135
|
-
|
|
136
|
-
it "accepts :tag" do
|
|
137
|
-
assert_equal "band", Compilation.representable_attrs.first.name
|
|
138
|
-
end
|
|
139
|
-
|
|
140
135
|
end
|
|
141
136
|
|
|
142
137
|
|
|
@@ -175,7 +170,7 @@ class CollectionTest < MiniTest::Spec
|
|
|
175
170
|
describe ":as => []" do
|
|
176
171
|
class Album
|
|
177
172
|
include Representable::XML
|
|
178
|
-
representable_collection :songs, :
|
|
173
|
+
representable_collection :songs, :from => :song
|
|
179
174
|
end
|
|
180
175
|
|
|
181
176
|
it "collects untyped items" do
|
metadata
CHANGED
|
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
|
4
4
|
prerelease: false
|
|
5
5
|
segments:
|
|
6
6
|
- 0
|
|
7
|
+
- 9
|
|
7
8
|
- 0
|
|
8
|
-
|
|
9
|
-
version: 0.0.2
|
|
9
|
+
version: 0.9.0
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- Nick Sutterer
|
|
@@ -14,11 +14,11 @@ autorequire:
|
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
16
|
|
|
17
|
-
date: 2011-
|
|
17
|
+
date: 2011-10-05 00:00:00 +02:00
|
|
18
18
|
default_executable:
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
|
-
name:
|
|
21
|
+
name: hooks
|
|
22
22
|
prerelease: false
|
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
24
|
none: false
|
|
@@ -31,7 +31,7 @@ dependencies:
|
|
|
31
31
|
type: :runtime
|
|
32
32
|
version_requirements: *id001
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
|
34
|
-
name:
|
|
34
|
+
name: nokogiri
|
|
35
35
|
prerelease: false
|
|
36
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
|
37
37
|
none: false
|
|
@@ -44,7 +44,7 @@ dependencies:
|
|
|
44
44
|
type: :runtime
|
|
45
45
|
version_requirements: *id002
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name:
|
|
47
|
+
name: json
|
|
48
48
|
prerelease: false
|
|
49
49
|
requirement: &id003 !ruby/object:Gem::Requirement
|
|
50
50
|
none: false
|
|
@@ -57,7 +57,7 @@ dependencies:
|
|
|
57
57
|
type: :runtime
|
|
58
58
|
version_requirements: *id003
|
|
59
59
|
- !ruby/object:Gem::Dependency
|
|
60
|
-
name:
|
|
60
|
+
name: rake
|
|
61
61
|
prerelease: false
|
|
62
62
|
requirement: &id004 !ruby/object:Gem::Requirement
|
|
63
63
|
none: false
|
|
@@ -67,10 +67,10 @@ dependencies:
|
|
|
67
67
|
segments:
|
|
68
68
|
- 0
|
|
69
69
|
version: "0"
|
|
70
|
-
type: :
|
|
70
|
+
type: :development
|
|
71
71
|
version_requirements: *id004
|
|
72
72
|
- !ruby/object:Gem::Dependency
|
|
73
|
-
name:
|
|
73
|
+
name: rspec
|
|
74
74
|
prerelease: false
|
|
75
75
|
requirement: &id005 !ruby/object:Gem::Requirement
|
|
76
76
|
none: false
|
|
@@ -80,10 +80,10 @@ dependencies:
|
|
|
80
80
|
segments:
|
|
81
81
|
- 0
|
|
82
82
|
version: "0"
|
|
83
|
-
type: :
|
|
83
|
+
type: :development
|
|
84
84
|
version_requirements: *id005
|
|
85
85
|
- !ruby/object:Gem::Dependency
|
|
86
|
-
name:
|
|
86
|
+
name: test_xml
|
|
87
87
|
prerelease: false
|
|
88
88
|
requirement: &id006 !ruby/object:Gem::Requirement
|
|
89
89
|
none: false
|
|
@@ -95,19 +95,6 @@ dependencies:
|
|
|
95
95
|
version: "0"
|
|
96
96
|
type: :development
|
|
97
97
|
version_requirements: *id006
|
|
98
|
-
- !ruby/object:Gem::Dependency
|
|
99
|
-
name: test_xml
|
|
100
|
-
prerelease: false
|
|
101
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
|
102
|
-
none: false
|
|
103
|
-
requirements:
|
|
104
|
-
- - ">="
|
|
105
|
-
- !ruby/object:Gem::Version
|
|
106
|
-
segments:
|
|
107
|
-
- 0
|
|
108
|
-
version: "0"
|
|
109
|
-
type: :development
|
|
110
|
-
version_requirements: *id007
|
|
111
98
|
description: Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.
|
|
112
99
|
email:
|
|
113
100
|
- apotonick@gmail.com
|
|
@@ -121,6 +108,7 @@ files:
|
|
|
121
108
|
- .gitignore
|
|
122
109
|
- .gitmodules
|
|
123
110
|
- .rspec
|
|
111
|
+
- CHANGES.textile
|
|
124
112
|
- Gemfile
|
|
125
113
|
- LICENSE
|
|
126
114
|
- README.rdoc
|