serialisable 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6fd4b076b8bdcf3bb038edb81861f995b5c64e03
4
+ data.tar.gz: eff45ba4141fb2eeff36635e4ffa21de831b1dd8
5
+ SHA512:
6
+ metadata.gz: d3c9d0c2e3e2d7f8973da83a85143e3a8a2e2ffa16770c4e19c074fbd2672e1543b7e1ff95dd3d02ef0b61c13fc00a4e0f034cd587db148705ea146ad946a163
7
+ data.tar.gz: 86677bd6af34fe7e3f5859a6266e55e67cd0d5dfd87b12b2dfde0b8dccfad10ea83896bcb69a1b8b66c62b310d3380fa13d72b57286dbb78cbc43a978fa2c1f2
@@ -0,0 +1,45 @@
1
+ # Serialisable
2
+
3
+ Simple xml to object deserialisation for Ruby, built on top of Nokogiri (at the
4
+ moment).
5
+
6
+ This is still in the hacky stage, so you probably shouldn't use it...
7
+
8
+ ``` xml
9
+ <?xml version="1.0" encoding="utf-8"?>
10
+ <plays>
11
+ <play>
12
+ <track>505</track>
13
+ <artist>Arctic Monkeys</artist>
14
+ <time>2013-10-12T15:34:50Z</time>
15
+ </play>
16
+ <play>
17
+ <track>Windowlicker</track>
18
+ <artist>Aphex Twin</artist>
19
+ <time>2013-10-12T15:37:43Z</time>
20
+ </play>
21
+ </plays>
22
+ ```
23
+
24
+ ``` ruby
25
+ require 'serialisable'
26
+ require 'time'
27
+
28
+ class Play
29
+ extend Serialisable
30
+
31
+ root 'play'
32
+ element :track, 'track'
33
+ element :artist, 'artist'
34
+ element :time, 'time', Time # an object responding to #parse
35
+ end
36
+
37
+ class Plays
38
+ extend Serialisable
39
+
40
+ root 'plays'
41
+ elements :plays, Play # a Serialisable object
42
+ end
43
+
44
+ plays = Plays.deserialise(File.read('plays.xml')).plays
45
+ ```
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.libs << 'lib' << 'spec'
5
+ t.pattern = 'spec/**/*_spec.rb'
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,136 @@
1
+ require 'nokogiri'
2
+ require_relative 'serialisable/selector'
3
+
4
+ module Serialisable
5
+ def root(selector)
6
+ @__root = selector
7
+ @__element = {}
8
+ @__elements = {}
9
+ @__attribute = {}
10
+ end
11
+
12
+ def attribute(name, selector, type=String)
13
+ @__attribute[name] = [selector, type]
14
+ end
15
+
16
+ def element(name, selector, type=String)
17
+ @__element[name] = [selector, type]
18
+ end
19
+
20
+ def elements(name, *args)
21
+ case args.size
22
+ when 1
23
+ klass = args.first
24
+ @__elements[name] = [nil, klass]
25
+ when 2
26
+ selector, type = args
27
+ @__elements[name] = [selector, type]
28
+ else
29
+ raise ArgumentError
30
+ end
31
+ end
32
+
33
+ def deserialise(xml)
34
+ __deserialise Nokogiri::XML(xml)
35
+ end
36
+
37
+ private
38
+
39
+ def __deserialise_all(doc)
40
+ doc = doc.children.find_all {|node| node.name == @__root }
41
+
42
+ attrs_list = doc.map do |node|
43
+ attrs = SerialisableHelpers.get_multiples(node, @__elements)
44
+ attrs += SerialisableHelpers.get_singles(node, @__element)
45
+ attrs += SerialisableHelpers.get_attributes(node, @__attribute)
46
+
47
+ attrs
48
+ end
49
+
50
+ objs = []
51
+ attrs_list.each do |attrs|
52
+ attrs = Hash[attrs]
53
+
54
+ attrs.each do |key, value|
55
+ define_method key do
56
+ instance_variable_get(:@__serialisable_attrs)[key]
57
+ end
58
+ end
59
+
60
+ obj = new
61
+ obj.instance_variable_set(:@__serialisable_attrs, attrs)
62
+ objs << obj
63
+ end
64
+
65
+ objs
66
+ end
67
+
68
+ def __deserialise(doc)
69
+ doc = doc.children.find {|node| node.name == @__root }
70
+
71
+ attrs = SerialisableHelpers.get_multiples(doc, @__elements)
72
+ attrs += SerialisableHelpers.get_singles(doc, @__element)
73
+ attrs += SerialisableHelpers.get_attributes(doc, @__attribute)
74
+
75
+ attrs = Hash[attrs]
76
+
77
+ attrs.each do |key, value|
78
+ define_method key do
79
+ instance_variable_get(:@__serialisable_attrs)[key]
80
+ end
81
+ end
82
+
83
+ obj = new
84
+ obj.instance_variable_set(:@__serialisable_attrs, attrs)
85
+ obj
86
+ end
87
+ end
88
+
89
+ module SerialisableHelpers
90
+ extend self
91
+
92
+ def get_multiples(root, hash)
93
+ hash.map do |name, (selector, type)|
94
+ if type.respond_to?(:__deserialise_all, true)
95
+ [name, type.send(:__deserialise_all, root)]
96
+ else
97
+ values = root.children.find_all {|node| node.name == selector }
98
+ .map {|node| node.children.to_s }
99
+ .map {|value| parse_type(value, type) }
100
+
101
+ [name, values]
102
+ end
103
+ end
104
+ end
105
+
106
+ def get_singles(root, hash)
107
+ hash.map do |name, (selector, type)|
108
+ if selector.respond_to?(:__deserialise, true)
109
+ [name, selector.send(:__deserialise, root)]
110
+ else
111
+ value = root.children.find {|node| node.name == selector }.children.to_s
112
+ value = parse_type(value, type)
113
+
114
+ [name, value]
115
+ end
116
+ end
117
+ end
118
+
119
+ def get_attributes(root, hash)
120
+ hash.map do |name, (selector, type)|
121
+ value = root.attributes[selector].value
122
+ value = parse_type(value, type)
123
+
124
+ [name, value]
125
+ end
126
+ end
127
+
128
+ # Parses the value read from xml if the type given responds to the method
129
+ # #parse, otherwise returns the string value.
130
+ #
131
+ # @param value [String]
132
+ # @param type [#parse]
133
+ def parse_type(value, type)
134
+ type.respond_to?(:parse) ? type.parse(value) : value
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module Serialisable
2
+ VERSION = '0.0.0'
3
+ end
@@ -0,0 +1,312 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Serialisable do
4
+
5
+ describe '#element' do
6
+ describe 'with a single element' do
7
+
8
+ subject {
9
+ Class.new {
10
+ extend Serialisable
11
+
12
+ root 'root'
13
+ element :node, 'node'
14
+ }
15
+ }
16
+
17
+ let(:xml) {
18
+ '<?xml version="1.0" encoding="utf-8"?><root><node>value</node></root>'
19
+ }
20
+
21
+ it 'takes xml and returns an object' do
22
+ subject.deserialise(xml).node.must_equal 'value'
23
+ end
24
+ end
25
+
26
+ describe 'with an single element with a type to parse' do
27
+ subject {
28
+ require 'time'
29
+
30
+ Class.new {
31
+ extend Serialisable
32
+
33
+ root 'root'
34
+ element :time, 'time', Time
35
+ }
36
+ }
37
+
38
+ let(:xml) {
39
+ '<?xml version="1.0" encoding="utf-8"?><root><time>2013-07-04T13:23:34Z</time></root>'
40
+ }
41
+
42
+ it 'takes xml and returns an object with the type parsed' do
43
+ subject.deserialise(xml).time.must_equal Time.utc(2013, 7, 4, 13, 23, 34)
44
+ end
45
+ end
46
+
47
+ describe 'with a nested serialisable' do
48
+ subject {
49
+ Class.new {
50
+ extend Serialisable
51
+
52
+ root 'songs'
53
+ element :song, Class.new {
54
+ extend Serialisable
55
+
56
+ root 'song'
57
+ element :artist, 'artist'
58
+ element :name, 'name'
59
+ }
60
+ }
61
+ }
62
+
63
+ let(:xml) {
64
+ <<EOS
65
+ <?xml version="1.0" encoding="utf-8"?>
66
+ <songs>
67
+ <song>
68
+ <artist>Arctic Monkeys</artist>
69
+ <name>505</name>
70
+ </song>
71
+ </songs>
72
+ EOS
73
+ }
74
+
75
+ it 'deserialises the nested object correctly' do
76
+ result = subject.deserialise(xml)
77
+
78
+ result.song.artist.must_equal 'Arctic Monkeys'
79
+ result.song.name.must_equal '505'
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#elements' do
85
+ describe 'with a single list of elements' do
86
+ subject {
87
+ require 'time'
88
+
89
+ Class.new {
90
+ extend Serialisable
91
+
92
+ root 'root'
93
+ elements :times, 'time', Time
94
+ }
95
+ }
96
+
97
+ let (:xml) {
98
+ '<?xml version="1.0" encoding="utf-8"?><root><time>2013-05-06T00:00:00Z</time><time>2013-06-07T00:00:00Z</time></root>'
99
+ }
100
+
101
+ it 'takes xml and returns an object with a list of results' do
102
+ times = subject.deserialise(xml).times
103
+ times.must_equal [Time.utc(2013, 5, 6), Time.utc(2013, 6, 7)]
104
+ end
105
+ end
106
+
107
+ describe 'with a list of nested objects' do
108
+ subject {
109
+ Class.new {
110
+ extend Serialisable
111
+
112
+ root 'songs'
113
+ elements :songs, Class.new {
114
+ extend Serialisable
115
+
116
+ root 'song'
117
+ element :artist, 'artist'
118
+ element :name, 'name'
119
+ }
120
+ }
121
+ }
122
+
123
+ let(:xml) {
124
+ <<EOS
125
+ <?xml version="1.0" encoding="utf-8"?>
126
+ <songs>
127
+ <song>
128
+ <artist>Arctic Monkeys</artist>
129
+ <name>505</name>
130
+ </song>
131
+ <song>
132
+ <artist>Aphex Twin</artist>
133
+ <name>Windowlicker</name>
134
+ </song>
135
+ </songs>
136
+ EOS
137
+ }
138
+
139
+ it 'takes xml and returns an object with a list of nested objects' do
140
+ result = subject.deserialise(xml)
141
+
142
+ result.songs.length.must_equal 2
143
+
144
+ result.songs[0].artist.must_equal 'Arctic Monkeys'
145
+ result.songs[0].name.must_equal '505'
146
+
147
+ result.songs[1].artist.must_equal 'Aphex Twin'
148
+ result.songs[1].name.must_equal 'Windowlicker'
149
+ end
150
+ end
151
+
152
+ it 'raises an exception if not given less than two arguments' do
153
+ lambda {
154
+ Class.new {
155
+ extend Serialisable
156
+
157
+ elements :name
158
+ }
159
+ }.must_raise ArgumentError
160
+ end
161
+
162
+ it 'raises an exception if given more than three arguments' do
163
+ lambda {
164
+ Class.new {
165
+ extend Serialisable
166
+
167
+ elements :name, 'one', 'two', 'three'
168
+ }
169
+ }.must_raise ArgumentError
170
+ end
171
+ end
172
+
173
+ describe '#attribute' do
174
+ describe 'with a single attribute' do
175
+ subject {
176
+ Class.new {
177
+ extend Serialisable
178
+
179
+ root 'item'
180
+ attribute :id, 'id'
181
+ }
182
+ }
183
+
184
+ let(:xml) {
185
+ '<?xml version="1.0" encoding="utf-8"?><item id="1234" />'
186
+ }
187
+
188
+ it 'takes xml and returns an object with the attribute deserialised' do
189
+ subject.deserialise(xml).id.must_equal '1234'
190
+ end
191
+ end
192
+
193
+ describe 'with an attribute with a type' do
194
+ subject {
195
+ require 'time'
196
+ Class.new {
197
+ extend Serialisable
198
+
199
+ root 'item'
200
+ attribute :at, 'at', Time
201
+ }
202
+ }
203
+
204
+ let(:xml) {
205
+ '<?xml version="1.0" encoding="utf-8"?><item at="2013-10-10T15:30:34Z" />'
206
+ }
207
+
208
+ it 'takes xml and returns an object with the attribute parsed' do
209
+ subject.deserialise(xml).at.must_equal Time.utc(2013, 10, 10, 15, 30, 34)
210
+ end
211
+ end
212
+
213
+ describe 'with an attribute on a nested object' do
214
+ subject {
215
+ Class.new {
216
+ extend Serialisable
217
+
218
+ root 'items'
219
+ element :item, Class.new {
220
+ extend Serialisable
221
+
222
+ root 'item'
223
+ attribute :id, 'id'
224
+ }
225
+ }
226
+ }
227
+
228
+ let(:xml) {
229
+ '<?xml version="1.0" encoding="utf-8"?><items><item id="1234" /></items>'
230
+ }
231
+
232
+ it 'takes xml and returns an object with the attribute deserialised' do
233
+ subject.deserialise(xml).item.id.must_equal '1234'
234
+ end
235
+ end
236
+
237
+ describe 'with an attribute on a list of nested object' do
238
+ subject {
239
+ Class.new {
240
+ extend Serialisable
241
+
242
+ root 'items'
243
+ elements :items, Class.new {
244
+ extend Serialisable
245
+
246
+ root 'item'
247
+ attribute :id, 'id'
248
+ }
249
+ }
250
+ }
251
+
252
+ let(:xml) {
253
+ '<?xml version="1.0" encoding="utf-8"?><items><item id="1234" /><item id="5678" /></items>'
254
+ }
255
+
256
+ it 'takes xml and returns an object with the attribute deserialised' do
257
+ items = subject.deserialise(xml).items
258
+
259
+ items[0].id.must_equal '1234'
260
+ items[1].id.must_equal '5678'
261
+ end
262
+ end
263
+ end
264
+
265
+ describe 'all together' do
266
+ let(:xml) {
267
+ <<EOS
268
+ <?xml version="1.0" encoding="utf-8"?>
269
+ <plays>
270
+ <play>
271
+ <track>505</track>
272
+ <artist>Arctic Monkeys</artist>
273
+ <time>2013-10-12T15:34:50Z</time>
274
+ </play>
275
+ <play>
276
+ <track>Windowlicker</track>
277
+ <artist>Aphex Twin</artist>
278
+ <time>2013-10-12T15:37:43Z</time>
279
+ </play>
280
+ </plays>
281
+ EOS
282
+ }
283
+
284
+ subject {
285
+ Class.new {
286
+ extend Serialisable
287
+
288
+ root 'plays'
289
+ elements :plays, Class.new {
290
+ extend Serialisable
291
+
292
+ root 'play'
293
+ element :track, 'track'
294
+ element :artist, 'artist'
295
+ element :time, 'time', Time
296
+ }
297
+ }
298
+ }
299
+
300
+ it 'works!' do
301
+ plays = subject.deserialise(xml).plays
302
+
303
+ plays[0].track.must_equal '505'
304
+ plays[0].artist.must_equal 'Arctic Monkeys'
305
+ plays[0].time.must_equal Time.utc(2013, 10, 12, 15, 34, 50)
306
+
307
+ plays[1].track.must_equal 'Windowlicker'
308
+ plays[1].artist.must_equal 'Aphex Twin'
309
+ plays[1].time.must_equal Time.utc(2013, 10, 12, 15, 37, 43)
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,6 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+
4
+ require 'mocha/setup'
5
+
6
+ require_relative '../lib/serialisable'
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: serialisable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Hawxwell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: |2
56
+ Serialisable provides a simple DSL for turning xml into ruby objects.
57
+ email: m@hawx.me
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - Rakefile
64
+ - lib/serialisable/version.rb
65
+ - lib/serialisable.rb
66
+ - spec/spec_helper.rb
67
+ - spec/serialisable_spec.rb
68
+ homepage: http://github.com/hawx/serialisable
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.0.14
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Serialisable allows easy xml deserialisation
92
+ test_files:
93
+ - spec/spec_helper.rb
94
+ - spec/serialisable_spec.rb