serialisable 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|