representable 0.9.3 → 0.10.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 +7 -0
- data/README.rdoc +11 -1
- data/lib/representable.rb +15 -8
- data/lib/representable/bindings/json_bindings.rb +2 -2
- data/lib/representable/bindings/xml_bindings.rb +16 -47
- data/lib/representable/definition.rb +3 -15
- data/lib/representable/json.rb +17 -23
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +18 -22
- data/test/bindings_test.rb +1 -1
- data/test/json_test.rb +86 -25
- data/test/representable_test.rb +16 -6
- data/test/xml_test.rb +107 -56
- metadata +4 -5
- data/lib/representable/nokogiri_extensions.rb +0 -19
data/CHANGES.textile
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
h2. 0.10.0
|
2
|
+
|
3
|
+
* Wrapping can now be set through @Representable.representation_wrap=@. Possible values are:
|
4
|
+
* @false@: No wrapping. In XML context, this is undefined behaviour. Default in JSON.
|
5
|
+
* @String@: Wrap with provided string.
|
6
|
+
* @true@: compute wrapper from class name.
|
7
|
+
|
1
8
|
h2. 0.9.3
|
2
9
|
|
3
10
|
* Removed the @:as => [..]@ syntax in favor of @:array => true@.
|
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Representable
|
2
2
|
|
3
|
-
<em>Maps
|
3
|
+
<em>Maps documents to Ruby objects and back.</em>
|
4
4
|
|
5
5
|
|
6
6
|
== Introduction
|
@@ -9,8 +9,18 @@ _Representable_ maps fragments in documents to attributes in Ruby objects and ba
|
|
9
9
|
|
10
10
|
This is especially helpful when implementing REST services and clients and keeps your representation knowledge in one place.
|
11
11
|
|
12
|
+
|
13
|
+
== Features
|
14
|
+
|
15
|
+
* Bidirectional - rendering and parsing
|
16
|
+
* OOP documents
|
17
|
+
* Support for JSON and XML
|
18
|
+
|
19
|
+
|
12
20
|
== Example
|
13
21
|
|
22
|
+
|
23
|
+
|
14
24
|
When writing a REST service you'd have to parse and extract data from an incoming representation document manually. Given the following XML document which represents an order.
|
15
25
|
|
16
26
|
<order>
|
data/lib/representable.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
require 'hooks/inheritable_attribute'
|
2
2
|
require 'representable/definition'
|
3
|
-
require 'representable/nokogiri_extensions'
|
4
|
-
|
5
3
|
|
6
4
|
module Representable
|
7
5
|
def self.included(base)
|
8
6
|
base.class_eval do
|
9
|
-
extend
|
7
|
+
extend ClassMethods::Declarations
|
8
|
+
extend ClassMethods::Accessors
|
10
9
|
|
11
10
|
extend Hooks::InheritableAttribute
|
12
11
|
inheritable_attr :representable_attrs
|
13
12
|
self.representable_attrs = []
|
14
13
|
|
15
|
-
inheritable_attr :
|
14
|
+
inheritable_attr :representable_wrap
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -85,12 +84,20 @@ private
|
|
85
84
|
end
|
86
85
|
|
87
86
|
module Accessors
|
88
|
-
def
|
89
|
-
self.
|
87
|
+
def representation_wrap=(name)
|
88
|
+
self.representable_wrap = name
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the wrapper for the representation. Mostly used in XML.
|
92
|
+
def representation_wrap
|
93
|
+
return unless representable_wrap
|
94
|
+
return infer_representation_name if representable_wrap === true
|
95
|
+
representable_wrap
|
90
96
|
end
|
91
97
|
|
92
|
-
|
93
|
-
|
98
|
+
private
|
99
|
+
def infer_representation_name
|
100
|
+
name.split('::').last.
|
94
101
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
95
102
|
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
96
103
|
downcase
|
@@ -44,9 +44,9 @@ module Representable
|
|
44
44
|
class ObjectBinding < Binding
|
45
45
|
def write(hash, value)
|
46
46
|
if definition.array?
|
47
|
-
hash
|
47
|
+
hash[definition.from] = value.collect {|v| v.to_hash(:wrap => false)}
|
48
48
|
else
|
49
|
-
hash.
|
49
|
+
hash[definition.from] = value.to_hash(:wrap => false)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -8,8 +8,6 @@ module Representable
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def read(xml)
|
11
|
-
xml = Nokogiri::XML::Node.from(xml) or return default
|
12
|
-
|
13
11
|
value_from_node(xml) or default
|
14
12
|
end
|
15
13
|
|
@@ -22,16 +20,6 @@ module Representable
|
|
22
20
|
definition.from
|
23
21
|
end
|
24
22
|
|
25
|
-
def wrap(xml, opts = {:always_create => false})
|
26
|
-
wrap_with = @auto_vals ? auto_wrapper : definition.wrapper
|
27
|
-
|
28
|
-
return xml if !wrap_with || xml.name == wrap_with
|
29
|
-
if !opts[:always_create] && (child = xml.children.find {|c| c.name == wrap_with })
|
30
|
-
return child
|
31
|
-
end
|
32
|
-
xml.add_node(wrap_with.to_s)
|
33
|
-
end
|
34
|
-
|
35
23
|
def collect_for(xml)
|
36
24
|
nodes = xml.search("./#{xpath}")
|
37
25
|
vals = nodes.collect { |node| yield node }
|
@@ -44,9 +32,7 @@ module Representable
|
|
44
32
|
# Represents a tag attribute.
|
45
33
|
class AttributeBinding < Binding
|
46
34
|
def write(xml, values)
|
47
|
-
|
48
|
-
xml[definition.from] = values.to_s
|
49
|
-
end
|
35
|
+
xml[definition.from] = values.to_s
|
50
36
|
end
|
51
37
|
|
52
38
|
private
|
@@ -58,21 +44,13 @@ module Representable
|
|
58
44
|
|
59
45
|
# Represents text content in a tag. # FIXME: is this tested???
|
60
46
|
class TextBinding < Binding
|
61
|
-
# Updates the text in the given _xml_ block to
|
62
|
-
# the _value_ provided.
|
63
47
|
def write(xml, value)
|
64
|
-
|
65
|
-
|
66
|
-
add(xml,
|
67
|
-
elsif definition.name?
|
68
|
-
xml.name = value
|
69
|
-
elsif definition.array?
|
70
|
-
value.each do |v|
|
71
|
-
add(xml.add_node(definition.from), v)
|
72
|
-
end
|
73
|
-
else
|
74
|
-
add(xml.add_node(definition.from), value)
|
48
|
+
if definition.array?
|
49
|
+
value.each do |v|
|
50
|
+
add(xml, definition.from, v)
|
75
51
|
end
|
52
|
+
else
|
53
|
+
add(xml, definition.from, value)
|
76
54
|
end
|
77
55
|
end
|
78
56
|
|
@@ -82,9 +60,10 @@ module Representable
|
|
82
60
|
node.content
|
83
61
|
end
|
84
62
|
end
|
85
|
-
|
86
|
-
def add(
|
87
|
-
|
63
|
+
|
64
|
+
def add(xml, name, value)
|
65
|
+
child = xml.add_child Nokogiri::XML::Node.new(name, xml.document)
|
66
|
+
child.content = value
|
88
67
|
end
|
89
68
|
end
|
90
69
|
|
@@ -93,12 +72,10 @@ module Representable
|
|
93
72
|
class ObjectBinding < Binding
|
94
73
|
# Adds the ref's markup to +xml+.
|
95
74
|
def write(xml, value)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
write_entity(xml, value)
|
101
|
-
end
|
75
|
+
if definition.array?
|
76
|
+
write_collection(xml, value)
|
77
|
+
else
|
78
|
+
write_entity(xml, value)
|
102
79
|
end
|
103
80
|
end
|
104
81
|
|
@@ -107,18 +84,10 @@ module Representable
|
|
107
84
|
[]
|
108
85
|
end
|
109
86
|
|
110
|
-
def serialize(object)
|
111
|
-
object.to_xml
|
112
|
-
end
|
113
|
-
|
114
|
-
def deserialize(node_class, xml)
|
115
|
-
node_class.from_xml(xml)
|
116
|
-
end
|
117
|
-
|
118
87
|
# Deserializes the ref's element from +xml+.
|
119
88
|
def value_from_node(xml)
|
120
89
|
collect_for(xml) do |node|
|
121
|
-
|
90
|
+
definition.sought_type.from_node(node)
|
122
91
|
end
|
123
92
|
end
|
124
93
|
|
@@ -129,7 +98,7 @@ module Representable
|
|
129
98
|
end
|
130
99
|
|
131
100
|
def write_entity(xml, entity)
|
132
|
-
xml.add_child(
|
101
|
+
xml.add_child(entity.to_node)
|
133
102
|
end
|
134
103
|
end
|
135
104
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Representable
|
2
|
-
class
|
3
|
-
|
2
|
+
# Created at class compile time. Keeps configuration options for one property.
|
3
|
+
class Definition
|
4
|
+
attr_reader :name, :sought_type, :from
|
4
5
|
alias_method :getter, :name
|
5
6
|
|
6
7
|
def initialize(sym, options={})
|
@@ -28,23 +29,10 @@ module Representable
|
|
28
29
|
sought_type.is_a?(Class)
|
29
30
|
end
|
30
31
|
|
31
|
-
def name?
|
32
|
-
@name == '*'
|
33
|
-
end
|
34
|
-
|
35
|
-
def content?
|
36
|
-
@name == '.'
|
37
|
-
end
|
38
|
-
|
39
32
|
def array?
|
40
33
|
@array
|
41
34
|
end
|
42
35
|
|
43
|
-
def cdata? # FIXME: move to XML!
|
44
|
-
@cdata
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
36
|
# Applies the block to +value+ which might also be a collection.
|
49
37
|
def apply(value)
|
50
38
|
return value unless value # DISCUSS: is that ok here?
|
data/lib/representable/json.rb
CHANGED
@@ -24,33 +24,26 @@ module Representable
|
|
24
24
|
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
25
25
|
end
|
26
26
|
|
27
|
-
# Creates a new
|
28
|
-
|
29
|
-
|
30
|
-
# book = Book.from_xml("<book><name>Beyond Java</name></book>")
|
31
|
-
# DISCUSS: assumes shitty wrapping like :article => {:name => ...}
|
32
|
-
def from_json(data, *args, &block)
|
33
|
-
create_from_json.tap do |object|
|
34
|
-
object.from_json(data, *args, &block)
|
35
|
-
end
|
27
|
+
# Creates a new object from the passed JSON document.
|
28
|
+
def from_json(*args, &block)
|
29
|
+
new.from_json(*args, &block)
|
36
30
|
end
|
37
31
|
|
38
|
-
def from_hash(
|
39
|
-
|
40
|
-
object.update_properties_from(data)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
def create_from_json(*args)
|
46
|
-
new(*args)
|
32
|
+
def from_hash(*args, &block)
|
33
|
+
new.from_hash(*args, &block)
|
47
34
|
end
|
48
35
|
end
|
49
36
|
|
50
|
-
|
37
|
+
# Parses the body as JSON and delegates to #from_hash.
|
38
|
+
def from_json(data, *args, &block)
|
51
39
|
data = ::JSON[data]
|
52
|
-
data
|
53
|
-
|
40
|
+
from_hash(data, *args, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def from_hash(data, options={}, &block)
|
44
|
+
if wrap = options[:wrap] || self.class.representation_wrap
|
45
|
+
data = data[wrap.to_s]
|
46
|
+
end
|
54
47
|
|
55
48
|
update_properties_from(data, &block)
|
56
49
|
end
|
@@ -58,8 +51,9 @@ module Representable
|
|
58
51
|
def to_hash(options={})
|
59
52
|
hash = create_representation_with({})
|
60
53
|
|
61
|
-
|
62
|
-
|
54
|
+
return hash unless wrap = options[:wrap] || self.class.representation_wrap
|
55
|
+
|
56
|
+
{wrap => hash}
|
63
57
|
end
|
64
58
|
|
65
59
|
# Returns a JSON string representing this object.
|
data/lib/representable/xml.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'representable'
|
2
|
-
require 'representable/nokogiri_extensions'
|
3
2
|
require 'representable/bindings/xml_bindings'
|
4
3
|
|
5
4
|
module Representable
|
@@ -13,20 +12,12 @@ module Representable
|
|
13
12
|
base.class_eval do
|
14
13
|
include Representable
|
15
14
|
extend ClassMethods
|
15
|
+
self.representation_wrap = true # let representable compute it.
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
|
20
|
-
class Definition < Representable::Definition
|
21
|
-
# FIXME: extract xml-specific from Definition.
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
20
|
module ClassMethods
|
26
|
-
def definition_class
|
27
|
-
Definition
|
28
|
-
end
|
29
|
-
|
30
21
|
def binding_for_definition(definition)
|
31
22
|
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
32
23
|
end
|
@@ -38,29 +29,34 @@ module Representable
|
|
38
29
|
#
|
39
30
|
# Example:
|
40
31
|
# band.from_xml("<band><name>Nofx</name></band>")
|
41
|
-
def from_xml(
|
42
|
-
|
43
|
-
object.from_xml(doc, *args, &block)
|
44
|
-
end
|
32
|
+
def from_xml(*args, &block)
|
33
|
+
new.from_xml(*args, &block)
|
45
34
|
end
|
46
35
|
|
47
|
-
|
48
|
-
|
49
|
-
new(*args)
|
36
|
+
def from_node(*args, &block)
|
37
|
+
new.from_node(*args, &block)
|
50
38
|
end
|
51
39
|
end
|
52
40
|
|
41
|
+
|
53
42
|
def from_xml(doc, *args, &block)
|
54
|
-
|
55
|
-
|
43
|
+
node = Nokogiri::XML(doc).root
|
44
|
+
from_node(node, *args, &block)
|
56
45
|
end
|
57
46
|
|
47
|
+
def from_node(node, options={}, &block)
|
48
|
+
update_properties_from(node, &block)
|
49
|
+
end
|
58
50
|
|
59
51
|
# Returns a Nokogiri::XML object representing this object.
|
60
|
-
def
|
61
|
-
root_tag =
|
52
|
+
def to_node(options={}, &block)
|
53
|
+
root_tag = options[:wrap] || self.class.representation_wrap
|
62
54
|
|
63
|
-
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new))
|
55
|
+
create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_xml(*args, &block)
|
59
|
+
to_node(*args, &block).to_s
|
64
60
|
end
|
65
61
|
end
|
66
62
|
end
|
data/test/bindings_test.rb
CHANGED
@@ -13,7 +13,7 @@ class BindingsTest < MiniTest::Spec
|
|
13
13
|
|
14
14
|
|
15
15
|
describe "TextRef#read" do
|
16
|
-
def parse_xml(xml); Nokogiri::XML
|
16
|
+
def parse_xml(xml); Nokogiri::XML(xml).root; end
|
17
17
|
|
18
18
|
before do
|
19
19
|
@ref = Representable::XML::TextBinding.new(Representable::Definition.new(:song))
|
data/test/json_test.rb
CHANGED
@@ -9,7 +9,6 @@ module JsonTest
|
|
9
9
|
describe "JSON module" do
|
10
10
|
before do
|
11
11
|
@Band = Class.new(Band) do
|
12
|
-
self.representation_name= :band
|
13
12
|
representable_property :label
|
14
13
|
end
|
15
14
|
end
|
@@ -42,35 +41,50 @@ module JsonTest
|
|
42
41
|
end
|
43
42
|
|
44
43
|
it "accepts json string" do
|
45
|
-
@band.from_json({
|
44
|
+
@band.from_json({name: "Nofx", label: "NOFX"}.to_json)
|
46
45
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
47
46
|
end
|
48
47
|
|
49
48
|
it "forwards block to #update_properties_from" do
|
50
|
-
@band.from_json({
|
49
|
+
@band.from_json({name: "Nofx", label: "NOFX"}.to_json) do |binding|
|
51
50
|
binding.definition.name == "name"
|
52
51
|
end
|
53
52
|
|
54
53
|
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
55
54
|
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
band.from_json({:
|
60
|
-
assert_equal "This Is A Standoff", band.name
|
56
|
+
|
57
|
+
it "doesn't use wrap per default" do
|
58
|
+
@band.from_json({:name => "This Is A Standoff"}.to_json)
|
59
|
+
assert_equal "This Is A Standoff", @band.name
|
60
|
+
end
|
61
|
+
|
62
|
+
it "respects :wrap option" do
|
63
|
+
@band.from_json({:band => {:name => "This Is A Standoff"}}.to_json, :wrap => :band)
|
64
|
+
assert_equal "This Is A Standoff", @band.name
|
65
|
+
end
|
66
|
+
|
67
|
+
it "respects :wrap option over representation_wrap" do
|
68
|
+
@Band.class_eval do
|
69
|
+
self.representation_wrap = :group
|
70
|
+
end
|
71
|
+
@band.from_json({:band => {:name => "This Is A Standoff"}}.to_json, :wrap => :band)
|
72
|
+
assert_equal "This Is A Standoff", @band.name
|
61
73
|
end
|
62
74
|
|
63
|
-
it "
|
64
|
-
|
65
|
-
|
66
|
-
|
75
|
+
it "respects representation_wrap" do
|
76
|
+
@Band.class_eval do
|
77
|
+
self.representation_wrap = :group
|
78
|
+
end
|
79
|
+
@band.from_json({:group => {:name => "This Is A Standoff"}}.to_json)
|
80
|
+
assert_equal "This Is A Standoff", @band.name
|
67
81
|
end
|
68
82
|
end
|
69
83
|
|
70
84
|
|
71
85
|
describe ".from_json" do
|
72
86
|
it "delegates to #from_json after object conception" do
|
73
|
-
band = @Band.from_json({
|
87
|
+
band = @Band.from_json({name: "Nofx", label: "NOFX"}.to_json) do |binding| binding.definition.name == "name" end
|
74
88
|
assert_equal ["Nofx", nil], [band.name, band.label]
|
75
89
|
end
|
76
90
|
|
@@ -95,12 +109,43 @@ module JsonTest
|
|
95
109
|
end
|
96
110
|
end
|
97
111
|
|
112
|
+
|
98
113
|
describe ".from_hash" do
|
99
114
|
it "accepts unwrapped hash with string keys" do
|
100
115
|
band = Band.from_hash("name" => "Bombshell Rocks")
|
101
116
|
assert_equal "Bombshell Rocks", band.name
|
102
117
|
end
|
103
118
|
end
|
119
|
+
|
120
|
+
|
121
|
+
describe "#to_json" do
|
122
|
+
before do
|
123
|
+
@band = @Band.new
|
124
|
+
@band.label = "Fat"
|
125
|
+
end
|
126
|
+
|
127
|
+
it "doesn't wrap per default" do
|
128
|
+
assert_equal "{\"label\":\"Fat\"}", @band.to_json
|
129
|
+
end
|
130
|
+
|
131
|
+
it "respects :wrap option" do
|
132
|
+
assert_equal "{\"band\":{\"label\":\"Fat\"}}", @band.to_json(:wrap => :band)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "respects :wrap option over representation_wrap" do
|
136
|
+
@Band.class_eval do
|
137
|
+
self.representation_wrap = :group
|
138
|
+
end
|
139
|
+
assert_equal "{\"band\":{\"label\":\"Fat\"}}", @band.to_json(:wrap => :band)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "respects representation_wrap" do
|
143
|
+
@Band.class_eval do
|
144
|
+
self.representation_wrap = :group
|
145
|
+
end
|
146
|
+
assert_equal "{\"group\":{\"label\":\"Fat\"}}", @band.to_json
|
147
|
+
end
|
148
|
+
end
|
104
149
|
end
|
105
150
|
end
|
106
151
|
|
@@ -112,7 +157,7 @@ module JsonTest
|
|
112
157
|
end
|
113
158
|
|
114
159
|
it "#from_json creates correct accessors" do
|
115
|
-
band = Band.from_json({:
|
160
|
+
band = Band.from_json({:name => "Bombshell Rocks"}.to_json)
|
116
161
|
assert_equal "Bombshell Rocks", band.name
|
117
162
|
end
|
118
163
|
|
@@ -120,7 +165,7 @@ module JsonTest
|
|
120
165
|
band = Band.new
|
121
166
|
band.name = "Cigar"
|
122
167
|
|
123
|
-
assert_equal '{"
|
168
|
+
assert_equal '{"name":"Cigar"}', band.to_json
|
124
169
|
end
|
125
170
|
end
|
126
171
|
|
@@ -136,7 +181,7 @@ module JsonTest
|
|
136
181
|
end
|
137
182
|
|
138
183
|
it "#from_json creates one Item instance" do
|
139
|
-
album = Album.from_json('{"
|
184
|
+
album = Album.from_json('{"label":{"name":"Fat Wreck"}}')
|
140
185
|
assert_equal "Fat Wreck", album.label.name
|
141
186
|
end
|
142
187
|
|
@@ -144,7 +189,23 @@ module JsonTest
|
|
144
189
|
label = Label.new; label.name = "Fat Wreck"
|
145
190
|
album = Album.new; album.label = label
|
146
191
|
|
147
|
-
assert_equal '{"
|
192
|
+
assert_equal '{"label":{"name":"Fat Wreck"}}', album.to_json
|
193
|
+
end
|
194
|
+
|
195
|
+
describe ":different_name, :as => Label" do
|
196
|
+
before do
|
197
|
+
@Album = Class.new do
|
198
|
+
include Representable::JSON
|
199
|
+
representable_property :seller, :as => Label
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it "#to_xml respects the different name" do
|
204
|
+
label = Label.new; label.name = "Fat Wreck"
|
205
|
+
album = @Album.new; album.seller = label
|
206
|
+
|
207
|
+
assert_equal "{\"seller\":{\"name\":\"Fat Wreck\"}}", album.to_json(:wrap => false)
|
208
|
+
end
|
148
209
|
end
|
149
210
|
end
|
150
211
|
|
@@ -155,13 +216,13 @@ module JsonTest
|
|
155
216
|
end
|
156
217
|
|
157
218
|
it "respects :from in #from_json" do
|
158
|
-
song = Song.from_json({:
|
219
|
+
song = Song.from_json({:songName => "Run To The Hills"}.to_json)
|
159
220
|
assert_equal "Run To The Hills", song.name
|
160
221
|
end
|
161
222
|
|
162
223
|
it "respects :from in #to_json" do
|
163
224
|
song = Song.new; song.name = "Run To The Hills"
|
164
|
-
assert_equal '{"
|
225
|
+
assert_equal '{"songName":"Run To The Hills"}', song.to_json
|
165
226
|
end
|
166
227
|
end
|
167
228
|
end
|
@@ -175,7 +236,7 @@ module JsonTest
|
|
175
236
|
end
|
176
237
|
|
177
238
|
it "#from_json creates correct accessors" do
|
178
|
-
cd = CD.from_json({:
|
239
|
+
cd = CD.from_json({:songs => ["Out in the cold", "Microphone"]}.to_json)
|
179
240
|
assert_equal ["Out in the cold", "Microphone"], cd.songs
|
180
241
|
end
|
181
242
|
|
@@ -183,7 +244,7 @@ module JsonTest
|
|
183
244
|
cd = CD.new
|
184
245
|
cd.songs = ["Out in the cold", "Microphone"]
|
185
246
|
|
186
|
-
assert_equal '{"
|
247
|
+
assert_equal '{"songs":["Out in the cold","Microphone"]}', cd.to_json
|
187
248
|
end
|
188
249
|
end
|
189
250
|
|
@@ -204,9 +265,9 @@ module JsonTest
|
|
204
265
|
|
205
266
|
describe "#from_json" do
|
206
267
|
it "pushes collection items to array" do
|
207
|
-
cd = Compilation.from_json({:
|
268
|
+
cd = Compilation.from_json({:bands => [
|
208
269
|
{:name => "Cobra Skulls"},
|
209
|
-
{:name => "Diesel Boy"}]}
|
270
|
+
{:name => "Diesel Boy"}]}.to_json)
|
210
271
|
assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
|
211
272
|
end
|
212
273
|
|
@@ -220,7 +281,7 @@ module JsonTest
|
|
220
281
|
cd = Compilation.new
|
221
282
|
cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
|
222
283
|
|
223
|
-
assert_equal '{"
|
284
|
+
assert_equal '{"bands":[{"name":"Diesel Boy"},{"name":"Bad Religion"}]}', cd.to_json
|
224
285
|
end
|
225
286
|
end
|
226
287
|
|
@@ -232,7 +293,7 @@ module JsonTest
|
|
232
293
|
end
|
233
294
|
|
234
295
|
it "respects :from in #from_json" do
|
235
|
-
songs = Songs.from_json({:
|
296
|
+
songs = Songs.from_json({:songList => ["Out in the cold", "Microphone"]}.to_json)
|
236
297
|
assert_equal ["Out in the cold", "Microphone"], songs.tracks
|
237
298
|
end
|
238
299
|
|
@@ -240,7 +301,7 @@ module JsonTest
|
|
240
301
|
songs = Songs.new
|
241
302
|
songs.tracks = ["Out in the cold", "Microphone"]
|
242
303
|
|
243
|
-
assert_equal '{"
|
304
|
+
assert_equal '{"songList":["Out in the cold","Microphone"]}', songs.to_json
|
244
305
|
end
|
245
306
|
end
|
246
307
|
end
|
data/test/representable_test.rb
CHANGED
@@ -58,7 +58,7 @@ class RepresentableTest < MiniTest::Spec
|
|
58
58
|
end
|
59
59
|
|
60
60
|
|
61
|
-
describe "#
|
61
|
+
describe "#representation_wrap" do
|
62
62
|
class SoundSystem
|
63
63
|
include Representable
|
64
64
|
end
|
@@ -70,14 +70,24 @@ class RepresentableTest < MiniTest::Spec
|
|
70
70
|
class SoftcoreBand < HardcoreBand
|
71
71
|
end
|
72
72
|
|
73
|
-
it "
|
74
|
-
assert_equal
|
73
|
+
it "returns false per default" do
|
74
|
+
assert_equal nil, SoundSystem.representation_wrap
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
it "infers a printable class name if set to true" do
|
79
|
+
HardcoreBand.representation_wrap = true
|
80
|
+
assert_equal "hardcore_band", HardcoreBand.send(:representation_wrap)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can be set explicitely" do
|
84
|
+
HardcoreBand.representation_wrap = "breach"
|
85
|
+
assert_equal "breach", HardcoreBand.representation_wrap
|
75
86
|
end
|
76
87
|
|
77
88
|
it "inherits correctly" do
|
78
|
-
HardcoreBand.
|
79
|
-
assert_equal "breach",
|
80
|
-
assert_equal "breach", SoftcoreBand.representation_name
|
89
|
+
HardcoreBand.representation_wrap = "breach"
|
90
|
+
assert_equal "breach", SoftcoreBand.representation_wrap
|
81
91
|
end
|
82
92
|
end
|
83
93
|
|
data/test/xml_test.rb
CHANGED
@@ -10,9 +10,12 @@ class Band
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
class
|
14
|
-
|
15
|
-
|
13
|
+
class Album
|
14
|
+
include Representable::XML
|
15
|
+
representable_property :band, :as => Band
|
16
|
+
|
17
|
+
def initialize(band=nil)
|
18
|
+
band and self.band = band
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
@@ -25,48 +28,65 @@ class XmlTest < MiniTest::Spec
|
|
25
28
|
before do
|
26
29
|
@Band = Class.new do
|
27
30
|
include Representable::XML
|
28
|
-
self.
|
31
|
+
self.representation_wrap = :band
|
29
32
|
representable_property :name
|
30
33
|
representable_property :label
|
31
34
|
end
|
32
35
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
|
37
|
+
|
38
|
+
describe ".from_xml" do
|
39
|
+
it "is delegated to #from_xml" do
|
40
|
+
block = lambda {|bind|}
|
41
|
+
@Band.any_instance.expects(:from_xml).with("{}", "yo") # FIXME: how to expect block?
|
42
|
+
@Band.from_xml("{}", "yo", &block)
|
43
|
+
end
|
38
44
|
end
|
39
45
|
|
40
|
-
|
41
|
-
|
46
|
+
|
47
|
+
describe ".from_node" do
|
48
|
+
it "is delegated to #from_node" do
|
49
|
+
block = lambda {|bind|}
|
50
|
+
@Band.any_instance.expects(:from_node).with("{}", "yo") # FIXME: how to expect block?
|
51
|
+
@Band.from_node("{}", "yo", &block)
|
52
|
+
end
|
42
53
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
|
55
|
+
|
56
|
+
describe "#from_xml" do
|
57
|
+
before do
|
58
|
+
@band = @Band.new
|
59
|
+
@xml = %{<band><name>Nofx</name><label>NOFX</label></band>}
|
47
60
|
end
|
48
61
|
|
49
|
-
it "
|
50
|
-
|
62
|
+
it "parses XML and assigns properties" do
|
63
|
+
@band.from_xml(@xml)
|
64
|
+
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
51
65
|
end
|
52
66
|
|
53
|
-
it "
|
54
|
-
|
67
|
+
it "forwards block to #from_node" do
|
68
|
+
@band.from_xml(@xml) do |binding|
|
69
|
+
binding.definition.name == "name"
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_equal ["Nofx", nil], [@band.name, @band.label]
|
55
73
|
end
|
56
74
|
end
|
57
75
|
|
58
|
-
|
76
|
+
|
77
|
+
describe "#from_node" do
|
59
78
|
before do
|
60
79
|
@band = @Band.new
|
80
|
+
@xml = Nokogiri::XML(%{<band><name>Nofx</name><label>NOFX</label></band>}).root
|
61
81
|
end
|
62
82
|
|
63
|
-
it "
|
64
|
-
@band.
|
83
|
+
it "receives Nokogiri node and assigns properties" do
|
84
|
+
@band.from_node(@xml)
|
65
85
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
66
86
|
end
|
67
87
|
|
68
88
|
it "forwards block to #update_properties_from" do
|
69
|
-
@band.
|
89
|
+
@band.from_node(@xml) do |binding|
|
70
90
|
binding.definition.name == "name"
|
71
91
|
end
|
72
92
|
|
@@ -74,17 +94,61 @@ class XmlTest < MiniTest::Spec
|
|
74
94
|
end
|
75
95
|
end
|
76
96
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
97
|
+
|
98
|
+
describe "#to_xml" do
|
99
|
+
it "delegates to #to_node and returns string" do
|
100
|
+
assert_xml_equal "<band><name>Rise Against</name></band>", Band.new("Rise Against").to_xml
|
101
|
+
end
|
102
|
+
|
103
|
+
it "forwards block to #to_node" do
|
104
|
+
band = @Band.new
|
105
|
+
band.name = "The Guinea Pigs"
|
106
|
+
band.label = "n/a"
|
107
|
+
xml = band.to_xml do |binding|
|
108
|
+
binding.definition.name == "name"
|
109
|
+
end
|
110
|
+
|
111
|
+
assert_xml_equal "<band><name>The Guinea Pigs</name></band>", xml
|
82
112
|
end
|
83
113
|
end
|
84
114
|
|
115
|
+
|
116
|
+
describe "#to_node" do
|
117
|
+
it "returns Nokogiri node" do
|
118
|
+
node = Band.new("Rise Against").to_node
|
119
|
+
assert_kind_of Nokogiri::XML::Element, node
|
120
|
+
end
|
121
|
+
|
122
|
+
it "wraps with infered class name per default" do
|
123
|
+
node = Band.new("Rise Against").to_node
|
124
|
+
assert_xml_equal "<band><name>Rise Against</name></band>", node.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
it "respects #representation_wrap=" do
|
128
|
+
klass = Class.new(Band)
|
129
|
+
klass.representation_wrap = :group
|
130
|
+
assert_xml_equal "<group><name>Rise Against</name></group>", klass.new("Rise Against").to_node.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
describe "#binding_for_definition" do
|
136
|
+
it "returns AttributeBinding" do
|
137
|
+
assert_kind_of XML::AttributeBinding, @Band.binding_for_definition(Def.new(:band, :from => "@band"))
|
138
|
+
end
|
139
|
+
|
140
|
+
it "returns ObjectBinding" do
|
141
|
+
assert_kind_of XML::ObjectBinding, @Band.binding_for_definition(Def.new(:band, :as => Hash))
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns TextBinding" do
|
145
|
+
assert_kind_of XML::TextBinding, @Band.binding_for_definition(Def.new(:band, :from => :content))
|
146
|
+
end
|
147
|
+
end
|
85
148
|
end
|
86
149
|
end
|
87
150
|
|
151
|
+
|
88
152
|
class AttributesTest < MiniTest::Spec
|
89
153
|
describe ":from => @rel" do
|
90
154
|
class Link
|
@@ -105,19 +169,13 @@ class AttributesTest < MiniTest::Spec
|
|
105
169
|
link = Link.new
|
106
170
|
link.href = "http://apotomo.de/"
|
107
171
|
|
108
|
-
assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml
|
172
|
+
assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml
|
109
173
|
end
|
110
174
|
end
|
111
175
|
end
|
112
176
|
|
113
177
|
class TypedPropertyTest < MiniTest::Spec
|
114
178
|
describe ":as => Item" do
|
115
|
-
class Album
|
116
|
-
include Representable::XML
|
117
|
-
representable_property :band, :as => Band
|
118
|
-
representable_property :label, :as => Label
|
119
|
-
end
|
120
|
-
|
121
179
|
it "#from_xml creates one Item instance" do
|
122
180
|
album = Album.from_xml(%{
|
123
181
|
<album>
|
@@ -129,25 +187,25 @@ class TypedPropertyTest < MiniTest::Spec
|
|
129
187
|
|
130
188
|
describe "#to_xml" do
|
131
189
|
it "doesn't escape xml from Band#to_xml" do
|
132
|
-
band = Band.new
|
133
|
-
|
134
|
-
album = Album.new
|
135
|
-
album.band = band
|
190
|
+
band = Band.new("Bad Religion")
|
191
|
+
album = Album.new(band)
|
136
192
|
|
137
193
|
assert_xml_equal %{<album>
|
138
194
|
<band>
|
139
195
|
<name>Bad Religion</name>
|
140
196
|
</band>
|
141
|
-
</album>}, album.to_xml
|
197
|
+
</album>}, album.to_xml
|
142
198
|
end
|
143
199
|
|
144
|
-
it "doesn't escape and wrap string from
|
145
|
-
|
146
|
-
|
200
|
+
it "doesn't escape and wrap string from Band#to_node" do
|
201
|
+
band = Band.new("Bad Religion")
|
202
|
+
band.instance_eval do
|
203
|
+
def to_node
|
204
|
+
"<band>Baaaad Religion</band>"
|
205
|
+
end
|
206
|
+
end
|
147
207
|
|
148
|
-
assert_xml_equal %{<album>
|
149
|
-
<label>Fat Wreck</label>
|
150
|
-
</album>}, album.to_xml.to_s
|
208
|
+
assert_xml_equal %{<album><band>Baaaad Religion</band></album>}, Album.new(band).to_xml
|
151
209
|
end
|
152
210
|
end
|
153
211
|
end
|
@@ -155,19 +213,12 @@ end
|
|
155
213
|
|
156
214
|
|
157
215
|
class CollectionTest < MiniTest::Spec
|
158
|
-
describe ":as =>
|
216
|
+
describe ":as => Band, :from => :band, :collection => true" do
|
159
217
|
class Compilation
|
160
218
|
include Representable::XML
|
161
219
|
representable_collection :bands, :as => Band, :from => :band
|
162
220
|
end
|
163
221
|
|
164
|
-
describe "#representable_collection" do
|
165
|
-
it "declares a collection" do
|
166
|
-
assert Compilation.representable_attrs.first.array?
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
|
171
222
|
describe "#from_xml" do
|
172
223
|
it "pushes collection items to array" do
|
173
224
|
cd = Compilation.from_xml(%{
|
@@ -195,12 +246,12 @@ class CollectionTest < MiniTest::Spec
|
|
195
246
|
assert_xml_equal %{<compilation>
|
196
247
|
<band><name>Diesel Boy</name></band>
|
197
248
|
<band><name>Bad Religion</name></band>
|
198
|
-
</compilation>}, cd.to_xml
|
249
|
+
</compilation>}, cd.to_xml
|
199
250
|
end
|
200
251
|
end
|
201
252
|
|
202
253
|
|
203
|
-
describe ":
|
254
|
+
describe ":from" do
|
204
255
|
class Album
|
205
256
|
include Representable::XML
|
206
257
|
representable_collection :songs, :from => :song
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 10
|
8
|
+
- 0
|
9
|
+
version: 0.10.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Nick Sutterer
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-11-
|
17
|
+
date: 2011-11-29 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -133,7 +133,6 @@ files:
|
|
133
133
|
- lib/representable/bindings/xml_bindings.rb
|
134
134
|
- lib/representable/definition.rb
|
135
135
|
- lib/representable/json.rb
|
136
|
-
- lib/representable/nokogiri_extensions.rb
|
137
136
|
- lib/representable/version.rb
|
138
137
|
- lib/representable/xml.rb
|
139
138
|
- representable.gemspec
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
|
3
|
-
Nokogiri::XML::Node.class_eval do
|
4
|
-
def add_node(name)
|
5
|
-
add_child Nokogiri::XML::Node.new(name, document)
|
6
|
-
end
|
7
|
-
|
8
|
-
# FIXME: remove switch. where is #from used with nodes?
|
9
|
-
def self.from(data)
|
10
|
-
case data
|
11
|
-
when Nokogiri::XML::Node
|
12
|
-
data
|
13
|
-
when Nokogiri::XML::Document
|
14
|
-
data.root
|
15
|
-
else
|
16
|
-
Nokogiri::XML(data).root
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|