multi_xml 0.2.2 → 0.3.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.
Potentially problematic release.
This version of multi_xml might be problematic. Click here for more details.
- data/.autotest +1 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -1
- data/Gemfile +3 -3
- data/{LICENSE.mkd → LICENSE.md} +0 -0
- data/{README.mkd → README.md} +37 -7
- data/Rakefile +3 -1
- data/lib/multi_xml.rb +53 -44
- data/lib/multi_xml/parsers/libxml.rb +13 -62
- data/lib/multi_xml/parsers/libxml2_parser.rb +66 -0
- data/lib/multi_xml/parsers/nokogiri.rb +16 -63
- data/lib/multi_xml/parsers/rexml.rb +8 -20
- data/lib/multi_xml/version.rb +1 -1
- data/multi_xml.gemspec +20 -22
- data/spec/helper.rb +0 -1
- data/spec/multi_xml_spec.rb +14 -551
- data/spec/parser_shared_example.rb +550 -0
- metadata +80 -94
- data/lib/multi_xml/core_extensions.rb +0 -108
@@ -0,0 +1,550 @@
|
|
1
|
+
shared_examples_for "a parser" do |parser|
|
2
|
+
|
3
|
+
before do
|
4
|
+
begin
|
5
|
+
MultiXml.parser = parser
|
6
|
+
rescue LoadError
|
7
|
+
pending "Parser #{parser} couldn't be loaded"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".parse" do
|
12
|
+
context "a blank string" do
|
13
|
+
before do
|
14
|
+
@xml = ''
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return an empty Hash" do
|
18
|
+
MultiXml.parse(@xml).should == {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "a whitespace string" do
|
23
|
+
before do
|
24
|
+
@xml = ' '
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should return an empty Hash" do
|
28
|
+
MultiXml.parse(@xml).should == {}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "an invalid XML document" do
|
33
|
+
before do
|
34
|
+
@xml = '<open></close>'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise MultiXml::ParseError" do
|
38
|
+
lambda do
|
39
|
+
MultiXml.parse(@xml)
|
40
|
+
end.should raise_error(MultiXml::ParseError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "a valid XML document" do
|
45
|
+
before do
|
46
|
+
@xml = '<user/>'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should parse correctly" do
|
50
|
+
MultiXml.parse(@xml).should == {'user' => nil}
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with CDATA" do
|
54
|
+
before do
|
55
|
+
@xml = '<user><![CDATA[Erik Michaels-Ober]]></user>'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return the correct CDATA" do
|
59
|
+
MultiXml.parse(@xml)['user'].should == "Erik Michaels-Ober"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "with content" do
|
64
|
+
before do
|
65
|
+
@xml = '<user>Erik Michaels-Ober</user>'
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should return the correct content" do
|
69
|
+
MultiXml.parse(@xml)['user'].should == "Erik Michaels-Ober"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with an attribute" do
|
74
|
+
before do
|
75
|
+
@xml = '<user name="Erik Michaels-Ober"/>'
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return the correct attribute" do
|
79
|
+
MultiXml.parse(@xml)['user']['name'].should == "Erik Michaels-Ober"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "with multiple attributes" do
|
84
|
+
before do
|
85
|
+
@xml = '<user name="Erik Michaels-Ober" screen_name="sferik"/>'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should return the correct attributes" do
|
89
|
+
MultiXml.parse(@xml)['user']['name'].should be == "Erik Michaels-Ober"
|
90
|
+
MultiXml.parse(@xml)['user']['screen_name'].should be == "sferik"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "with :symbolize_keys => true" do
|
95
|
+
before do
|
96
|
+
@xml = '<user><name>Erik Michaels-Ober</name></user>'
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should symbolize keys" do
|
100
|
+
MultiXml.parse(@xml, :symbolize_keys => true).should == {:user => {:name => "Erik Michaels-Ober"}}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when value is true" do
|
105
|
+
before do
|
106
|
+
pending
|
107
|
+
@xml = '<tag>true</tag>'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should return true" do
|
111
|
+
MultiXml.parse(@xml)['tag'].should be_true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when value is false" do
|
116
|
+
before do
|
117
|
+
pending
|
118
|
+
@xml = '<tag>false</tag>'
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should return false" do
|
122
|
+
MultiXml.parse(@xml)['tag'].should be_false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when key is id" do
|
127
|
+
before do
|
128
|
+
pending
|
129
|
+
@xml = '<id>1</id>'
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return a Fixnum" do
|
133
|
+
MultiXml.parse(@xml)['id'].should be_a(Fixnum)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should return the correct number" do
|
137
|
+
MultiXml.parse(@xml)['id'].should == 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when key contains _id" do
|
142
|
+
before do
|
143
|
+
pending
|
144
|
+
@xml = '<tag_id>1</tag_id>'
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should return a Fixnum" do
|
148
|
+
MultiXml.parse(@xml)['tag_id'].should be_a(Fixnum)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should return the correct number" do
|
152
|
+
MultiXml.parse(@xml)['tag_id'].should == 1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "with an attribute type=\"boolean\"" do
|
157
|
+
%w(true false).each do |boolean|
|
158
|
+
context "when #{boolean}" do
|
159
|
+
it "should return #{boolean}" do
|
160
|
+
xml = "<tag type=\"boolean\">#{boolean}</tag>"
|
161
|
+
MultiXml.parse(xml)['tag'].should instance_eval("be_#{boolean}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when 1" do
|
167
|
+
before do
|
168
|
+
@xml = '<tag type="boolean">1</tag>'
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should return true" do
|
172
|
+
MultiXml.parse(@xml)['tag'].should be_true
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "when 0" do
|
177
|
+
before do
|
178
|
+
@xml = '<tag type="boolean">0</tag>'
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should return false" do
|
182
|
+
MultiXml.parse(@xml)['tag'].should be_false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "with an attribute type=\"integer\"" do
|
188
|
+
context "with a positive integer" do
|
189
|
+
before do
|
190
|
+
@xml = '<tag type="integer">1</tag>'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should return a Fixnum" do
|
194
|
+
MultiXml.parse(@xml)['tag'].should be_a(Fixnum)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should return a positive number" do
|
198
|
+
MultiXml.parse(@xml)['tag'].should > 0
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should return the correct number" do
|
202
|
+
MultiXml.parse(@xml)['tag'].should == 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with a negative integer" do
|
207
|
+
before do
|
208
|
+
@xml = '<tag type="integer">-1</tag>'
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should return a Fixnum" do
|
212
|
+
MultiXml.parse(@xml)['tag'].should be_a(Fixnum)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should return a negative number" do
|
216
|
+
MultiXml.parse(@xml)['tag'].should < 0
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should return the correct number" do
|
220
|
+
MultiXml.parse(@xml)['tag'].should == -1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "with an attribute type=\"string\"" do
|
226
|
+
before do
|
227
|
+
@xml = '<tag type="string"></tag>'
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should return a String" do
|
231
|
+
MultiXml.parse(@xml)['tag'].should be_a(String)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should return the correct string" do
|
235
|
+
MultiXml.parse(@xml)['tag'].should == ""
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "with an attribute type=\"date\"" do
|
240
|
+
before do
|
241
|
+
@xml = '<tag type="date">1970-01-01</tag>'
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should return a Date" do
|
245
|
+
MultiXml.parse(@xml)['tag'].should be_a(Date)
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should return the correct date" do
|
249
|
+
MultiXml.parse(@xml)['tag'].should == Date.parse('1970-01-01')
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context "with an attribute type=\"datetime\"" do
|
254
|
+
before do
|
255
|
+
@xml = '<tag type="datetime">1970-01-01 00:00</tag>'
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should return a Time" do
|
259
|
+
MultiXml.parse(@xml)['tag'].should be_a(Time)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should return the correct time" do
|
263
|
+
MultiXml.parse(@xml)['tag'].should == Time.parse('1970-01-01 00:00')
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "with an attribute type=\"dateTime\"" do
|
268
|
+
before do
|
269
|
+
@xml = '<tag type="datetime">1970-01-01 00:00</tag>'
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should return a Time" do
|
273
|
+
MultiXml.parse(@xml)['tag'].should be_a(Time)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should return the correct time" do
|
277
|
+
MultiXml.parse(@xml)['tag'].should == Time.parse('1970-01-01 00:00')
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "with an attribute type=\"double\"" do
|
282
|
+
before do
|
283
|
+
@xml = '<tag type="double">3.14159265358979</tag>'
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should return a Float" do
|
287
|
+
MultiXml.parse(@xml)['tag'].should be_a(Float)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should return the correct number" do
|
291
|
+
MultiXml.parse(@xml)['tag'].should == 3.14159265358979
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
context "with an attribute type=\"decimal\"" do
|
296
|
+
before do
|
297
|
+
@xml = '<tag type="decimal">3.14159265358979323846264338327950288419716939937510</tag>'
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should return a BigDecimal" do
|
301
|
+
MultiXml.parse(@xml)['tag'].should be_a(BigDecimal)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should return the correct number" do
|
305
|
+
MultiXml.parse(@xml)['tag'].should == 3.14159265358979323846264338327950288419716939937510
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "with an attribute type=\"base64Binary\"" do
|
310
|
+
before do
|
311
|
+
@xml = '<tag type="base64Binary">aW1hZ2UucG5n</tag>'
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should return a String" do
|
315
|
+
MultiXml.parse(@xml)['tag'].should be_a(String)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should return the correct string" do
|
319
|
+
MultiXml.parse(@xml)['tag'].should == "image.png"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "with an attribute type=\"yaml\"" do
|
324
|
+
before do
|
325
|
+
@xml = "<tag type=\"yaml\">--- \n1: should return an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</tag>"
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should return a Hash" do
|
329
|
+
MultiXml.parse(@xml)['tag'].should be_a(Hash)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should return the correctly parsed YAML" do
|
333
|
+
MultiXml.parse(@xml)['tag'].should == {:message => "Have a nice day", 1 => "should return an integer", "array" => [{"should-have-dashes" => true, "should_have_underscores" => true}]}
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context "with an attribute type=\"file\"" do
|
338
|
+
before do
|
339
|
+
@xml = '<tag type="file" name="data.txt" content_type="text/plain">ZGF0YQ==</tag>'
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should return a StringIO" do
|
343
|
+
MultiXml.parse(@xml)['tag'].should be_a(StringIO)
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should be decoded correctly" do
|
347
|
+
MultiXml.parse(@xml)['tag'].string.should == 'data'
|
348
|
+
end
|
349
|
+
|
350
|
+
it "should have the correct file name" do
|
351
|
+
MultiXml.parse(@xml)['tag'].original_filename.should == 'data.txt'
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should have the correct content type" do
|
355
|
+
MultiXml.parse(@xml)['tag'].content_type.should == 'text/plain'
|
356
|
+
end
|
357
|
+
|
358
|
+
context "with missing name and content type" do
|
359
|
+
before do
|
360
|
+
@xml = '<tag type="file">ZGF0YQ==</tag>'
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should return a StringIO" do
|
364
|
+
MultiXml.parse(@xml)['tag'].should be_a(StringIO)
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should be decoded correctly" do
|
368
|
+
MultiXml.parse(@xml)['tag'].string.should == 'data'
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should have the default file name" do
|
372
|
+
MultiXml.parse(@xml)['tag'].original_filename.should == 'untitled'
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should have the default content type" do
|
376
|
+
MultiXml.parse(@xml)['tag'].content_type.should == 'application/octet-stream'
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
context "with an attribute type=\"array\"" do
|
382
|
+
before do
|
383
|
+
@xml = '<users type="array"><user>Erik Michaels-Ober</user><user>Wynn Netherland</user></users>'
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should return an Array" do
|
387
|
+
MultiXml.parse(@xml)['users'].should be_a(Array)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should return the correct array" do
|
391
|
+
MultiXml.parse(@xml)['users'].should == ["Erik Michaels-Ober", "Wynn Netherland"]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
%w(integer boolean date datetime yaml file).each do |type|
|
396
|
+
context "with an empty attribute type=\"#{type}\"" do
|
397
|
+
before do
|
398
|
+
@xml = "<tag type=\"#{type}\"/>"
|
399
|
+
end
|
400
|
+
|
401
|
+
it "should return nil" do
|
402
|
+
MultiXml.parse(@xml)['tag'].should be_nil
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
context "with an empty attribute type=\"array\"" do
|
408
|
+
before do
|
409
|
+
@xml = '<tag type="array"/>'
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should return an empty Array" do
|
413
|
+
MultiXml.parse(@xml)['tag'].should == []
|
414
|
+
end
|
415
|
+
|
416
|
+
context "with whitespace" do
|
417
|
+
before do
|
418
|
+
@xml = '<tag type="array"> </tag>'
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should return an empty Array" do
|
422
|
+
MultiXml.parse(@xml)['tag'].should == []
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
context "with XML entities" do
|
428
|
+
before do
|
429
|
+
@xml_entities = {
|
430
|
+
"<" => "<",
|
431
|
+
">" => ">",
|
432
|
+
'"' => """,
|
433
|
+
"'" => "'",
|
434
|
+
"&" => "&"
|
435
|
+
}
|
436
|
+
end
|
437
|
+
|
438
|
+
context "in content" do
|
439
|
+
it "should return unescaped XML entities" do
|
440
|
+
@xml_entities.each do |key, value|
|
441
|
+
xml = "<tag>#{value}</tag>"
|
442
|
+
MultiXml.parse(xml)['tag'].should == key
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
context "in attribute" do
|
448
|
+
it "should return unescaped XML entities" do
|
449
|
+
@xml_entities.each do |key, value|
|
450
|
+
xml = "<tag attribute=\"#{value}\"/>"
|
451
|
+
MultiXml.parse(xml)['tag']['attribute'].should == key
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context "with dasherized tag" do
|
458
|
+
before do
|
459
|
+
@xml = '<tag-1/>'
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should return undasherize tag" do
|
463
|
+
MultiXml.parse(@xml).keys.should include('tag_1')
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
context "with dasherized attribute" do
|
468
|
+
before do
|
469
|
+
@xml = '<tag attribute-1="1"></tag>'
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should return undasherize attribute" do
|
473
|
+
MultiXml.parse(@xml)['tag'].keys.should include('attribute_1')
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
context "with children" do
|
478
|
+
context "with attributes" do
|
479
|
+
before do
|
480
|
+
@xml = '<users><user name="Erik Michaels-Ober"/></users>'
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should return the correct attributes" do
|
484
|
+
MultiXml.parse(@xml)['users']['user']['name'].should == "Erik Michaels-Ober"
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
context "with text" do
|
489
|
+
before do
|
490
|
+
@xml = '<user><name>Erik Michaels-Ober</name></user>'
|
491
|
+
end
|
492
|
+
|
493
|
+
it "should return the correct text" do
|
494
|
+
MultiXml.parse(@xml)['user']['name'].should == "Erik Michaels-Ober"
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
context "with an unrecognized attribute type" do
|
499
|
+
before do
|
500
|
+
@xml = '<user type="admin"><name>Erik Michaels-Ober</name></user>'
|
501
|
+
end
|
502
|
+
|
503
|
+
it "should pass through the type" do
|
504
|
+
MultiXml.parse(@xml)['user']['type'].should == 'admin'
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context "with newlines and whitespace" do
|
509
|
+
before do
|
510
|
+
@xml = <<-XML
|
511
|
+
<user>
|
512
|
+
<name>Erik Michaels-Ober</name>
|
513
|
+
</user>
|
514
|
+
XML
|
515
|
+
end
|
516
|
+
|
517
|
+
it "should parse correctly" do
|
518
|
+
MultiXml.parse(@xml).should == {"user" => {"name" => "Erik Michaels-Ober"}}
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
# Babies having babies
|
523
|
+
context "with children" do
|
524
|
+
before do
|
525
|
+
@xml = '<users><user name="Erik Michaels-Ober"><status text="Hello"/></user></users>'
|
526
|
+
end
|
527
|
+
|
528
|
+
it "should parse correctly" do
|
529
|
+
MultiXml.parse(@xml).should == {"users" => {"user" => {"name" => "Erik Michaels-Ober", "status" => {"text" => "Hello"}}}}
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
context "with sibling children" do
|
535
|
+
before do
|
536
|
+
@xml = '<users><user>Erik Michaels-Ober</user><user>Wynn Netherland</user></users>'
|
537
|
+
end
|
538
|
+
|
539
|
+
it "should return an Array" do
|
540
|
+
MultiXml.parse(@xml)['users']['user'].should be_a(Array)
|
541
|
+
end
|
542
|
+
|
543
|
+
it "should parse correctly" do
|
544
|
+
MultiXml.parse(@xml).should == {"users" => {"user" => ["Erik Michaels-Ober", "Wynn Netherland"]}}
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
end
|