serialisable 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/serialisable.rb +212 -106
- data/lib/serialisable/selector.rb +57 -0
- data/lib/serialisable/version.rb +1 -1
- data/spec/serialisable/selector_spec.rb +166 -0
- data/spec/serialisable_spec.rb +75 -10
- data/spec/spec_helper.rb +2 -0
- metadata +12 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 398799ae5550e1a9379df90c241567166e12eeb7
|
4
|
+
data.tar.gz: d785cbed52c32917be7d480a865f80dffcc1316d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ff5997b676acdbc6335fc0d1525eaa3196738a0c398dfb5b35adbe2fc3291b57b0a7304ed4bdf9ed0e6f5a1087072df8a7e2e274407949b64a186e07fa3c4a8
|
7
|
+
data.tar.gz: 1c1d395a6771858063ff8ab519e1ffabf808a32365bda9933108af81bd0f27b18b41442e3e68077db0654c4854e78f6f448b5c08dae21d8250df984fec94d48c
|
data/lib/serialisable.rb
CHANGED
@@ -1,34 +1,218 @@
|
|
1
1
|
require 'nokogiri'
|
2
|
-
|
2
|
+
require_relative 'serialisable/selector'
|
3
|
+
|
4
|
+
# Serialisable allows you to easily define an object that can deserialise xml
|
5
|
+
# into instances of itself. It provides a simple but powerful DSL for defining
|
6
|
+
# elements and attributes.
|
7
|
+
#
|
8
|
+
# require 'serialisable'
|
9
|
+
# require 'time'
|
10
|
+
#
|
11
|
+
# class Play
|
12
|
+
# extend Serialisable
|
13
|
+
#
|
14
|
+
# root 'play'
|
15
|
+
# element :artist, 'artist'
|
16
|
+
# element :name, 'name'
|
17
|
+
# element :played_at, 'playedat', Time
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# class User
|
21
|
+
# extend Serialisable
|
22
|
+
#
|
23
|
+
# root 'user'
|
24
|
+
# attribute :id, 'id', :to_i
|
25
|
+
# element :name, 'name'
|
26
|
+
# elements :plays, 'plays', Play
|
27
|
+
#
|
28
|
+
# def inspect
|
29
|
+
# "#<User:#{id} #{name}>"
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# user = User.deserialise <<XML
|
34
|
+
# <?xml version="1.0" encoding="utf-8"?>
|
35
|
+
# <user id="12452">
|
36
|
+
# <name>John Doe</name>
|
37
|
+
# <plays>
|
38
|
+
# <play>
|
39
|
+
# <artist>Arctic Monkeys</artist>
|
40
|
+
# <name>505</name>
|
41
|
+
# <playedat>2014-02-03T12:23:55Z</playedat>
|
42
|
+
# </play>
|
43
|
+
# <play>
|
44
|
+
# <artist>Aphex Twin</artist>
|
45
|
+
# <name>Windowlicker</name>
|
46
|
+
# <playedat>2014-02-03T12:26:13Z</playedat>
|
47
|
+
# </play>
|
48
|
+
# </plays>
|
49
|
+
# </user>
|
50
|
+
# XML
|
51
|
+
#
|
52
|
+
# p user
|
53
|
+
# #=> #<User:12452 John Doe>
|
54
|
+
# p user.plays.map(&:name)
|
55
|
+
# #=> ["505", "Windowlicker"]
|
56
|
+
#
|
57
|
+
#
|
58
|
+
# @example Type as Symbol
|
59
|
+
#
|
60
|
+
# class Count
|
61
|
+
# extend Serialisable
|
62
|
+
#
|
63
|
+
# root 'count'
|
64
|
+
# attribute :value, 'value', :to_i
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# count = Count.deserialise <<XML
|
68
|
+
# <?xml version="1.0" encoding="utf-8"?>
|
69
|
+
# <count value="500" />
|
70
|
+
# XML
|
71
|
+
#
|
72
|
+
# p count.value #=> 500
|
73
|
+
#
|
74
|
+
#
|
75
|
+
# @example Type as object responding to +#parse+
|
76
|
+
#
|
77
|
+
# class IntParser
|
78
|
+
# def parse(str); str.to_i; end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# class Count
|
82
|
+
# extend Serialisable
|
83
|
+
#
|
84
|
+
# root 'count'
|
85
|
+
# attribute :value, 'value', IntParser
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# count = Count.deserialise <<XML
|
89
|
+
# <?xml version="1.0" encoding="utf-8"?>
|
90
|
+
# <count value="500" />
|
91
|
+
# XML
|
92
|
+
#
|
93
|
+
# p count.value #=> 500
|
94
|
+
#
|
95
|
+
#
|
3
96
|
module Serialisable
|
97
|
+
def self.extended(obj)
|
98
|
+
obj.instance_variable_set(:@__selectors, [])
|
99
|
+
end
|
100
|
+
|
101
|
+
# Define the element that makes up the root for the class.
|
102
|
+
#
|
103
|
+
# @param selector [String] Name of xml element to mark as the root.
|
4
104
|
def root(selector)
|
5
105
|
@__root = selector
|
6
|
-
@__element = {}
|
7
|
-
@__elements = {}
|
8
|
-
@__attribute = {}
|
9
106
|
end
|
10
107
|
|
11
|
-
|
12
|
-
|
108
|
+
# Define an attribute selector. This will match an attribute in the defined
|
109
|
+
# root element that has the name given by +selector+.
|
110
|
+
#
|
111
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
112
|
+
# returns the value matched.
|
113
|
+
# @param selector [String] Name of xml attribute to match against.
|
114
|
+
# @param type [Symbol, #parse] If a symbol is given the matched string will
|
115
|
+
# have the method named by it called on it. If an object responding to
|
116
|
+
# #parse is given then the string value will be passed to that method.
|
117
|
+
def attribute(name, selector, type = nil)
|
118
|
+
@__selectors << Selector::Attribute.new(name, selector, type)
|
13
119
|
end
|
14
120
|
|
15
|
-
|
16
|
-
|
121
|
+
# Define an element selector. This will match a node in the defined root
|
122
|
+
# element that has the name given by +selector+.
|
123
|
+
#
|
124
|
+
# @overload element(name, serialisable)
|
125
|
+
# Use this when the node being matched contains nested xml.
|
126
|
+
#
|
127
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
128
|
+
# returns the value matched.
|
129
|
+
# @param serialisable [Serialisable]
|
130
|
+
#
|
131
|
+
# @overload element(name, root, serialisable)
|
132
|
+
# Use this when the node being matched contains nested xml and the root
|
133
|
+
# needs to be set.
|
134
|
+
#
|
135
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
136
|
+
# returns the value matched.
|
137
|
+
# @param root [String] Name of the root of the nested element, this
|
138
|
+
# overrides any root set on the +serialisable+ passed.
|
139
|
+
# @param serialisable [Serialisable] Serialisable object that represents
|
140
|
+
# the nested element.
|
141
|
+
#
|
142
|
+
# @overload element(name, selector, type=nil)
|
143
|
+
# Use this when the node only contains text.
|
144
|
+
#
|
145
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
146
|
+
# returns the value matched.
|
147
|
+
# @param selector [String] Name of xml attribute to match against.
|
148
|
+
# @param type [Symbol, #parse] If a symbol is given the matched string will
|
149
|
+
# have the method named by it called on it. If an object responding to
|
150
|
+
# #parse is given then the string value will be passed to that method.
|
151
|
+
#
|
152
|
+
def element(name, selector, type = nil)
|
153
|
+
if selector.respond_to?(:__deserialise, true)
|
154
|
+
@__selectors << Selector::Nested.new(name, selector)
|
155
|
+
|
156
|
+
elsif type.respond_to?(:__deserialise, true)
|
157
|
+
cloned_type = type.clone
|
158
|
+
cloned_type.instance_variable_set(:@__root, selector)
|
159
|
+
@__selectors << Selector::Nested.new(name, cloned_type)
|
160
|
+
|
161
|
+
else
|
162
|
+
@__selectors << Selector::Node.new(name, selector, type)
|
163
|
+
end
|
17
164
|
end
|
18
165
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
166
|
+
# Define an elements selector. This will match all nodes in the defined root
|
167
|
+
# element that has the name given by +selector+. The method created by this
|
168
|
+
# will return an array of matching values.
|
169
|
+
#
|
170
|
+
# @overload elements(name, serialisable)
|
171
|
+
# Use this when the nodes being matched contains nested xml.
|
172
|
+
#
|
173
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
174
|
+
# returns the value matched.
|
175
|
+
# @param serialisable [Serialisable]
|
176
|
+
#
|
177
|
+
# @overload elements(name, root, serialisable)
|
178
|
+
# Use this when the nodes being matched contains nested xml and the root
|
179
|
+
# needs to be set.
|
180
|
+
#
|
181
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
182
|
+
# returns the value matched.
|
183
|
+
# @param root [String] Name of the root of the nested element, this
|
184
|
+
# overrides any root set on the +serialisable+ passed.
|
185
|
+
# @param serialisable [Serialisable] Serialisable object that represents
|
186
|
+
# the nested element.
|
187
|
+
#
|
188
|
+
# @overload elements(name, selector, type=nil)
|
189
|
+
# Use this when the nodes only contain text.
|
190
|
+
#
|
191
|
+
# @param name [Symbol] Name of the method to be defined on the class that
|
192
|
+
# returns the value matched.
|
193
|
+
# @param selector [String] Name of xml attribute to match against.
|
194
|
+
# @param type [Symbol, #parse] If a symbol is given the matched string will
|
195
|
+
# have the method named by it called on it. If an object responding to
|
196
|
+
# #parse is given then the string value will be passed to that method.
|
197
|
+
#
|
198
|
+
def elements(name, selector, type = nil)
|
199
|
+
if selector.respond_to?(:__deserialise_all, true)
|
200
|
+
@__selectors << Selector::NestedMultiple.new(name, selector, type)
|
201
|
+
|
202
|
+
elsif type.respond_to?(:__deserialise_all, true)
|
203
|
+
cloned_type = type.clone
|
204
|
+
cloned_type.instance_variable_set(:@__root, selector)
|
205
|
+
@__selectors << Selector::NestedMultiple.new(name, cloned_type)
|
206
|
+
|
27
207
|
else
|
28
|
-
|
208
|
+
@__selectors << Selector::Nodes.new(name, selector, type)
|
29
209
|
end
|
30
210
|
end
|
31
211
|
|
212
|
+
# Deserialises the given +xml+ into an instance of the class.
|
213
|
+
#
|
214
|
+
# @param xml [String]
|
215
|
+
# @return An instance of the class that the method was called on.
|
32
216
|
def deserialise(xml)
|
33
217
|
__deserialise Nokogiri::XML(xml)
|
34
218
|
end
|
@@ -37,99 +221,21 @@ module Serialisable
|
|
37
221
|
|
38
222
|
def __deserialise_all(doc)
|
39
223
|
doc = doc.children.find_all {|node| node.name == @__root }
|
40
|
-
|
41
|
-
attrs_list = doc.map do |node|
|
42
|
-
attrs = SerialisableHelpers.get_multiples(node, @__elements)
|
43
|
-
attrs += SerialisableHelpers.get_singles(node, @__element)
|
44
|
-
attrs += SerialisableHelpers.get_attributes(node, @__attribute)
|
45
|
-
|
46
|
-
attrs
|
47
|
-
end
|
48
|
-
|
49
|
-
objs = []
|
50
|
-
attrs_list.each do |attrs|
|
51
|
-
attrs = Hash[attrs]
|
52
|
-
|
53
|
-
attrs.each do |key, value|
|
54
|
-
define_method key do
|
55
|
-
instance_variable_get(:@__serialisable_attrs)[key]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
obj = new
|
60
|
-
obj.instance_variable_set(:@__serialisable_attrs, attrs)
|
61
|
-
objs << obj
|
62
|
-
end
|
63
|
-
|
64
|
-
objs
|
224
|
+
doc.map {|node| __select(node) }
|
65
225
|
end
|
66
226
|
|
67
227
|
def __deserialise(doc)
|
68
|
-
|
69
|
-
|
70
|
-
attrs = SerialisableHelpers.get_multiples(doc, @__elements)
|
71
|
-
attrs += SerialisableHelpers.get_singles(doc, @__element)
|
72
|
-
attrs += SerialisableHelpers.get_attributes(doc, @__attribute)
|
73
|
-
|
74
|
-
attrs = Hash[attrs]
|
75
|
-
|
76
|
-
attrs.each do |key, value|
|
77
|
-
define_method key do
|
78
|
-
instance_variable_get(:@__serialisable_attrs)[key]
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
obj = new
|
83
|
-
obj.instance_variable_set(:@__serialisable_attrs, attrs)
|
84
|
-
obj
|
228
|
+
node = doc.children.find {|node| node.name == @__root }
|
229
|
+
__select(node)
|
85
230
|
end
|
86
|
-
end
|
87
|
-
|
88
|
-
module SerialisableHelpers
|
89
|
-
extend self
|
90
231
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
[name, values]
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def get_singles(root, hash)
|
106
|
-
hash.map do |name, (selector, type)|
|
107
|
-
if selector.respond_to?(:__deserialise, true)
|
108
|
-
[name, selector.send(:__deserialise, root)]
|
109
|
-
else
|
110
|
-
value = root.children.find {|node| node.name == selector }.children.to_s
|
111
|
-
value = parse_type(value, type)
|
112
|
-
|
113
|
-
[name, value]
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def get_attributes(root, hash)
|
119
|
-
hash.map do |name, (selector, type)|
|
120
|
-
value = root.attributes[selector].value
|
121
|
-
value = parse_type(value, type)
|
122
|
-
|
123
|
-
[name, value]
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Parses the value read from xml if the type given responds to the method
|
128
|
-
# #parse, otherwise returns the string value.
|
129
|
-
#
|
130
|
-
# @param value [String]
|
131
|
-
# @param type [#parse]
|
132
|
-
def parse_type(value, type)
|
133
|
-
type.respond_to?(:parse) ? type.parse(value) : value
|
232
|
+
def __select(node)
|
233
|
+
new.tap {|obj|
|
234
|
+
@__selectors.each {|selector|
|
235
|
+
obj.singleton_class.send(:define_method, selector.name) {
|
236
|
+
selector.match(node)
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
134
240
|
end
|
135
241
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Serialisable
|
2
|
+
|
3
|
+
# @abstract Must implement #match
|
4
|
+
class Selector
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name, selector, type = nil)
|
8
|
+
@name = name
|
9
|
+
@selector = selector
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(value)
|
14
|
+
if @type.respond_to?(:parse)
|
15
|
+
@type.parse(value)
|
16
|
+
elsif @type.is_a?(Symbol)
|
17
|
+
value.send(@type)
|
18
|
+
else
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Attribute < Selector
|
24
|
+
def match(root)
|
25
|
+
parse root.attributes[@selector].value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Node < Selector
|
30
|
+
def match(root)
|
31
|
+
parse root.children
|
32
|
+
.find {|node| node.name == @selector }
|
33
|
+
.children.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Nodes < Selector
|
38
|
+
def match(root)
|
39
|
+
root.children
|
40
|
+
.find_all {|node| node.name == @selector }
|
41
|
+
.map {|node| parse node.children.to_s }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Nested < Selector
|
46
|
+
def match(root)
|
47
|
+
@selector.send(:__deserialise, root)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class NestedMultiple < Selector
|
52
|
+
def match(root)
|
53
|
+
@selector.send(:__deserialise_all, root)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/serialisable/version.rb
CHANGED
@@ -0,0 +1,166 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Serialisable::Selector do
|
4
|
+
describe '#name' do
|
5
|
+
let(:selector_name) { "some name" }
|
6
|
+
subject { Serialisable::Selector.new(selector_name, nil) }
|
7
|
+
|
8
|
+
it 'returns the name' do
|
9
|
+
subject.name.must_equal selector_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Serialisable::Selector::Attribute do
|
14
|
+
describe '#match' do
|
15
|
+
describe 'when type not given' do
|
16
|
+
subject { Serialisable::Selector::Attribute.new(nil, 'hey') }
|
17
|
+
|
18
|
+
let(:xml) { '<el hey="1" what="5"/>' }
|
19
|
+
let(:doc) { Nokogiri::XML(xml) }
|
20
|
+
|
21
|
+
it 'returns the matching attribute' do
|
22
|
+
subject.match(doc.children.first).must_equal "1"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when symbol given as type' do
|
27
|
+
subject { Serialisable::Selector::Attribute.new(nil, 'hey', :to_i) }
|
28
|
+
|
29
|
+
let(:xml) { '<el hey="1" what="5"/>' }
|
30
|
+
let(:doc) { Nokogiri::XML(xml) }
|
31
|
+
|
32
|
+
it 'returns the matching attribute' do
|
33
|
+
subject.match(doc.children.first).must_equal 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'when object responding to #parse given as type' do
|
38
|
+
let(:type) { Class.new { def parse(v); v.to_i; end }.new }
|
39
|
+
subject { Serialisable::Selector::Attribute.new(nil, 'hey', type) }
|
40
|
+
|
41
|
+
let(:xml) { '<el hey="1" what="5"/>' }
|
42
|
+
let(:doc) { Nokogiri::XML(xml) }
|
43
|
+
|
44
|
+
it 'returns the matching attribute' do
|
45
|
+
subject.match(doc.children.first).must_equal 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Serialisable::Selector::Node do
|
52
|
+
describe '#match' do
|
53
|
+
describe 'when type not given' do
|
54
|
+
subject { Serialisable::Selector::Node.new(nil, 'hey') }
|
55
|
+
|
56
|
+
let(:xml) { '<el><hey>5</hey><what>6</what></el>' }
|
57
|
+
let(:doc) { Nokogiri::XML(xml) }
|
58
|
+
|
59
|
+
it 'returns the matching node' do
|
60
|
+
subject.match(doc.children.first).must_equal "5"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when symbol given as type' do
|
65
|
+
subject { Serialisable::Selector::Node.new(nil, 'hey', :to_i) }
|
66
|
+
|
67
|
+
let(:xml) { '<el><hey>5</hey><what>6</what></el>' }
|
68
|
+
let(:doc) { Nokogiri::XML(xml) }
|
69
|
+
|
70
|
+
it 'returns the matching node' do
|
71
|
+
subject.match(doc.children.first).must_equal 5
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'when object responding to #parse given as type' do
|
76
|
+
let(:type) { Class.new { def parse(v); v.to_i; end }.new }
|
77
|
+
subject { Serialisable::Selector::Node.new(nil, 'hey', type) }
|
78
|
+
|
79
|
+
let(:xml) { '<el><hey>5</hey><what>6</what></el>' }
|
80
|
+
let(:doc) { Nokogiri::XML(xml) }
|
81
|
+
|
82
|
+
it 'returns the matching node' do
|
83
|
+
subject.match(doc.children.first).must_equal 5
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe Serialisable::Selector::Nodes do
|
90
|
+
describe '#match' do
|
91
|
+
describe 'when no type given' do
|
92
|
+
subject { Serialisable::Selector::Nodes.new(nil, 'hey') }
|
93
|
+
|
94
|
+
let(:xml) { '<el><hey>5</hey><hey>6</hey><what>7</what></el>' }
|
95
|
+
let(:doc) { Nokogiri::XML(xml) }
|
96
|
+
|
97
|
+
it 'returns the matching nodes' do
|
98
|
+
subject.match(doc.children.first).must_equal ['5', '6']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'when symbol given as type' do
|
103
|
+
subject { Serialisable::Selector::Nodes.new(nil, 'hey', :to_i) }
|
104
|
+
|
105
|
+
let(:xml) { '<el><hey>5</hey><hey>6</hey><what>7</what></el>' }
|
106
|
+
let(:doc) { Nokogiri::XML(xml) }
|
107
|
+
|
108
|
+
it 'returns the matching nodes' do
|
109
|
+
subject.match(doc.children.first).must_equal [5, 6]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'when object responding to #parse given as type' do
|
114
|
+
let(:type) { Class.new { def parse(v); v.to_i; end }.new }
|
115
|
+
subject { Serialisable::Selector::Nodes.new(nil, 'hey', type) }
|
116
|
+
|
117
|
+
let(:xml) { '<el><hey>5</hey><hey>6</hey><what>7</what></el>' }
|
118
|
+
let(:doc) { Nokogiri::XML(xml) }
|
119
|
+
|
120
|
+
it 'returns the matching nodes' do
|
121
|
+
subject.match(doc.children.first).must_equal [5, 6]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe Serialisable::Selector::Nested do
|
128
|
+
describe '#match' do
|
129
|
+
let(:root) { mock }
|
130
|
+
let(:nested) { mock }
|
131
|
+
let(:parsed) { mock }
|
132
|
+
subject { Serialisable::Selector::Nested.new(nil, nested, nil) }
|
133
|
+
|
134
|
+
before {
|
135
|
+
nested
|
136
|
+
.expects(:send)
|
137
|
+
.with(:__deserialise, root)
|
138
|
+
.returns(parsed)
|
139
|
+
}
|
140
|
+
|
141
|
+
it 'calls the private #__deserialise method' do
|
142
|
+
subject.match(root).must_equal parsed
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe Serialisable::Selector::NestedMultiple do
|
148
|
+
describe '#match' do
|
149
|
+
let(:root) { mock }
|
150
|
+
let(:nested) { mock }
|
151
|
+
let(:parsed) { mock }
|
152
|
+
subject { Serialisable::Selector::NestedMultiple.new(nil, nested, nil) }
|
153
|
+
|
154
|
+
before {
|
155
|
+
nested
|
156
|
+
.expects(:send)
|
157
|
+
.with(:__deserialise_all, root)
|
158
|
+
.returns(parsed)
|
159
|
+
}
|
160
|
+
|
161
|
+
it 'calls the private #__deserialise_all method' do
|
162
|
+
subject.match(root).must_equal parsed
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/spec/serialisable_spec.rb
CHANGED
@@ -79,6 +79,44 @@ EOS
|
|
79
79
|
result.song.name.must_equal '505'
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
describe 'with a nested serialisable given a root' do
|
84
|
+
subject {
|
85
|
+
track = Class.new {
|
86
|
+
extend Serialisable
|
87
|
+
|
88
|
+
root 'track'
|
89
|
+
element :artist, 'artist'
|
90
|
+
element :name, 'name'
|
91
|
+
}
|
92
|
+
|
93
|
+
Class.new {
|
94
|
+
extend Serialisable
|
95
|
+
|
96
|
+
root 'songs'
|
97
|
+
element :song, 'song', track
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
let(:xml) {
|
102
|
+
<<EOS
|
103
|
+
<?xml version="1.0" encoding="utf-8"?>
|
104
|
+
<songs>
|
105
|
+
<song>
|
106
|
+
<artist>Arctic Monkeys</artist>
|
107
|
+
<name>505</name>
|
108
|
+
</song>
|
109
|
+
</songs>
|
110
|
+
EOS
|
111
|
+
}
|
112
|
+
|
113
|
+
it 'deserialises the nested object correctly' do
|
114
|
+
result = subject.deserialise(xml)
|
115
|
+
|
116
|
+
result.song.artist.must_equal 'Arctic Monkeys'
|
117
|
+
result.song.name.must_equal '505'
|
118
|
+
end
|
119
|
+
end
|
82
120
|
end
|
83
121
|
|
84
122
|
describe '#elements' do
|
@@ -149,24 +187,51 @@ EOS
|
|
149
187
|
end
|
150
188
|
end
|
151
189
|
|
152
|
-
|
153
|
-
|
154
|
-
Class.new {
|
190
|
+
describe 'with a list of nested objects given a root' do
|
191
|
+
subject {
|
192
|
+
track = Class.new {
|
155
193
|
extend Serialisable
|
156
194
|
|
157
|
-
|
195
|
+
root 'track'
|
196
|
+
element :artist, 'artist'
|
197
|
+
element :name, 'name'
|
158
198
|
}
|
159
|
-
}.must_raise ArgumentError
|
160
|
-
end
|
161
199
|
|
162
|
-
it 'raises an exception if given more than three arguments' do
|
163
|
-
lambda {
|
164
200
|
Class.new {
|
165
201
|
extend Serialisable
|
166
202
|
|
167
|
-
|
203
|
+
root 'songs'
|
204
|
+
elements :songs, 'song', track
|
168
205
|
}
|
169
|
-
}
|
206
|
+
}
|
207
|
+
|
208
|
+
let(:xml) {
|
209
|
+
<<EOS
|
210
|
+
<?xml version="1.0" encoding="utf-8"?>
|
211
|
+
<songs>
|
212
|
+
<song>
|
213
|
+
<artist>Arctic Monkeys</artist>
|
214
|
+
<name>505</name>
|
215
|
+
</song>
|
216
|
+
<song>
|
217
|
+
<artist>Aphex Twin</artist>
|
218
|
+
<name>Windowlicker</name>
|
219
|
+
</song>
|
220
|
+
</songs>
|
221
|
+
EOS
|
222
|
+
}
|
223
|
+
|
224
|
+
it 'takes xml and returns an object with a list of nested objects' do
|
225
|
+
result = subject.deserialise(xml)
|
226
|
+
|
227
|
+
result.songs.length.must_equal 2
|
228
|
+
|
229
|
+
result.songs[0].artist.must_equal 'Arctic Monkeys'
|
230
|
+
result.songs[0].name.must_equal '505'
|
231
|
+
|
232
|
+
result.songs[1].artist.must_equal 'Aphex Twin'
|
233
|
+
result.songs[1].name.must_equal 'Windowlicker'
|
234
|
+
end
|
170
235
|
end
|
171
236
|
end
|
172
237
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serialisable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Hawxwell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -16,42 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.6
|
19
|
+
version: '1.6'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.6
|
26
|
+
version: '1.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.2
|
33
|
+
version: '5.2'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.2
|
40
|
+
version: '5.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mocha
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.0
|
47
|
+
version: '1.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0
|
54
|
+
version: '1.0'
|
55
55
|
description: |2
|
56
56
|
Serialisable provides a simple DSL for turning xml into ruby objects.
|
57
57
|
email: m@hawx.me
|
@@ -61,9 +61,11 @@ extra_rdoc_files: []
|
|
61
61
|
files:
|
62
62
|
- README.md
|
63
63
|
- Rakefile
|
64
|
+
- lib/serialisable/selector.rb
|
64
65
|
- lib/serialisable/version.rb
|
65
66
|
- lib/serialisable.rb
|
66
67
|
- spec/spec_helper.rb
|
68
|
+
- spec/serialisable/selector_spec.rb
|
67
69
|
- spec/serialisable_spec.rb
|
68
70
|
homepage: http://github.com/hawx/serialisable
|
69
71
|
licenses:
|
@@ -91,4 +93,6 @@ specification_version: 4
|
|
91
93
|
summary: Serialisable allows easy xml deserialisation
|
92
94
|
test_files:
|
93
95
|
- spec/spec_helper.rb
|
96
|
+
- spec/serialisable/selector_spec.rb
|
94
97
|
- spec/serialisable_spec.rb
|
98
|
+
has_rdoc:
|