serialisable 0.0.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.
@@ -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