sax-machine 0.1.0 → 1.3.2
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +32 -0
- data/Gemfile +13 -2
- data/Guardfile +5 -0
- data/HISTORY.md +77 -0
- data/README.md +207 -0
- data/Rakefile +6 -20
- data/lib/sax-machine/{sax_ancestor_config.rb → config/sax_ancestor.rb} +3 -7
- data/lib/sax-machine/config/sax_attribute.rb +18 -0
- data/lib/sax-machine/config/sax_collection.rb +33 -0
- data/lib/sax-machine/{sax_element_config.rb → config/sax_element.rb} +23 -31
- data/lib/sax-machine/{sax_element_value_config.rb → config/sax_element_value.rb} +7 -8
- data/lib/sax-machine/handlers/sax_abstract_handler.rb +199 -0
- data/lib/sax-machine/handlers/sax_nokogiri_handler.rb +23 -0
- data/lib/sax-machine/handlers/sax_oga_handler.rb +39 -0
- data/lib/sax-machine/handlers/sax_ox_handler.rb +56 -0
- data/lib/sax-machine/sax_config.rb +13 -9
- data/lib/sax-machine/sax_configure.rb +3 -8
- data/lib/sax-machine/sax_document.rb +79 -49
- data/lib/sax-machine/version.rb +3 -0
- data/lib/sax-machine.rb +26 -7
- data/sax-machine.gemspec +20 -0
- data/spec/fixtures/atom-content.html +15 -0
- data/spec/fixtures/atom.xml +165 -0
- data/spec/sax-machine/sax_activerecord_spec.rb +21 -0
- data/spec/sax-machine/sax_configure_spec.rb +51 -0
- data/spec/sax-machine/sax_document_spec.rb +709 -239
- data/spec/sax-machine/sax_include_spec.rb +49 -0
- data/spec/spec_helper.rb +18 -7
- metadata +71 -70
- data/README.textile +0 -110
- data/lib/sax-machine/sax_attribute_config.rb +0 -40
- data/lib/sax-machine/sax_collection_config.rb +0 -45
- data/lib/sax-machine/sax_handler.rb +0 -107
- data/spec/sax-machine/configure_sax_machine_spec.rb +0 -53
- data/spec/sax-machine/include_sax_machine_spec.rb +0 -42
@@ -1,26 +1,30 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../spec_helper'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe "SAXMachine" do
|
4
4
|
describe "element" do
|
5
5
|
describe "when parsing a single element" do
|
6
|
-
before
|
6
|
+
before do
|
7
7
|
@klass = Class.new do
|
8
8
|
include SAXMachine
|
9
9
|
element :title
|
10
|
+
ancestor :body
|
11
|
+
value :something, required: false
|
12
|
+
attribute :anything, required: true
|
10
13
|
end
|
11
14
|
end
|
12
15
|
|
13
|
-
it "
|
14
|
-
document = @klass.new
|
15
|
-
document.title
|
16
|
-
document.title.should == "Title"
|
16
|
+
it "provides mass assignment through initialize method" do
|
17
|
+
document = @klass.new(title: "Title")
|
18
|
+
expect(document.title).to eq("Title")
|
17
19
|
end
|
18
20
|
|
19
|
-
it "
|
20
|
-
@klass.
|
21
|
+
it "provides an accessor" do
|
22
|
+
document = @klass.new
|
23
|
+
document.title = "Title"
|
24
|
+
expect(document.title).to eq("Title")
|
21
25
|
end
|
22
26
|
|
23
|
-
it "
|
27
|
+
it "does not overwrites the getter is there is already one present" do
|
24
28
|
@klass = Class.new do
|
25
29
|
def title
|
26
30
|
"#{@title} ***"
|
@@ -29,12 +33,13 @@ describe "SAXMachine" do
|
|
29
33
|
include SAXMachine
|
30
34
|
element :title
|
31
35
|
end
|
36
|
+
|
32
37
|
document = @klass.new
|
33
38
|
document.title = "Title"
|
34
|
-
document.title.
|
39
|
+
expect(document.title).to eq("Title ***")
|
35
40
|
end
|
36
41
|
|
37
|
-
it "
|
42
|
+
it "does not overwrites the setter if there is already one present" do
|
38
43
|
@klass = Class.new do
|
39
44
|
def title=(val)
|
40
45
|
@title = "#{val} **"
|
@@ -43,71 +48,363 @@ describe "SAXMachine" do
|
|
43
48
|
include SAXMachine
|
44
49
|
element :title
|
45
50
|
end
|
51
|
+
|
46
52
|
document = @klass.new
|
47
53
|
document.title = "Title"
|
48
|
-
document.title.
|
49
|
-
end
|
50
|
-
describe "the class attribute" do
|
51
|
-
before(:each) do
|
52
|
-
@klass = Class.new do
|
53
|
-
include SAXMachine
|
54
|
-
element :date, :class => DateTime
|
55
|
-
end
|
56
|
-
@document = @klass.new
|
57
|
-
@document.date = DateTime.now.to_s
|
58
|
-
end
|
59
|
-
it "should be available" do
|
60
|
-
@klass.data_class(:date).should == DateTime
|
61
|
-
end
|
62
|
-
end
|
63
|
-
describe "the required attribute" do
|
64
|
-
it "should be available" do
|
65
|
-
@klass = Class.new do
|
66
|
-
include SAXMachine
|
67
|
-
element :date, :required => true
|
68
|
-
end
|
69
|
-
@klass.required?(:date).should be_true
|
70
|
-
end
|
54
|
+
expect(document.title).to eq("Title **")
|
71
55
|
end
|
72
56
|
|
73
|
-
it "
|
57
|
+
it "does not overwrites the accessor when the element is not present" do
|
74
58
|
document = @klass.new
|
75
59
|
document.title = "Title"
|
76
60
|
document.parse("<foo></foo>")
|
77
|
-
document.title.
|
61
|
+
expect(document.title).to eq("Title")
|
78
62
|
end
|
79
63
|
|
80
|
-
it "
|
64
|
+
it "overwrites the value when the element is present" do
|
81
65
|
document = @klass.new
|
82
66
|
document.title = "Old title"
|
83
67
|
document.parse("<title>New title</title>")
|
84
|
-
document.title.
|
68
|
+
expect(document.title).to eq("New title")
|
85
69
|
end
|
86
70
|
|
87
|
-
it "
|
71
|
+
it "saves the element text into an accessor" do
|
88
72
|
document = @klass.parse("<title>My Title</title>")
|
89
|
-
document.title.
|
73
|
+
expect(document.title).to eq("My Title")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "keeps the document encoding for elements" do
|
77
|
+
data = "<title>My Title</title>"
|
78
|
+
data.encode!("utf-8")
|
79
|
+
|
80
|
+
document = @klass.parse(data)
|
81
|
+
expect(document.title.encoding).to eq(data.encoding)
|
90
82
|
end
|
91
83
|
|
92
|
-
it "
|
84
|
+
it "saves cdata into an accessor" do
|
93
85
|
document = @klass.parse("<title><![CDATA[A Title]]></title>")
|
94
|
-
document.title.
|
86
|
+
expect(document.title).to eq("A Title")
|
95
87
|
end
|
96
88
|
|
97
|
-
it "
|
89
|
+
it "saves the element text into an accessor when there are multiple elements" do
|
98
90
|
document = @klass.parse("<xml><title>My Title</title><foo>bar</foo></xml>")
|
99
|
-
document.title.
|
91
|
+
expect(document.title).to eq("My Title")
|
100
92
|
end
|
101
93
|
|
102
|
-
it "
|
94
|
+
it "saves the first element text when there are multiple of the same element" do
|
103
95
|
document = @klass.parse("<xml><title>My Title</title><title>bar</title></xml>")
|
104
|
-
document.title.
|
96
|
+
expect(document.title).to eq("My Title")
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "the introspection" do
|
100
|
+
it "allows to get column names" do
|
101
|
+
expect(@klass.column_names).to match_array([:title])
|
102
|
+
end
|
103
|
+
|
104
|
+
it "allows to get elements" do
|
105
|
+
expect(@klass.sax_config.top_level_elements.values.flatten.map(&:to_s)).to \
|
106
|
+
match_array(["name: title dataclass: setter: title= required: value: as:title collection: with: {}"])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "allows to get ancestors" do
|
110
|
+
expect(@klass.sax_config.ancestors.map(&:column)).to \
|
111
|
+
match_array([:body])
|
112
|
+
end
|
113
|
+
|
114
|
+
it "allows to get values" do
|
115
|
+
expect(@klass.sax_config.top_level_element_value.map(&:column)).to \
|
116
|
+
match_array([:something])
|
117
|
+
expect(@klass.sax_config.top_level_element_value.map(&:required?)).to \
|
118
|
+
match_array([false])
|
119
|
+
end
|
120
|
+
|
121
|
+
it "allows to get attributes" do
|
122
|
+
expect(@klass.sax_config.top_level_attributes.map(&:column)).to \
|
123
|
+
match_array([:anything])
|
124
|
+
expect(@klass.sax_config.top_level_attributes.map(&:required?)).to \
|
125
|
+
match_array([true])
|
126
|
+
expect(@klass.sax_config.top_level_attributes.map(&:collection?)).to \
|
127
|
+
match_array([false])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "the class attribute" do
|
132
|
+
before(:each) do
|
133
|
+
@klass = Class.new do
|
134
|
+
include SAXMachine
|
135
|
+
element :date, class: DateTime
|
136
|
+
end
|
137
|
+
|
138
|
+
@document = @klass.new
|
139
|
+
@document.date = Time.now.iso8601
|
140
|
+
end
|
141
|
+
|
142
|
+
it "is available" do
|
143
|
+
expect(@klass.data_class(:date)).to eq(DateTime)
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "string" do
|
147
|
+
before do
|
148
|
+
class TestString
|
149
|
+
include SAXMachine
|
150
|
+
element :number, class: String
|
151
|
+
end
|
152
|
+
|
153
|
+
class TestStringAttribute
|
154
|
+
include SAXMachine
|
155
|
+
attribute :sub_number, class: String
|
156
|
+
end
|
157
|
+
|
158
|
+
class TestStringWithAttribute
|
159
|
+
include SAXMachine
|
160
|
+
element :number, class: TestStringAttribute
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it "is handled in an element" do
|
165
|
+
document = TestString.parse("<number>5.5</number>")
|
166
|
+
expect(document.number).to eq("5.5")
|
167
|
+
end
|
168
|
+
|
169
|
+
it "is handled in an attribute" do
|
170
|
+
document = TestStringWithAttribute.parse("<number sub_number='5.5'></number>")
|
171
|
+
expect(document.number.sub_number).to eq("5.5")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "integer" do
|
176
|
+
before do
|
177
|
+
class TestInteger
|
178
|
+
include SAXMachine
|
179
|
+
element :number, class: Integer
|
180
|
+
end
|
181
|
+
|
182
|
+
class TestIntegerAttribute
|
183
|
+
include SAXMachine
|
184
|
+
attribute :sub_number, class: Integer
|
185
|
+
end
|
186
|
+
|
187
|
+
class TestIntegerWithAttribute
|
188
|
+
include SAXMachine
|
189
|
+
element :number, class: TestIntegerAttribute
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "is handled in an element" do
|
194
|
+
document = TestInteger.parse("<number>5</number>")
|
195
|
+
expect(document.number).to eq(5)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "is handled in an attribute" do
|
199
|
+
document = TestIntegerWithAttribute.parse("<number sub_number='5'></number>")
|
200
|
+
expect(document.number.sub_number).to eq(5)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "float" do
|
205
|
+
before do
|
206
|
+
class TestFloat
|
207
|
+
include SAXMachine
|
208
|
+
element :number, class: Float
|
209
|
+
end
|
210
|
+
|
211
|
+
class TestFloatAttribute
|
212
|
+
include SAXMachine
|
213
|
+
attribute :sub_number, class: Float
|
214
|
+
end
|
215
|
+
|
216
|
+
class TestFloatWithAttribute
|
217
|
+
include SAXMachine
|
218
|
+
element :number, class: TestFloatAttribute
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it "is handled in an element with '.' delimiter" do
|
223
|
+
document = TestFloat.parse("<number>5.5</number>")
|
224
|
+
expect(document.number).to eq(5.5)
|
225
|
+
end
|
226
|
+
|
227
|
+
it "is handled in an element with ',' delimiter" do
|
228
|
+
document = TestFloat.parse("<number>5,5</number>")
|
229
|
+
expect(document.number).to eq(5.5)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "is handled in an attribute" do
|
233
|
+
document = TestFloatWithAttribute.parse("<number sub_number='5.5'>5.5</number>")
|
234
|
+
expect(document.number.sub_number).to eq(5.5)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "symbol" do
|
239
|
+
before do
|
240
|
+
class TestSymbol
|
241
|
+
include SAXMachine
|
242
|
+
element :symbol, class: Symbol
|
243
|
+
end
|
244
|
+
|
245
|
+
class TestSymbolAttribute
|
246
|
+
include SAXMachine
|
247
|
+
attribute :sub_symbol, class: Symbol
|
248
|
+
end
|
249
|
+
|
250
|
+
class TestSymbolWithAttribute
|
251
|
+
include SAXMachine
|
252
|
+
element :symbol, class: TestSymbolAttribute
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
it "is handled in an element" do
|
257
|
+
document = TestSymbol.parse("<symbol>MY_SYMBOL_VALUE</symbol>")
|
258
|
+
expect(document.symbol).to eq(:my_symbol_value)
|
259
|
+
end
|
260
|
+
|
261
|
+
it "is handled in an attribute" do
|
262
|
+
document = TestSymbolWithAttribute.parse("<symbol sub_symbol='MY_SYMBOL_VALUE'></symbol>")
|
263
|
+
expect(document.symbol.sub_symbol).to eq(:my_symbol_value)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "time" do
|
268
|
+
before do
|
269
|
+
class TestTime
|
270
|
+
include SAXMachine
|
271
|
+
element :time, class: Time
|
272
|
+
end
|
273
|
+
|
274
|
+
class TestTimeAttribute
|
275
|
+
include SAXMachine
|
276
|
+
attribute :sub_time, class: Time
|
277
|
+
end
|
278
|
+
|
279
|
+
class TestTimeWithAttribute
|
280
|
+
include SAXMachine
|
281
|
+
element :time, class: TestTimeAttribute
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
it "is handled in an element" do
|
286
|
+
document = TestTime.parse("<time>1994-02-04T06:20:00Z</time>")
|
287
|
+
expect(document.time).to eq(Time.utc(1994, 2, 4, 6, 20, 0, 0))
|
288
|
+
end
|
289
|
+
|
290
|
+
it "is handled in an attribute" do
|
291
|
+
document = TestTimeWithAttribute.parse("<time sub_time='1994-02-04T06:20:00Z'>1994-02-04T06:20:00Z</time>")
|
292
|
+
expect(document.time.sub_time).to eq(Time.utc(1994, 2, 4, 6, 20, 0, 0))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "the default attribute" do
|
298
|
+
it "is available" do
|
299
|
+
@klass = Class.new do
|
300
|
+
include SAXMachine
|
301
|
+
element :number, class: Integer, default: 0
|
302
|
+
end
|
303
|
+
|
304
|
+
document = @klass.parse("<no>number</no>")
|
305
|
+
expect(document.number).to eq(0)
|
306
|
+
|
307
|
+
document = @klass.parse("<number></number>")
|
308
|
+
expect(document.number).to eq(0)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "can be a Boolean" do
|
312
|
+
@klass = Class.new do
|
313
|
+
include SAXMachine
|
314
|
+
element(:bool, default: false) { |v| !!v }
|
315
|
+
end
|
316
|
+
|
317
|
+
document = @klass.parse("<no>bool</no>")
|
318
|
+
expect(document.bool).to be false
|
319
|
+
|
320
|
+
document = @klass.parse("<bool></bool>")
|
321
|
+
expect(document.bool).to be false
|
322
|
+
|
323
|
+
document = @klass.parse("<bool>1</bool>")
|
324
|
+
expect(document.bool).to be true
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe "the required attribute" do
|
329
|
+
it "is available" do
|
330
|
+
@klass = Class.new do
|
331
|
+
include SAXMachine
|
332
|
+
element :date, required: true
|
333
|
+
end
|
334
|
+
expect(@klass.required?(:date)).to be_truthy
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe "the block" do
|
339
|
+
before do
|
340
|
+
class ElementBlockParser
|
341
|
+
include SAXMachine
|
342
|
+
|
343
|
+
ancestor :parent do |parent|
|
344
|
+
parent.class.to_s
|
345
|
+
end
|
346
|
+
|
347
|
+
value :text do |text|
|
348
|
+
text.downcase
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
class BlockParser
|
353
|
+
include SAXMachine
|
354
|
+
|
355
|
+
element :title do |title|
|
356
|
+
"#{title}!!!"
|
357
|
+
end
|
358
|
+
|
359
|
+
element :scope do |scope|
|
360
|
+
"#{title} #{scope}"
|
361
|
+
end
|
362
|
+
|
363
|
+
attribute :id do |id|
|
364
|
+
id.to_i
|
365
|
+
end
|
366
|
+
|
367
|
+
element :nested, class: ElementBlockParser
|
368
|
+
elements :message, as: :messages do |message|
|
369
|
+
"#{message}!"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
it "has instance as a block context" do
|
375
|
+
document = BlockParser.parse("<root><title>SAX</title><scope>something</scope></root>")
|
376
|
+
expect(document.scope).to eq("SAX!!! something")
|
377
|
+
end
|
378
|
+
|
379
|
+
it "uses block for element" do
|
380
|
+
document = BlockParser.parse("<title>SAX</title>")
|
381
|
+
expect(document.title).to eq("SAX!!!")
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'uses block for attribute' do
|
385
|
+
document = BlockParser.parse("<title id='345'>SAX</title>")
|
386
|
+
expect(document.id).to eq(345)
|
387
|
+
end
|
388
|
+
|
389
|
+
it "uses block for value" do
|
390
|
+
document = BlockParser.parse("<title><nested>tEst</nested></title>")
|
391
|
+
expect(document.nested.text).to eq("test")
|
392
|
+
end
|
105
393
|
|
394
|
+
it "uses block for ancestor" do
|
395
|
+
document = BlockParser.parse("<title><nested>SAX</nested></title>")
|
396
|
+
expect(document.nested.parent).to eq("BlockParser")
|
397
|
+
end
|
398
|
+
|
399
|
+
it "uses block for elements" do
|
400
|
+
document = BlockParser.parse("<title><message>hi</message><message>world</message></title>")
|
401
|
+
expect(document.messages).to eq(["hi!", "world!"])
|
402
|
+
end
|
106
403
|
end
|
107
404
|
end
|
108
405
|
|
109
406
|
describe "when parsing multiple elements" do
|
110
|
-
before
|
407
|
+
before do
|
111
408
|
@klass = Class.new do
|
112
409
|
include SAXMachine
|
113
410
|
element :title
|
@@ -115,14 +412,13 @@ describe "SAXMachine" do
|
|
115
412
|
end
|
116
413
|
end
|
117
414
|
|
118
|
-
it "
|
415
|
+
it "saves the element text for a second tag" do
|
119
416
|
document = @klass.parse("<xml><title>My Title</title><name>Paul</name></xml>")
|
120
|
-
document.name.
|
121
|
-
document.title.
|
417
|
+
expect(document.name).to eq("Paul")
|
418
|
+
expect(document.title).to eq("My Title")
|
122
419
|
end
|
123
420
|
|
124
|
-
|
125
|
-
it "should not overwrite the getter is there is already one present" do
|
421
|
+
it "does not overwrites the getter is there is already one present" do
|
126
422
|
@klass = Class.new do
|
127
423
|
def items
|
128
424
|
[]
|
@@ -131,12 +427,13 @@ describe "SAXMachine" do
|
|
131
427
|
include SAXMachine
|
132
428
|
elements :items
|
133
429
|
end
|
430
|
+
|
134
431
|
document = @klass.new
|
135
432
|
document.items = [1, 2, 3, 4]
|
136
|
-
document.items.
|
433
|
+
expect(document.items).to eq([])
|
137
434
|
end
|
138
435
|
|
139
|
-
it "
|
436
|
+
it "does not overwrites the setter if there is already one present" do
|
140
437
|
@klass = Class.new do
|
141
438
|
def items=(val)
|
142
439
|
@items = [1, *val]
|
@@ -145,226 +442,255 @@ describe "SAXMachine" do
|
|
145
442
|
include SAXMachine
|
146
443
|
elements :items
|
147
444
|
end
|
445
|
+
|
148
446
|
document = @klass.new
|
149
447
|
document.items = [2, 3]
|
150
|
-
document.items.
|
448
|
+
expect(document.items).to eq([1, 2, 3])
|
151
449
|
end
|
152
|
-
|
153
450
|
end
|
154
451
|
|
155
452
|
describe "when using options for parsing elements" do
|
156
453
|
describe "using the 'as' option" do
|
157
|
-
before
|
454
|
+
before do
|
158
455
|
@klass = Class.new do
|
159
456
|
include SAXMachine
|
160
|
-
element :description, :
|
457
|
+
element :description, as: :summary
|
161
458
|
end
|
162
459
|
end
|
163
460
|
|
164
|
-
it "
|
461
|
+
it "provides an accessor using the 'as' name" do
|
165
462
|
document = @klass.new
|
166
463
|
document.summary = "a small summary"
|
167
|
-
document.summary.
|
464
|
+
expect(document.summary).to eq("a small summary")
|
168
465
|
end
|
169
466
|
|
170
|
-
it "
|
467
|
+
it "saves the element text into the 'as' accessor" do
|
171
468
|
document = @klass.parse("<description>here is a description</description>")
|
172
|
-
document.summary.
|
469
|
+
expect(document.summary).to eq("here is a description")
|
173
470
|
end
|
174
471
|
end
|
175
472
|
|
176
473
|
describe "using the :with option" do
|
177
474
|
describe "and the :value option" do
|
178
|
-
before
|
475
|
+
before do
|
179
476
|
@klass = Class.new do
|
180
477
|
include SAXMachine
|
181
|
-
element :link, :
|
478
|
+
element :link, value: :href, with: { foo: "bar" }
|
182
479
|
end
|
183
480
|
end
|
184
481
|
|
185
|
-
it "
|
482
|
+
it "saves the value of a matching element" do
|
186
483
|
document = @klass.parse("<link href='test' foo='bar'>asdf</link>")
|
187
|
-
document.link.
|
484
|
+
expect(document.link).to eq("test")
|
188
485
|
end
|
189
486
|
|
190
|
-
it "
|
487
|
+
it "saves the value of the first matching element" do
|
191
488
|
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' foo='bar' /></xml>")
|
192
|
-
document.link.
|
489
|
+
expect(document.link).to eq("first")
|
193
490
|
end
|
194
491
|
|
195
492
|
describe "and the :as option" do
|
196
|
-
before
|
493
|
+
before do
|
197
494
|
@klass = Class.new do
|
198
495
|
include SAXMachine
|
199
|
-
element :link, :
|
200
|
-
element :link, :
|
496
|
+
element :link, value: :href, as: :url, with: { foo: "bar" }
|
497
|
+
element :link, value: :href, as: :second_url, with: { asdf: "jkl" }
|
201
498
|
end
|
202
499
|
end
|
203
500
|
|
204
|
-
it "
|
501
|
+
it "saves the value of the first matching element" do
|
205
502
|
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' asdf='jkl' /><link href='second' foo='bar' /></xml>")
|
206
|
-
document.url.
|
207
|
-
document.second_url.
|
503
|
+
expect(document.url).to eq("first")
|
504
|
+
expect(document.second_url).to eq("second")
|
208
505
|
end
|
209
|
-
|
210
506
|
end
|
211
507
|
end
|
212
508
|
|
213
509
|
describe "with only one element" do
|
214
|
-
before
|
510
|
+
before do
|
215
511
|
@klass = Class.new do
|
216
512
|
include SAXMachine
|
217
|
-
element :link, :
|
513
|
+
element :link, with: { foo: "bar" }
|
218
514
|
end
|
219
515
|
end
|
220
516
|
|
221
|
-
it "
|
517
|
+
it "saves the text of an element that has matching attributes" do
|
222
518
|
document = @klass.parse("<link foo=\"bar\">match</link>")
|
223
|
-
document.link.
|
519
|
+
expect(document.link).to eq("match")
|
224
520
|
end
|
225
521
|
|
226
|
-
it "
|
522
|
+
it "does not saves the text of an element that doesn't have matching attributes" do
|
227
523
|
document = @klass.parse("<link>no match</link>")
|
228
|
-
document.link.
|
524
|
+
expect(document.link).to be_nil
|
229
525
|
end
|
230
526
|
|
231
|
-
it "
|
527
|
+
it "saves the text of an element that has matching attributes when it is the second of that type" do
|
232
528
|
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">match</link></xml>")
|
233
|
-
document.link.
|
234
|
-
|
529
|
+
expect(document.link).to eq("match")
|
235
530
|
end
|
236
531
|
|
237
|
-
it "
|
532
|
+
it "saves the text of an element that has matching attributes plus a few more" do
|
238
533
|
document = @klass.parse("<xml><link>no match</link><link asdf='jkl' foo='bar'>match</link>")
|
239
|
-
document.link.
|
534
|
+
expect(document.link).to eq("match")
|
240
535
|
end
|
241
536
|
end
|
242
537
|
|
243
538
|
describe "with multiple elements of same tag" do
|
244
|
-
before
|
539
|
+
before do
|
245
540
|
@klass = Class.new do
|
246
541
|
include SAXMachine
|
247
|
-
element :link, :
|
248
|
-
element :link, :
|
542
|
+
element :link, as: :first, with: { foo: "bar" }
|
543
|
+
element :link, as: :second, with: { asdf: "jkl" }
|
249
544
|
end
|
250
545
|
end
|
251
546
|
|
252
|
-
it "
|
547
|
+
it "matches the first element" do
|
253
548
|
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">first match</link><link>no match</link></xml>")
|
254
|
-
document.first.
|
549
|
+
expect(document.first).to eq("first match")
|
255
550
|
end
|
256
551
|
|
257
|
-
it "
|
552
|
+
it "matches the second element" do
|
258
553
|
document = @klass.parse("<xml><link>no match</link><link foo='bar'>first match</link><link asdf='jkl'>second match</link><link>hi</link></xml>")
|
259
|
-
document.second.
|
554
|
+
expect(document.second).to eq("second match")
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
describe "with only one element as a regular expression" do
|
559
|
+
before do
|
560
|
+
@klass = Class.new do
|
561
|
+
include SAXMachine
|
562
|
+
element :link, with: { foo: /ar$/ }
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
it "saves the text of an element that has matching attributes" do
|
567
|
+
document = @klass.parse("<link foo=\"bar\">match</link>")
|
568
|
+
expect(document.link).to eq("match")
|
569
|
+
end
|
570
|
+
|
571
|
+
it "does not saves the text of an element that doesn't have matching attributes" do
|
572
|
+
document = @klass.parse("<link>no match</link>")
|
573
|
+
expect(document.link).to be_nil
|
574
|
+
end
|
575
|
+
|
576
|
+
it "saves the text of an element that has matching attributes when it is the second of that type" do
|
577
|
+
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">match</link></xml>")
|
578
|
+
expect(document.link).to eq("match")
|
579
|
+
end
|
580
|
+
|
581
|
+
it "saves the text of an element that has matching attributes plus a few more" do
|
582
|
+
document = @klass.parse("<xml><link>no match</link><link asdf='jkl' foo='bar'>match</link>")
|
583
|
+
expect(document.link).to eq("match")
|
260
584
|
end
|
261
585
|
end
|
262
|
-
end
|
586
|
+
end
|
263
587
|
|
264
588
|
describe "using the 'value' option" do
|
265
|
-
before
|
589
|
+
before do
|
266
590
|
@klass = Class.new do
|
267
591
|
include SAXMachine
|
268
|
-
element :link, :
|
592
|
+
element :link, value: :foo
|
269
593
|
end
|
270
594
|
end
|
271
595
|
|
272
|
-
it "
|
596
|
+
it "saves the attribute value" do
|
273
597
|
document = @klass.parse("<link foo='test'>hello</link>")
|
274
|
-
document.link.
|
598
|
+
expect(document.link).to eq("test")
|
275
599
|
end
|
276
600
|
|
277
|
-
it "
|
601
|
+
it "saves the attribute value when there is no text enclosed by the tag" do
|
278
602
|
document = @klass.parse("<link foo='test'></link>")
|
279
|
-
document.link.
|
603
|
+
expect(document.link).to eq("test")
|
280
604
|
end
|
281
605
|
|
282
|
-
it "
|
606
|
+
it "saves the attribute value when the tag close is in the open" do
|
283
607
|
document = @klass.parse("<link foo='test'/>")
|
284
|
-
document.link.
|
608
|
+
expect(document.link).to eq("test")
|
285
609
|
end
|
286
610
|
|
287
|
-
it "
|
611
|
+
it "saves two different attribute values on a single tag" do
|
288
612
|
@klass = Class.new do
|
289
613
|
include SAXMachine
|
290
|
-
element :link, :
|
291
|
-
element :link, :
|
614
|
+
element :link, value: :foo, as: :first
|
615
|
+
element :link, value: :bar, as: :second
|
292
616
|
end
|
617
|
+
|
293
618
|
document = @klass.parse("<link foo='foo value' bar='bar value'></link>")
|
294
|
-
document.first.
|
295
|
-
document.second.
|
619
|
+
expect(document.first).to eq("foo value")
|
620
|
+
expect(document.second).to eq("bar value")
|
296
621
|
end
|
297
622
|
|
298
|
-
it "
|
623
|
+
it "does not fail if one of the attribute hasn't been defined" do
|
299
624
|
@klass = Class.new do
|
300
625
|
include SAXMachine
|
301
|
-
element :link, :
|
302
|
-
element :link, :
|
626
|
+
element :link, value: :foo, as: :first
|
627
|
+
element :link, value: :bar, as: :second
|
303
628
|
end
|
629
|
+
|
304
630
|
document = @klass.parse("<link foo='foo value'></link>")
|
305
|
-
document.first.
|
306
|
-
document.second.
|
631
|
+
expect(document.first).to eq("foo value")
|
632
|
+
expect(document.second).to be_nil
|
307
633
|
end
|
308
634
|
end
|
309
635
|
|
310
636
|
describe "when desiring both the content and attributes of an element" do
|
311
|
-
before
|
637
|
+
before do
|
312
638
|
@klass = Class.new do
|
313
639
|
include SAXMachine
|
314
640
|
element :link
|
315
|
-
element :link, :
|
316
|
-
element :link, :
|
641
|
+
element :link, value: :foo, as: :link_foo
|
642
|
+
element :link, value: :bar, as: :link_bar
|
317
643
|
end
|
318
644
|
end
|
319
645
|
|
320
|
-
it "
|
646
|
+
it "parses the element and attribute values" do
|
321
647
|
document = @klass.parse("<link foo='test1' bar='test2'>hello</link>")
|
322
|
-
document.link.
|
323
|
-
document.link_foo.
|
324
|
-
document.link_bar.
|
648
|
+
expect(document.link).to eq("hello")
|
649
|
+
expect(document.link_foo).to eq("test1")
|
650
|
+
expect(document.link_bar).to eq("test2")
|
325
651
|
end
|
326
652
|
end
|
327
|
-
|
328
653
|
end
|
329
654
|
end
|
330
655
|
|
331
656
|
describe "elements" do
|
332
657
|
describe "when parsing multiple elements" do
|
333
|
-
before
|
658
|
+
before do
|
334
659
|
@klass = Class.new do
|
335
660
|
include SAXMachine
|
336
|
-
elements :entry, :
|
661
|
+
elements :entry, as: :entries
|
337
662
|
end
|
338
663
|
end
|
339
664
|
|
340
|
-
it "
|
665
|
+
it "provides a collection accessor" do
|
341
666
|
document = @klass.new
|
342
667
|
document.entries << :foo
|
343
|
-
document.entries.
|
668
|
+
expect(document.entries).to eq([:foo])
|
344
669
|
end
|
345
670
|
|
346
|
-
it "
|
671
|
+
it "parses a single element" do
|
347
672
|
document = @klass.parse("<entry>hello</entry>")
|
348
|
-
document.entries.
|
673
|
+
expect(document.entries).to eq(["hello"])
|
349
674
|
end
|
350
675
|
|
351
|
-
it "
|
676
|
+
it "parses multiple elements" do
|
352
677
|
document = @klass.parse("<xml><entry>hello</entry><entry>world</entry></xml>")
|
353
|
-
document.entries.
|
678
|
+
expect(document.entries).to eq(["hello", "world"])
|
354
679
|
end
|
355
680
|
|
356
|
-
it "
|
681
|
+
it "parses multiple elements when taking an attribute value" do
|
357
682
|
attribute_klass = Class.new do
|
358
683
|
include SAXMachine
|
359
|
-
elements :entry, :
|
684
|
+
elements :entry, as: :entries, value: :foo
|
360
685
|
end
|
686
|
+
|
361
687
|
doc = attribute_klass.parse("<xml><entry foo='asdf' /><entry foo='jkl' /></xml>")
|
362
|
-
doc.entries.
|
688
|
+
expect(doc.entries).to eq(["asdf", "jkl"])
|
363
689
|
end
|
364
690
|
end
|
365
691
|
|
366
692
|
describe "when using the with and class options" do
|
367
|
-
before
|
693
|
+
before do
|
368
694
|
class Bar
|
369
695
|
include SAXMachine
|
370
696
|
element :title
|
@@ -377,93 +703,110 @@ describe "SAXMachine" do
|
|
377
703
|
|
378
704
|
class Item
|
379
705
|
include SAXMachine
|
380
|
-
|
381
706
|
end
|
707
|
+
|
382
708
|
@klass = Class.new do
|
383
709
|
include SAXMachine
|
384
|
-
elements :item, :
|
385
|
-
elements :item, :
|
710
|
+
elements :item, as: :items, with: { type: "Bar" }, class: Bar
|
711
|
+
elements :item, as: :items, with: { type: /Foo/ }, class: Foo
|
386
712
|
end
|
387
713
|
end
|
388
714
|
|
389
|
-
it "
|
715
|
+
it "casts into the correct class" do
|
390
716
|
document = @klass.parse("<items><item type=\"Bar\"><title>Bar title</title></item><item type=\"Foo\"><title>Foo title</title></item></items>")
|
391
|
-
document.items.size.
|
392
|
-
document.items.first.
|
393
|
-
document.items.first.title.
|
394
|
-
document.items.last.
|
395
|
-
document.items.last.title.
|
717
|
+
expect(document.items.size).to eq(2)
|
718
|
+
expect(document.items.first).to be_a(Bar)
|
719
|
+
expect(document.items.first.title).to eq("Bar title")
|
720
|
+
expect(document.items.last).to be_a(Foo)
|
721
|
+
expect(document.items.last.title).to eq("Foo title")
|
396
722
|
end
|
397
723
|
end
|
398
724
|
|
399
725
|
describe "when using the class option" do
|
400
|
-
before
|
726
|
+
before do
|
401
727
|
class Foo
|
402
728
|
include SAXMachine
|
403
729
|
element :title
|
404
730
|
end
|
731
|
+
|
405
732
|
@klass = Class.new do
|
406
733
|
include SAXMachine
|
407
|
-
elements :entry, :
|
734
|
+
elements :entry, as: :entries, class: Foo
|
408
735
|
end
|
409
736
|
end
|
410
737
|
|
411
|
-
it "
|
738
|
+
it "parses a single element with children" do
|
412
739
|
document = @klass.parse("<entry><title>a title</title></entry>")
|
413
|
-
document.entries.size.
|
414
|
-
document.entries.first.title.
|
740
|
+
expect(document.entries.size).to eq(1)
|
741
|
+
expect(document.entries.first.title).to eq("a title")
|
415
742
|
end
|
416
743
|
|
417
|
-
it "
|
744
|
+
it "parses multiple elements with children" do
|
418
745
|
document = @klass.parse("<xml><entry><title>title 1</title></entry><entry><title>title 2</title></entry></xml>")
|
419
|
-
document.entries.size.
|
420
|
-
document.entries.first.title.
|
421
|
-
document.entries.last.title.
|
746
|
+
expect(document.entries.size).to eq(2)
|
747
|
+
expect(document.entries.first.title).to eq("title 1")
|
748
|
+
expect(document.entries.last.title).to eq("title 2")
|
422
749
|
end
|
423
750
|
|
424
|
-
it "
|
751
|
+
it "does not parse a top level element that is specified only in a child" do
|
425
752
|
document = @klass.parse("<xml><title>no parse</title><entry><title>correct title</title></entry></xml>")
|
426
|
-
document.entries.size.
|
427
|
-
document.entries.first.title.
|
753
|
+
expect(document.entries.size).to eq(1)
|
754
|
+
expect(document.entries.first.title).to eq("correct title")
|
428
755
|
end
|
429
756
|
|
430
|
-
it "
|
757
|
+
it "parses elements, and make attributes and inner text available" do
|
431
758
|
class Related
|
432
759
|
include SAXMachine
|
433
|
-
element
|
434
|
-
element
|
760
|
+
element "related", as: :item
|
761
|
+
element "related", as: :attr, value: "attr"
|
435
762
|
end
|
763
|
+
|
436
764
|
class Foo
|
437
|
-
elements
|
765
|
+
elements "related", as: "items", class: Related
|
438
766
|
end
|
439
767
|
|
440
768
|
doc = Foo.parse(%{<xml><collection><related attr='baz'>something</related><related>somethingelse</related></collection></xml>})
|
441
|
-
doc.items.first.
|
442
|
-
doc.items.size.
|
443
|
-
doc.items.first.item.
|
444
|
-
doc.items.last.item.
|
769
|
+
expect(doc.items.first).not_to be_nil
|
770
|
+
expect(doc.items.size).to eq(2)
|
771
|
+
expect(doc.items.first.item).to eq("something")
|
772
|
+
expect(doc.items.last.item).to eq("somethingelse")
|
445
773
|
end
|
446
774
|
|
447
|
-
it "
|
775
|
+
it "parses out an attribute value from the tag that starts the collection" do
|
448
776
|
class Foo
|
449
|
-
element :entry, :
|
777
|
+
element :entry, value: :href, as: :url
|
450
778
|
end
|
779
|
+
|
451
780
|
document = @klass.parse("<xml><entry href='http://pauldix.net'><title>paul</title></entry></xml>")
|
452
|
-
document.entries.size.
|
453
|
-
document.entries.first.title.
|
454
|
-
document.entries.first.url.
|
781
|
+
expect(document.entries.size).to eq(1)
|
782
|
+
expect(document.entries.first.title).to eq("paul")
|
783
|
+
expect(document.entries.first.url).to eq("http://pauldix.net")
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
describe "when dealing with element names containing dashes" do
|
789
|
+
it "converts dashes to underscores" do
|
790
|
+
class Dashes
|
791
|
+
include SAXMachine
|
792
|
+
element :dashed_element
|
455
793
|
end
|
794
|
+
|
795
|
+
parsed = Dashes.parse("<dashed-element>Text</dashed-element>")
|
796
|
+
expect(parsed.dashed_element).to eq "Text"
|
456
797
|
end
|
457
798
|
end
|
458
799
|
|
459
800
|
describe "full example" do
|
460
|
-
before
|
461
|
-
@xml = File.read(
|
801
|
+
before do
|
802
|
+
@xml = File.read("spec/fixtures/atom.xml")
|
803
|
+
|
462
804
|
class AtomEntry
|
463
805
|
include SAXMachine
|
464
806
|
element :title
|
465
|
-
element :name, :
|
466
|
-
element "feedburner:origLink", :
|
807
|
+
element :name, as: :author
|
808
|
+
element "feedburner:origLink", as: :url
|
809
|
+
element :link, as: :alternate, value: :href, with: { type: "text/html", rel: "alternate" }
|
467
810
|
element :summary
|
468
811
|
element :content
|
469
812
|
element :published
|
@@ -472,15 +815,25 @@ describe "SAXMachine" do
|
|
472
815
|
class Atom
|
473
816
|
include SAXMachine
|
474
817
|
element :title
|
475
|
-
element :link, :
|
476
|
-
element :link, :
|
477
|
-
elements :entry, :
|
818
|
+
element :link, value: :href, as: :url, with: { type: "text/html" }
|
819
|
+
element :link, value: :href, as: :feed_url, with: { type: "application/atom+xml" }
|
820
|
+
elements :entry, as: :entries, class: AtomEntry
|
478
821
|
end
|
479
|
-
end # before
|
480
822
|
|
481
|
-
|
482
|
-
|
483
|
-
|
823
|
+
@feed = Atom.parse(@xml)
|
824
|
+
end
|
825
|
+
|
826
|
+
it "parses the url" do
|
827
|
+
expect(@feed.url).to eq("http://www.pauldix.net/")
|
828
|
+
end
|
829
|
+
|
830
|
+
it "parses entry url" do
|
831
|
+
expect(@feed.entries.first.url).to eq("http://www.pauldix.net/2008/09/marshal-data-to.html?param1=1¶m2=2")
|
832
|
+
expect(@feed.entries.first.alternate).to eq("http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/383536354/marshal-data-to.html?param1=1¶m2=2")
|
833
|
+
end
|
834
|
+
|
835
|
+
it "parses content" do
|
836
|
+
expect(@feed.entries.first.content.strip).to eq(File.read("spec/fixtures/atom-content.html").strip)
|
484
837
|
end
|
485
838
|
end
|
486
839
|
|
@@ -498,32 +851,35 @@ describe "SAXMachine" do
|
|
498
851
|
</category>
|
499
852
|
</categories>
|
500
853
|
]
|
501
|
-
|
502
|
-
end
|
854
|
+
|
855
|
+
class CategoryCollection; end
|
856
|
+
|
503
857
|
class Category
|
504
858
|
include SAXMachine
|
505
859
|
attr_accessor :id
|
506
|
-
element :category, :
|
860
|
+
element :category, value: :id, as: :id
|
507
861
|
element :title
|
508
|
-
element :categories, :
|
862
|
+
element :categories, as: :collection, class: CategoryCollection
|
509
863
|
ancestor :ancestor
|
510
864
|
end
|
865
|
+
|
511
866
|
class CategoryCollection
|
512
867
|
include SAXMachine
|
513
|
-
elements :category, :
|
868
|
+
elements :category, as: :categories, class: Category
|
514
869
|
end
|
870
|
+
|
515
871
|
@collection = CategoryCollection.parse(@xml)
|
516
872
|
end
|
517
873
|
|
518
|
-
it "
|
519
|
-
@collection.categories.first.id.
|
520
|
-
@collection.categories.first.title.
|
521
|
-
@collection.categories.first.ancestor.
|
874
|
+
it "parses the first category" do
|
875
|
+
expect(@collection.categories.first.id).to eq("1")
|
876
|
+
expect(@collection.categories.first.title).to eq("First")
|
877
|
+
expect(@collection.categories.first.ancestor).to eq(@collection)
|
522
878
|
end
|
523
879
|
|
524
|
-
it "
|
525
|
-
@collection.categories.first.collection.categories.first.id.
|
526
|
-
@collection.categories.first.collection.categories.first.title.
|
880
|
+
it "parses the nested category" do
|
881
|
+
expect(@collection.categories.first.collection.categories.first.id).to eq("2")
|
882
|
+
expect(@collection.categories.first.collection.categories.first.title).to eq("Second")
|
527
883
|
end
|
528
884
|
end
|
529
885
|
|
@@ -541,24 +897,26 @@ describe "SAXMachine" do
|
|
541
897
|
</category>
|
542
898
|
</categories>
|
543
899
|
]
|
900
|
+
|
544
901
|
class CategoryTree
|
545
902
|
include SAXMachine
|
546
903
|
attr_accessor :id
|
547
|
-
element :category, :
|
904
|
+
element :category, value: :id, as: :id
|
548
905
|
element :title
|
549
|
-
elements :category, :
|
906
|
+
elements :category, as: :categories, class: CategoryTree
|
550
907
|
end
|
908
|
+
|
551
909
|
@collection = CategoryTree.parse(@xml)
|
552
910
|
end
|
553
911
|
|
554
|
-
it "
|
555
|
-
@collection.categories.first.id.
|
556
|
-
@collection.categories.first.title.
|
912
|
+
it "parses the first category" do
|
913
|
+
expect(@collection.categories.first.id).to eq("1")
|
914
|
+
expect(@collection.categories.first.title).to eq("First")
|
557
915
|
end
|
558
916
|
|
559
|
-
it "
|
560
|
-
@collection.categories.first.categories.first.id.
|
561
|
-
@collection.categories.first.categories.first.title.
|
917
|
+
it "parses the nested category" do
|
918
|
+
expect(@collection.categories.first.categories.first.id).to eq("2")
|
919
|
+
expect(@collection.categories.first.categories.first.title).to eq("Second")
|
562
920
|
end
|
563
921
|
end
|
564
922
|
|
@@ -571,21 +929,23 @@ describe "SAXMachine" do
|
|
571
929
|
</texts>
|
572
930
|
</item>
|
573
931
|
]
|
932
|
+
|
574
933
|
@klass = Class.new do
|
575
934
|
include SAXMachine
|
576
935
|
attr_accessor :id
|
577
|
-
element :item, :
|
936
|
+
element :item, value: "id", as: :id
|
578
937
|
element :title
|
579
938
|
end
|
939
|
+
|
580
940
|
@item = @klass.parse(@xml)
|
581
941
|
end
|
582
942
|
|
583
|
-
it "
|
584
|
-
@item.id.
|
943
|
+
it "has an id" do
|
944
|
+
expect(@item.id).to eq("1")
|
585
945
|
end
|
586
946
|
|
587
|
-
it "
|
588
|
-
@item.title.
|
947
|
+
it "has a title" do
|
948
|
+
expect(@item.title).to eq("Hello")
|
589
949
|
end
|
590
950
|
end
|
591
951
|
|
@@ -596,28 +956,31 @@ describe "SAXMachine" do
|
|
596
956
|
<author name="John Doe" role="writer" />
|
597
957
|
</item>
|
598
958
|
]
|
959
|
+
|
599
960
|
class AuthorElement
|
600
961
|
include SAXMachine
|
601
962
|
attribute :name
|
602
963
|
attribute :role
|
603
964
|
end
|
965
|
+
|
604
966
|
class ItemElement
|
605
967
|
include SAXMachine
|
606
|
-
element :author, :
|
968
|
+
element :author, class: AuthorElement
|
607
969
|
end
|
970
|
+
|
608
971
|
@item = ItemElement.parse(@xml)
|
609
972
|
end
|
610
973
|
|
611
|
-
it
|
612
|
-
@item.author.
|
974
|
+
it "has the child element" do
|
975
|
+
expect(@item.author).not_to be_nil
|
613
976
|
end
|
614
977
|
|
615
|
-
it
|
616
|
-
@item.author.name.
|
978
|
+
it "has the author name" do
|
979
|
+
expect(@item.author.name).to eq("John Doe")
|
617
980
|
end
|
618
981
|
|
619
|
-
it
|
620
|
-
@item.author.role.
|
982
|
+
it "has the author role" do
|
983
|
+
expect(@item.author.role).to eq("writer")
|
621
984
|
end
|
622
985
|
end
|
623
986
|
|
@@ -629,31 +992,34 @@ describe "SAXMachine" do
|
|
629
992
|
<author name="Jane Doe" role="artist" />
|
630
993
|
</item>
|
631
994
|
]
|
995
|
+
|
632
996
|
class AuthorElement2
|
633
997
|
include SAXMachine
|
634
998
|
attribute :name
|
635
999
|
attribute :role
|
636
1000
|
end
|
1001
|
+
|
637
1002
|
class ItemElement2
|
638
1003
|
include SAXMachine
|
639
|
-
elements :author, :
|
1004
|
+
elements :author, as: :authors, class: AuthorElement2
|
640
1005
|
end
|
1006
|
+
|
641
1007
|
@item = ItemElement2.parse(@xml)
|
642
1008
|
end
|
643
1009
|
|
644
|
-
it
|
645
|
-
@item.authors.
|
646
|
-
@item.authors.count.
|
1010
|
+
it "has the child elements" do
|
1011
|
+
expect(@item.authors).not_to be_nil
|
1012
|
+
expect(@item.authors.count).to eq(2)
|
647
1013
|
end
|
648
1014
|
|
649
|
-
it
|
650
|
-
@item.authors.first.name.
|
651
|
-
@item.authors.last.name.
|
1015
|
+
it "has the author names" do
|
1016
|
+
expect(@item.authors.first.name).to eq("John Doe")
|
1017
|
+
expect(@item.authors.last.name).to eq("Jane Doe")
|
652
1018
|
end
|
653
1019
|
|
654
|
-
it
|
655
|
-
@item.authors.first.role.
|
656
|
-
@item.authors.last.role.
|
1020
|
+
it "has the author roles" do
|
1021
|
+
expect(@item.authors.first.role).to eq("writer")
|
1022
|
+
expect(@item.authors.last.role).to eq("artist")
|
657
1023
|
end
|
658
1024
|
end
|
659
1025
|
|
@@ -664,28 +1030,31 @@ describe "SAXMachine" do
|
|
664
1030
|
<author role="writer">John Doe</author>
|
665
1031
|
</item>
|
666
1032
|
]
|
1033
|
+
|
667
1034
|
class AuthorElement3
|
668
1035
|
include SAXMachine
|
669
1036
|
value :name
|
670
1037
|
attribute :role
|
671
1038
|
end
|
1039
|
+
|
672
1040
|
class ItemElement3
|
673
1041
|
include SAXMachine
|
674
|
-
element :author, :
|
1042
|
+
element :author, class: AuthorElement3
|
675
1043
|
end
|
1044
|
+
|
676
1045
|
@item = ItemElement3.parse(@xml)
|
677
1046
|
end
|
678
1047
|
|
679
|
-
it
|
680
|
-
@item.author.
|
1048
|
+
it "has the child elements" do
|
1049
|
+
expect(@item.author).not_to be_nil
|
681
1050
|
end
|
682
1051
|
|
683
|
-
it
|
684
|
-
@item.author.name.
|
1052
|
+
it "has the author names" do
|
1053
|
+
expect(@item.author.name).to eq("John Doe")
|
685
1054
|
end
|
686
1055
|
|
687
|
-
it
|
688
|
-
@item.author.role.
|
1056
|
+
it "has the author roles" do
|
1057
|
+
expect(@item.author.role).to eq("writer")
|
689
1058
|
end
|
690
1059
|
end
|
691
1060
|
|
@@ -698,41 +1067,142 @@ describe "SAXMachine" do
|
|
698
1067
|
<author role="artist">Jane Doe</author>
|
699
1068
|
</item>
|
700
1069
|
]
|
1070
|
+
|
701
1071
|
class AuthorElement4
|
702
1072
|
include SAXMachine
|
703
1073
|
value :name
|
704
1074
|
attribute :role
|
705
1075
|
end
|
1076
|
+
|
706
1077
|
class ItemElement4
|
707
1078
|
include SAXMachine
|
708
1079
|
element :title
|
709
|
-
elements :author, :
|
1080
|
+
elements :author, as: :authors, class: AuthorElement4
|
710
1081
|
|
711
1082
|
def title=(blah)
|
712
|
-
#raise 'woo'
|
713
1083
|
@title = blah
|
714
1084
|
end
|
715
1085
|
end
|
1086
|
+
|
716
1087
|
@item = ItemElement4.parse(@xml)
|
717
1088
|
end
|
718
1089
|
|
719
|
-
it
|
720
|
-
@item.title.
|
1090
|
+
it "has the title" do
|
1091
|
+
expect(@item.title).to eq("sweet")
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
it "has the child elements" do
|
1095
|
+
expect(@item.authors).not_to be_nil
|
1096
|
+
expect(@item.authors.count).to eq(2)
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
it "has the author names" do
|
1100
|
+
expect(@item.authors.first.name).to eq("John Doe")
|
1101
|
+
expect(@item.authors.last.name).to eq("Jane Doe")
|
721
1102
|
end
|
722
1103
|
|
723
|
-
it
|
724
|
-
@item.authors.
|
725
|
-
@item.authors.
|
1104
|
+
it "has the author roles" do
|
1105
|
+
expect(@item.authors.first.role).to eq("writer")
|
1106
|
+
expect(@item.authors.last.role).to eq("artist")
|
726
1107
|
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
describe "with multiple elements with the same alias" do
|
1111
|
+
let(:item) { ItemElement5.parse(xml) }
|
1112
|
+
|
1113
|
+
before do
|
1114
|
+
class ItemElement5
|
1115
|
+
include SAXMachine
|
1116
|
+
element :pubDate, as: :published
|
1117
|
+
element :"dc:date", as: :published
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
describe "only first defined" do
|
1122
|
+
let(:xml) { "<item xmlns:dc='http://www.example.com'><pubDate>first value</pubDate></item>" }
|
1123
|
+
|
1124
|
+
it "has first value" do
|
1125
|
+
expect(item.published).to eq("first value")
|
1126
|
+
end
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
describe "only last defined" do
|
1130
|
+
let(:xml) { "<item xmlns:dc='http://www.example.com'><dc:date>last value</dc:date></item>" }
|
1131
|
+
|
1132
|
+
it "has last value" do
|
1133
|
+
expect(item.published).to eq("last value")
|
1134
|
+
end
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
describe "both defined" do
|
1138
|
+
let(:xml) { "<item xmlns:dc='http://www.example.com'><pubDate>first value</pubDate><dc:date>last value</dc:date></item>" }
|
1139
|
+
|
1140
|
+
it "has last value" do
|
1141
|
+
expect(item.published).to eq("last value")
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
describe "both defined but order is reversed" do
|
1146
|
+
let(:xml) { "<item xmlns:dc='http://www.example.com'><dc:date>last value</dc:date><pubDate>first value</pubDate></item>" }
|
1147
|
+
|
1148
|
+
it "has first value" do
|
1149
|
+
expect(item.published).to eq("first value")
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
describe "both defined but last is empty" do
|
1154
|
+
let(:xml) { "<item xmlns:dc='http://www.example.com'><pubDate>first value</pubDate><dc:date></dc:date></item>" }
|
1155
|
+
|
1156
|
+
it "has first value" do
|
1157
|
+
expect(item.published).to eq("first value")
|
1158
|
+
end
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
describe "with error handling" do
|
1163
|
+
before do
|
1164
|
+
@xml = %[
|
1165
|
+
<item id="1">
|
1166
|
+
<title>sweet</title>
|
1167
|
+
]
|
1168
|
+
|
1169
|
+
class ItemElement5
|
1170
|
+
include SAXMachine
|
1171
|
+
element :title
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
@errors = []
|
1175
|
+
@warnings = []
|
1176
|
+
@item = ItemElement5.parse(
|
1177
|
+
@xml,
|
1178
|
+
->(x) { @errors << x },
|
1179
|
+
->(x) { @warnings << x },
|
1180
|
+
)
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
it "has error" do
|
1184
|
+
expect(@errors.uniq.size).to eq(1)
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
it "has no warning" do
|
1188
|
+
expect(@warnings.uniq.size).to eq(0)
|
1189
|
+
end
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
describe "with io as a input" do
|
1193
|
+
before do
|
1194
|
+
@io = StringIO.new('<item id="1"><title>sweet</title></item>')
|
1195
|
+
|
1196
|
+
class IoParser
|
1197
|
+
include SAXMachine
|
1198
|
+
element :title
|
1199
|
+
end
|
727
1200
|
|
728
|
-
|
729
|
-
@item.authors.first.name.should == 'John Doe'
|
730
|
-
@item.authors.last.name.should == 'Jane Doe'
|
1201
|
+
@item = ItemElement5.parse(@io)
|
731
1202
|
end
|
732
1203
|
|
733
|
-
it
|
734
|
-
@item.
|
735
|
-
@item.authors.last.role.should == 'artist'
|
1204
|
+
it "parses" do
|
1205
|
+
expect(@item.title).to eq("sweet")
|
736
1206
|
end
|
737
1207
|
end
|
738
1208
|
end
|