sax-machine 0.0.16 → 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 +1 -1
- 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 -18
- data/lib/sax-machine/config/sax_ancestor.rb +17 -0
- data/lib/sax-machine/config/sax_attribute.rb +18 -0
- data/lib/sax-machine/config/sax_collection.rb +33 -0
- data/lib/sax-machine/config/sax_element.rb +65 -0
- data/lib/sax-machine/config/sax_element_value.rb +23 -0
- 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 +49 -18
- data/lib/sax-machine/sax_configure.rb +33 -0
- data/lib/sax-machine/sax_document.rb +105 -50
- data/lib/sax-machine/version.rb +3 -0
- data/lib/sax-machine.rb +27 -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 +862 -181
- data/spec/sax-machine/sax_include_spec.rb +49 -0
- data/spec/spec_helper.rb +18 -8
- metadata +73 -69
- data/Gemfile.lock +0 -20
- data/README.textile +0 -87
- data/lib/sax-machine/sax_collection_config.rb +0 -45
- data/lib/sax-machine/sax_element_config.rb +0 -65
- data/lib/sax-machine/sax_handler.rb +0 -67
|
@@ -1,98 +1,410 @@
|
|
|
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 "
|
|
16
|
+
it "provides mass assignment through initialize method" do
|
|
17
|
+
document = @klass.new(title: "Title")
|
|
18
|
+
expect(document.title).to eq("Title")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "provides an accessor" do
|
|
14
22
|
document = @klass.new
|
|
15
23
|
document.title = "Title"
|
|
16
|
-
document.title.
|
|
24
|
+
expect(document.title).to eq("Title")
|
|
17
25
|
end
|
|
18
26
|
|
|
19
|
-
it "
|
|
20
|
-
@klass.
|
|
27
|
+
it "does not overwrites the getter is there is already one present" do
|
|
28
|
+
@klass = Class.new do
|
|
29
|
+
def title
|
|
30
|
+
"#{@title} ***"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
include SAXMachine
|
|
34
|
+
element :title
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
document = @klass.new
|
|
38
|
+
document.title = "Title"
|
|
39
|
+
expect(document.title).to eq("Title ***")
|
|
21
40
|
end
|
|
22
41
|
|
|
23
|
-
it "
|
|
42
|
+
it "does not overwrites the setter if there is already one present" do
|
|
24
43
|
@klass = Class.new do
|
|
25
44
|
def title=(val)
|
|
26
45
|
@title = "#{val} **"
|
|
27
46
|
end
|
|
47
|
+
|
|
28
48
|
include SAXMachine
|
|
29
49
|
element :title
|
|
30
50
|
end
|
|
51
|
+
|
|
31
52
|
document = @klass.new
|
|
32
53
|
document.title = "Title"
|
|
33
|
-
document.title.
|
|
34
|
-
end
|
|
35
|
-
describe "the class attribute" do
|
|
36
|
-
before(:each) do
|
|
37
|
-
@klass = Class.new do
|
|
38
|
-
include SAXMachine
|
|
39
|
-
element :date, :class => DateTime
|
|
40
|
-
end
|
|
41
|
-
@document = @klass.new
|
|
42
|
-
@document.date = DateTime.now.to_s
|
|
43
|
-
end
|
|
44
|
-
it "should be available" do
|
|
45
|
-
@klass.data_class(:date).should == DateTime
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
describe "the required attribute" do
|
|
49
|
-
it "should be available" do
|
|
50
|
-
@klass = Class.new do
|
|
51
|
-
include SAXMachine
|
|
52
|
-
element :date, :required => true
|
|
53
|
-
end
|
|
54
|
-
@klass.required?(:date).should be_true
|
|
55
|
-
end
|
|
54
|
+
expect(document.title).to eq("Title **")
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
it "
|
|
57
|
+
it "does not overwrites the accessor when the element is not present" do
|
|
59
58
|
document = @klass.new
|
|
60
59
|
document.title = "Title"
|
|
61
60
|
document.parse("<foo></foo>")
|
|
62
|
-
document.title.
|
|
61
|
+
expect(document.title).to eq("Title")
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
it "
|
|
64
|
+
it "overwrites the value when the element is present" do
|
|
66
65
|
document = @klass.new
|
|
67
66
|
document.title = "Old title"
|
|
68
67
|
document.parse("<title>New title</title>")
|
|
69
|
-
document.title.
|
|
68
|
+
expect(document.title).to eq("New title")
|
|
70
69
|
end
|
|
71
70
|
|
|
72
|
-
it "
|
|
71
|
+
it "saves the element text into an accessor" do
|
|
73
72
|
document = @klass.parse("<title>My Title</title>")
|
|
74
|
-
document.title.
|
|
73
|
+
expect(document.title).to eq("My Title")
|
|
75
74
|
end
|
|
76
75
|
|
|
77
|
-
it "
|
|
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)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "saves cdata into an accessor" do
|
|
78
85
|
document = @klass.parse("<title><![CDATA[A Title]]></title>")
|
|
79
|
-
document.title.
|
|
86
|
+
expect(document.title).to eq("A Title")
|
|
80
87
|
end
|
|
81
88
|
|
|
82
|
-
it "
|
|
89
|
+
it "saves the element text into an accessor when there are multiple elements" do
|
|
83
90
|
document = @klass.parse("<xml><title>My Title</title><foo>bar</foo></xml>")
|
|
84
|
-
document.title.
|
|
91
|
+
expect(document.title).to eq("My Title")
|
|
85
92
|
end
|
|
86
93
|
|
|
87
|
-
it "
|
|
94
|
+
it "saves the first element text when there are multiple of the same element" do
|
|
88
95
|
document = @klass.parse("<xml><title>My Title</title><title>bar</title></xml>")
|
|
89
|
-
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
|
|
90
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
|
|
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
|
|
91
403
|
end
|
|
92
404
|
end
|
|
93
405
|
|
|
94
406
|
describe "when parsing multiple elements" do
|
|
95
|
-
before
|
|
407
|
+
before do
|
|
96
408
|
@klass = Class.new do
|
|
97
409
|
include SAXMachine
|
|
98
410
|
element :title
|
|
@@ -100,226 +412,285 @@ describe "SAXMachine" do
|
|
|
100
412
|
end
|
|
101
413
|
end
|
|
102
414
|
|
|
103
|
-
it "
|
|
415
|
+
it "saves the element text for a second tag" do
|
|
104
416
|
document = @klass.parse("<xml><title>My Title</title><name>Paul</name></xml>")
|
|
105
|
-
document.name.
|
|
106
|
-
document.title.
|
|
417
|
+
expect(document.name).to eq("Paul")
|
|
418
|
+
expect(document.title).to eq("My Title")
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
it "does not overwrites the getter is there is already one present" do
|
|
422
|
+
@klass = Class.new do
|
|
423
|
+
def items
|
|
424
|
+
[]
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
include SAXMachine
|
|
428
|
+
elements :items
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
document = @klass.new
|
|
432
|
+
document.items = [1, 2, 3, 4]
|
|
433
|
+
expect(document.items).to eq([])
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "does not overwrites the setter if there is already one present" do
|
|
437
|
+
@klass = Class.new do
|
|
438
|
+
def items=(val)
|
|
439
|
+
@items = [1, *val]
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
include SAXMachine
|
|
443
|
+
elements :items
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
document = @klass.new
|
|
447
|
+
document.items = [2, 3]
|
|
448
|
+
expect(document.items).to eq([1, 2, 3])
|
|
107
449
|
end
|
|
108
450
|
end
|
|
109
451
|
|
|
110
452
|
describe "when using options for parsing elements" do
|
|
111
453
|
describe "using the 'as' option" do
|
|
112
|
-
before
|
|
454
|
+
before do
|
|
113
455
|
@klass = Class.new do
|
|
114
456
|
include SAXMachine
|
|
115
|
-
element :description, :
|
|
457
|
+
element :description, as: :summary
|
|
116
458
|
end
|
|
117
459
|
end
|
|
118
460
|
|
|
119
|
-
it "
|
|
461
|
+
it "provides an accessor using the 'as' name" do
|
|
120
462
|
document = @klass.new
|
|
121
463
|
document.summary = "a small summary"
|
|
122
|
-
document.summary.
|
|
464
|
+
expect(document.summary).to eq("a small summary")
|
|
123
465
|
end
|
|
124
466
|
|
|
125
|
-
it "
|
|
467
|
+
it "saves the element text into the 'as' accessor" do
|
|
126
468
|
document = @klass.parse("<description>here is a description</description>")
|
|
127
|
-
document.summary.
|
|
469
|
+
expect(document.summary).to eq("here is a description")
|
|
128
470
|
end
|
|
129
471
|
end
|
|
130
472
|
|
|
131
473
|
describe "using the :with option" do
|
|
132
474
|
describe "and the :value option" do
|
|
133
|
-
before
|
|
475
|
+
before do
|
|
134
476
|
@klass = Class.new do
|
|
135
477
|
include SAXMachine
|
|
136
|
-
element :link, :
|
|
478
|
+
element :link, value: :href, with: { foo: "bar" }
|
|
137
479
|
end
|
|
138
480
|
end
|
|
139
481
|
|
|
140
|
-
it "
|
|
482
|
+
it "saves the value of a matching element" do
|
|
141
483
|
document = @klass.parse("<link href='test' foo='bar'>asdf</link>")
|
|
142
|
-
document.link.
|
|
484
|
+
expect(document.link).to eq("test")
|
|
143
485
|
end
|
|
144
486
|
|
|
145
|
-
it "
|
|
487
|
+
it "saves the value of the first matching element" do
|
|
146
488
|
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' foo='bar' /></xml>")
|
|
147
|
-
document.link.
|
|
489
|
+
expect(document.link).to eq("first")
|
|
148
490
|
end
|
|
149
491
|
|
|
150
492
|
describe "and the :as option" do
|
|
151
|
-
before
|
|
493
|
+
before do
|
|
152
494
|
@klass = Class.new do
|
|
153
495
|
include SAXMachine
|
|
154
|
-
element :link, :
|
|
155
|
-
element :link, :
|
|
496
|
+
element :link, value: :href, as: :url, with: { foo: "bar" }
|
|
497
|
+
element :link, value: :href, as: :second_url, with: { asdf: "jkl" }
|
|
156
498
|
end
|
|
157
499
|
end
|
|
158
500
|
|
|
159
|
-
it "
|
|
501
|
+
it "saves the value of the first matching element" do
|
|
160
502
|
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' asdf='jkl' /><link href='second' foo='bar' /></xml>")
|
|
161
|
-
document.url.
|
|
162
|
-
document.second_url.
|
|
503
|
+
expect(document.url).to eq("first")
|
|
504
|
+
expect(document.second_url).to eq("second")
|
|
163
505
|
end
|
|
164
|
-
|
|
165
506
|
end
|
|
166
507
|
end
|
|
167
508
|
|
|
168
509
|
describe "with only one element" do
|
|
169
|
-
before
|
|
510
|
+
before do
|
|
170
511
|
@klass = Class.new do
|
|
171
512
|
include SAXMachine
|
|
172
|
-
element :link, :
|
|
513
|
+
element :link, with: { foo: "bar" }
|
|
173
514
|
end
|
|
174
515
|
end
|
|
175
516
|
|
|
176
|
-
it "
|
|
517
|
+
it "saves the text of an element that has matching attributes" do
|
|
177
518
|
document = @klass.parse("<link foo=\"bar\">match</link>")
|
|
178
|
-
document.link.
|
|
519
|
+
expect(document.link).to eq("match")
|
|
179
520
|
end
|
|
180
521
|
|
|
181
|
-
it "
|
|
522
|
+
it "does not saves the text of an element that doesn't have matching attributes" do
|
|
182
523
|
document = @klass.parse("<link>no match</link>")
|
|
183
|
-
document.link.
|
|
524
|
+
expect(document.link).to be_nil
|
|
184
525
|
end
|
|
185
526
|
|
|
186
|
-
it "
|
|
527
|
+
it "saves the text of an element that has matching attributes when it is the second of that type" do
|
|
187
528
|
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">match</link></xml>")
|
|
188
|
-
document.link.
|
|
189
|
-
|
|
529
|
+
expect(document.link).to eq("match")
|
|
190
530
|
end
|
|
191
531
|
|
|
192
|
-
it "
|
|
532
|
+
it "saves the text of an element that has matching attributes plus a few more" do
|
|
193
533
|
document = @klass.parse("<xml><link>no match</link><link asdf='jkl' foo='bar'>match</link>")
|
|
194
|
-
document.link.
|
|
534
|
+
expect(document.link).to eq("match")
|
|
195
535
|
end
|
|
196
536
|
end
|
|
197
537
|
|
|
198
538
|
describe "with multiple elements of same tag" do
|
|
199
|
-
before
|
|
539
|
+
before do
|
|
200
540
|
@klass = Class.new do
|
|
201
541
|
include SAXMachine
|
|
202
|
-
element :link, :
|
|
203
|
-
element :link, :
|
|
542
|
+
element :link, as: :first, with: { foo: "bar" }
|
|
543
|
+
element :link, as: :second, with: { asdf: "jkl" }
|
|
204
544
|
end
|
|
205
545
|
end
|
|
206
546
|
|
|
207
|
-
it "
|
|
547
|
+
it "matches the first element" do
|
|
208
548
|
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">first match</link><link>no match</link></xml>")
|
|
209
|
-
document.first.
|
|
549
|
+
expect(document.first).to eq("first match")
|
|
210
550
|
end
|
|
211
551
|
|
|
212
|
-
it "
|
|
552
|
+
it "matches the second element" do
|
|
213
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>")
|
|
214
|
-
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")
|
|
215
584
|
end
|
|
216
585
|
end
|
|
217
|
-
end
|
|
586
|
+
end
|
|
218
587
|
|
|
219
588
|
describe "using the 'value' option" do
|
|
220
|
-
before
|
|
589
|
+
before do
|
|
221
590
|
@klass = Class.new do
|
|
222
591
|
include SAXMachine
|
|
223
|
-
element :link, :
|
|
592
|
+
element :link, value: :foo
|
|
224
593
|
end
|
|
225
594
|
end
|
|
226
595
|
|
|
227
|
-
it "
|
|
596
|
+
it "saves the attribute value" do
|
|
228
597
|
document = @klass.parse("<link foo='test'>hello</link>")
|
|
229
|
-
document.link.
|
|
598
|
+
expect(document.link).to eq("test")
|
|
230
599
|
end
|
|
231
600
|
|
|
232
|
-
it "
|
|
601
|
+
it "saves the attribute value when there is no text enclosed by the tag" do
|
|
233
602
|
document = @klass.parse("<link foo='test'></link>")
|
|
234
|
-
document.link.
|
|
603
|
+
expect(document.link).to eq("test")
|
|
235
604
|
end
|
|
236
605
|
|
|
237
|
-
it "
|
|
606
|
+
it "saves the attribute value when the tag close is in the open" do
|
|
238
607
|
document = @klass.parse("<link foo='test'/>")
|
|
239
|
-
document.link.
|
|
608
|
+
expect(document.link).to eq("test")
|
|
240
609
|
end
|
|
241
610
|
|
|
242
|
-
it "
|
|
611
|
+
it "saves two different attribute values on a single tag" do
|
|
243
612
|
@klass = Class.new do
|
|
244
613
|
include SAXMachine
|
|
245
|
-
element :link, :
|
|
246
|
-
element :link, :
|
|
614
|
+
element :link, value: :foo, as: :first
|
|
615
|
+
element :link, value: :bar, as: :second
|
|
247
616
|
end
|
|
617
|
+
|
|
248
618
|
document = @klass.parse("<link foo='foo value' bar='bar value'></link>")
|
|
249
|
-
document.first.
|
|
250
|
-
document.second.
|
|
619
|
+
expect(document.first).to eq("foo value")
|
|
620
|
+
expect(document.second).to eq("bar value")
|
|
251
621
|
end
|
|
252
622
|
|
|
253
|
-
it "
|
|
623
|
+
it "does not fail if one of the attribute hasn't been defined" do
|
|
254
624
|
@klass = Class.new do
|
|
255
625
|
include SAXMachine
|
|
256
|
-
element :link, :
|
|
257
|
-
element :link, :
|
|
626
|
+
element :link, value: :foo, as: :first
|
|
627
|
+
element :link, value: :bar, as: :second
|
|
258
628
|
end
|
|
629
|
+
|
|
259
630
|
document = @klass.parse("<link foo='foo value'></link>")
|
|
260
|
-
document.first.
|
|
261
|
-
document.second.
|
|
631
|
+
expect(document.first).to eq("foo value")
|
|
632
|
+
expect(document.second).to be_nil
|
|
262
633
|
end
|
|
263
634
|
end
|
|
264
635
|
|
|
265
636
|
describe "when desiring both the content and attributes of an element" do
|
|
266
|
-
before
|
|
637
|
+
before do
|
|
267
638
|
@klass = Class.new do
|
|
268
639
|
include SAXMachine
|
|
269
640
|
element :link
|
|
270
|
-
element :link, :
|
|
271
|
-
element :link, :
|
|
641
|
+
element :link, value: :foo, as: :link_foo
|
|
642
|
+
element :link, value: :bar, as: :link_bar
|
|
272
643
|
end
|
|
273
644
|
end
|
|
274
645
|
|
|
275
|
-
it "
|
|
646
|
+
it "parses the element and attribute values" do
|
|
276
647
|
document = @klass.parse("<link foo='test1' bar='test2'>hello</link>")
|
|
277
|
-
document.link.
|
|
278
|
-
document.link_foo.
|
|
279
|
-
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")
|
|
280
651
|
end
|
|
281
652
|
end
|
|
282
|
-
|
|
283
653
|
end
|
|
284
654
|
end
|
|
285
655
|
|
|
286
656
|
describe "elements" do
|
|
287
657
|
describe "when parsing multiple elements" do
|
|
288
|
-
before
|
|
658
|
+
before do
|
|
289
659
|
@klass = Class.new do
|
|
290
660
|
include SAXMachine
|
|
291
|
-
elements :entry, :
|
|
661
|
+
elements :entry, as: :entries
|
|
292
662
|
end
|
|
293
663
|
end
|
|
294
664
|
|
|
295
|
-
it "
|
|
665
|
+
it "provides a collection accessor" do
|
|
296
666
|
document = @klass.new
|
|
297
667
|
document.entries << :foo
|
|
298
|
-
document.entries.
|
|
668
|
+
expect(document.entries).to eq([:foo])
|
|
299
669
|
end
|
|
300
670
|
|
|
301
|
-
it "
|
|
671
|
+
it "parses a single element" do
|
|
302
672
|
document = @klass.parse("<entry>hello</entry>")
|
|
303
|
-
document.entries.
|
|
673
|
+
expect(document.entries).to eq(["hello"])
|
|
304
674
|
end
|
|
305
675
|
|
|
306
|
-
it "
|
|
676
|
+
it "parses multiple elements" do
|
|
307
677
|
document = @klass.parse("<xml><entry>hello</entry><entry>world</entry></xml>")
|
|
308
|
-
document.entries.
|
|
678
|
+
expect(document.entries).to eq(["hello", "world"])
|
|
309
679
|
end
|
|
310
680
|
|
|
311
|
-
it "
|
|
681
|
+
it "parses multiple elements when taking an attribute value" do
|
|
312
682
|
attribute_klass = Class.new do
|
|
313
683
|
include SAXMachine
|
|
314
|
-
elements :entry, :
|
|
684
|
+
elements :entry, as: :entries, value: :foo
|
|
315
685
|
end
|
|
686
|
+
|
|
316
687
|
doc = attribute_klass.parse("<xml><entry foo='asdf' /><entry foo='jkl' /></xml>")
|
|
317
|
-
doc.entries.
|
|
688
|
+
expect(doc.entries).to eq(["asdf", "jkl"])
|
|
318
689
|
end
|
|
319
690
|
end
|
|
320
691
|
|
|
321
692
|
describe "when using the with and class options" do
|
|
322
|
-
before
|
|
693
|
+
before do
|
|
323
694
|
class Bar
|
|
324
695
|
include SAXMachine
|
|
325
696
|
element :title
|
|
@@ -332,77 +703,110 @@ describe "SAXMachine" do
|
|
|
332
703
|
|
|
333
704
|
class Item
|
|
334
705
|
include SAXMachine
|
|
335
|
-
|
|
336
706
|
end
|
|
707
|
+
|
|
337
708
|
@klass = Class.new do
|
|
338
709
|
include SAXMachine
|
|
339
|
-
elements :item, :
|
|
340
|
-
elements :item, :
|
|
710
|
+
elements :item, as: :items, with: { type: "Bar" }, class: Bar
|
|
711
|
+
elements :item, as: :items, with: { type: /Foo/ }, class: Foo
|
|
341
712
|
end
|
|
342
713
|
end
|
|
343
714
|
|
|
344
|
-
it "
|
|
715
|
+
it "casts into the correct class" do
|
|
345
716
|
document = @klass.parse("<items><item type=\"Bar\"><title>Bar title</title></item><item type=\"Foo\"><title>Foo title</title></item></items>")
|
|
346
|
-
document.items.size.
|
|
347
|
-
document.items.first.
|
|
348
|
-
document.items.first.title.
|
|
349
|
-
document.items.last.
|
|
350
|
-
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")
|
|
351
722
|
end
|
|
352
723
|
end
|
|
353
724
|
|
|
354
725
|
describe "when using the class option" do
|
|
355
|
-
before
|
|
726
|
+
before do
|
|
356
727
|
class Foo
|
|
357
728
|
include SAXMachine
|
|
358
729
|
element :title
|
|
359
730
|
end
|
|
731
|
+
|
|
360
732
|
@klass = Class.new do
|
|
361
733
|
include SAXMachine
|
|
362
|
-
elements :entry, :
|
|
734
|
+
elements :entry, as: :entries, class: Foo
|
|
363
735
|
end
|
|
364
736
|
end
|
|
365
737
|
|
|
366
|
-
it "
|
|
738
|
+
it "parses a single element with children" do
|
|
367
739
|
document = @klass.parse("<entry><title>a title</title></entry>")
|
|
368
|
-
document.entries.size.
|
|
369
|
-
document.entries.first.title.
|
|
740
|
+
expect(document.entries.size).to eq(1)
|
|
741
|
+
expect(document.entries.first.title).to eq("a title")
|
|
370
742
|
end
|
|
371
743
|
|
|
372
|
-
it "
|
|
744
|
+
it "parses multiple elements with children" do
|
|
373
745
|
document = @klass.parse("<xml><entry><title>title 1</title></entry><entry><title>title 2</title></entry></xml>")
|
|
374
|
-
document.entries.size.
|
|
375
|
-
document.entries.first.title.
|
|
376
|
-
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")
|
|
377
749
|
end
|
|
378
750
|
|
|
379
|
-
it "
|
|
751
|
+
it "does not parse a top level element that is specified only in a child" do
|
|
380
752
|
document = @klass.parse("<xml><title>no parse</title><entry><title>correct title</title></entry></xml>")
|
|
381
|
-
document.entries.size.
|
|
382
|
-
document.entries.first.title.
|
|
753
|
+
expect(document.entries.size).to eq(1)
|
|
754
|
+
expect(document.entries.first.title).to eq("correct title")
|
|
383
755
|
end
|
|
384
756
|
|
|
385
|
-
it "
|
|
757
|
+
it "parses elements, and make attributes and inner text available" do
|
|
758
|
+
class Related
|
|
759
|
+
include SAXMachine
|
|
760
|
+
element "related", as: :item
|
|
761
|
+
element "related", as: :attr, value: "attr"
|
|
762
|
+
end
|
|
763
|
+
|
|
386
764
|
class Foo
|
|
387
|
-
|
|
765
|
+
elements "related", as: "items", class: Related
|
|
388
766
|
end
|
|
767
|
+
|
|
768
|
+
doc = Foo.parse(%{<xml><collection><related attr='baz'>something</related><related>somethingelse</related></collection></xml>})
|
|
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")
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
it "parses out an attribute value from the tag that starts the collection" do
|
|
776
|
+
class Foo
|
|
777
|
+
element :entry, value: :href, as: :url
|
|
778
|
+
end
|
|
779
|
+
|
|
389
780
|
document = @klass.parse("<xml><entry href='http://pauldix.net'><title>paul</title></entry></xml>")
|
|
390
|
-
document.entries.size.
|
|
391
|
-
document.entries.first.title.
|
|
392
|
-
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")
|
|
393
784
|
end
|
|
394
785
|
end
|
|
786
|
+
end
|
|
395
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
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
parsed = Dashes.parse("<dashed-element>Text</dashed-element>")
|
|
796
|
+
expect(parsed.dashed_element).to eq "Text"
|
|
797
|
+
end
|
|
396
798
|
end
|
|
397
799
|
|
|
398
800
|
describe "full example" do
|
|
399
|
-
before
|
|
400
|
-
@xml = File.read(
|
|
801
|
+
before do
|
|
802
|
+
@xml = File.read("spec/fixtures/atom.xml")
|
|
803
|
+
|
|
401
804
|
class AtomEntry
|
|
402
805
|
include SAXMachine
|
|
403
806
|
element :title
|
|
404
|
-
element :name, :
|
|
405
|
-
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" }
|
|
406
810
|
element :summary
|
|
407
811
|
element :content
|
|
408
812
|
element :published
|
|
@@ -411,15 +815,25 @@ describe "SAXMachine" do
|
|
|
411
815
|
class Atom
|
|
412
816
|
include SAXMachine
|
|
413
817
|
element :title
|
|
414
|
-
element :link, :
|
|
415
|
-
element :link, :
|
|
416
|
-
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
|
|
417
821
|
end
|
|
418
|
-
end # before
|
|
419
822
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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)
|
|
423
837
|
end
|
|
424
838
|
end
|
|
425
839
|
|
|
@@ -437,29 +851,35 @@ describe "SAXMachine" do
|
|
|
437
851
|
</category>
|
|
438
852
|
</categories>
|
|
439
853
|
]
|
|
854
|
+
|
|
440
855
|
class CategoryCollection; end
|
|
856
|
+
|
|
441
857
|
class Category
|
|
442
858
|
include SAXMachine
|
|
443
859
|
attr_accessor :id
|
|
444
|
-
element :category, :
|
|
860
|
+
element :category, value: :id, as: :id
|
|
445
861
|
element :title
|
|
446
|
-
element :categories, :
|
|
862
|
+
element :categories, as: :collection, class: CategoryCollection
|
|
863
|
+
ancestor :ancestor
|
|
447
864
|
end
|
|
865
|
+
|
|
448
866
|
class CategoryCollection
|
|
449
867
|
include SAXMachine
|
|
450
|
-
elements :category, :
|
|
868
|
+
elements :category, as: :categories, class: Category
|
|
451
869
|
end
|
|
870
|
+
|
|
452
871
|
@collection = CategoryCollection.parse(@xml)
|
|
453
872
|
end
|
|
454
873
|
|
|
455
|
-
it "
|
|
456
|
-
@collection.categories.first.id.
|
|
457
|
-
@collection.categories.first.title.
|
|
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)
|
|
458
878
|
end
|
|
459
879
|
|
|
460
|
-
it "
|
|
461
|
-
@collection.categories.first.collection.categories.first.id.
|
|
462
|
-
@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")
|
|
463
883
|
end
|
|
464
884
|
end
|
|
465
885
|
|
|
@@ -477,24 +897,26 @@ describe "SAXMachine" do
|
|
|
477
897
|
</category>
|
|
478
898
|
</categories>
|
|
479
899
|
]
|
|
900
|
+
|
|
480
901
|
class CategoryTree
|
|
481
902
|
include SAXMachine
|
|
482
903
|
attr_accessor :id
|
|
483
|
-
element :category, :
|
|
904
|
+
element :category, value: :id, as: :id
|
|
484
905
|
element :title
|
|
485
|
-
elements :category, :
|
|
906
|
+
elements :category, as: :categories, class: CategoryTree
|
|
486
907
|
end
|
|
908
|
+
|
|
487
909
|
@collection = CategoryTree.parse(@xml)
|
|
488
910
|
end
|
|
489
911
|
|
|
490
|
-
it "
|
|
491
|
-
@collection.categories.first.id.
|
|
492
|
-
@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")
|
|
493
915
|
end
|
|
494
916
|
|
|
495
|
-
it "
|
|
496
|
-
@collection.categories.first.categories.first.id.
|
|
497
|
-
@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")
|
|
498
920
|
end
|
|
499
921
|
end
|
|
500
922
|
|
|
@@ -507,21 +929,280 @@ describe "SAXMachine" do
|
|
|
507
929
|
</texts>
|
|
508
930
|
</item>
|
|
509
931
|
]
|
|
932
|
+
|
|
510
933
|
@klass = Class.new do
|
|
511
934
|
include SAXMachine
|
|
512
935
|
attr_accessor :id
|
|
513
|
-
element :item, :
|
|
936
|
+
element :item, value: "id", as: :id
|
|
514
937
|
element :title
|
|
515
938
|
end
|
|
939
|
+
|
|
516
940
|
@item = @klass.parse(@xml)
|
|
517
941
|
end
|
|
518
942
|
|
|
519
|
-
it "
|
|
520
|
-
@item.id.
|
|
943
|
+
it "has an id" do
|
|
944
|
+
expect(@item.id).to eq("1")
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
it "has a title" do
|
|
948
|
+
expect(@item.title).to eq("Hello")
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
describe "with config to pull multiple attributes" do
|
|
953
|
+
before do
|
|
954
|
+
@xml = %[
|
|
955
|
+
<item id="1">
|
|
956
|
+
<author name="John Doe" role="writer" />
|
|
957
|
+
</item>
|
|
958
|
+
]
|
|
959
|
+
|
|
960
|
+
class AuthorElement
|
|
961
|
+
include SAXMachine
|
|
962
|
+
attribute :name
|
|
963
|
+
attribute :role
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
class ItemElement
|
|
967
|
+
include SAXMachine
|
|
968
|
+
element :author, class: AuthorElement
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
@item = ItemElement.parse(@xml)
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
it "has the child element" do
|
|
975
|
+
expect(@item.author).not_to be_nil
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
it "has the author name" do
|
|
979
|
+
expect(@item.author.name).to eq("John Doe")
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
it "has the author role" do
|
|
983
|
+
expect(@item.author.role).to eq("writer")
|
|
984
|
+
end
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
describe "with multiple elements and multiple attributes" do
|
|
988
|
+
before do
|
|
989
|
+
@xml = %[
|
|
990
|
+
<item id="1">
|
|
991
|
+
<author name="John Doe" role="writer" />
|
|
992
|
+
<author name="Jane Doe" role="artist" />
|
|
993
|
+
</item>
|
|
994
|
+
]
|
|
995
|
+
|
|
996
|
+
class AuthorElement2
|
|
997
|
+
include SAXMachine
|
|
998
|
+
attribute :name
|
|
999
|
+
attribute :role
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
class ItemElement2
|
|
1003
|
+
include SAXMachine
|
|
1004
|
+
elements :author, as: :authors, class: AuthorElement2
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
@item = ItemElement2.parse(@xml)
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
it "has the child elements" do
|
|
1011
|
+
expect(@item.authors).not_to be_nil
|
|
1012
|
+
expect(@item.authors.count).to eq(2)
|
|
1013
|
+
end
|
|
1014
|
+
|
|
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")
|
|
1018
|
+
end
|
|
1019
|
+
|
|
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")
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
describe "with mixed attributes and element values" do
|
|
1027
|
+
before do
|
|
1028
|
+
@xml = %[
|
|
1029
|
+
<item id="1">
|
|
1030
|
+
<author role="writer">John Doe</author>
|
|
1031
|
+
</item>
|
|
1032
|
+
]
|
|
1033
|
+
|
|
1034
|
+
class AuthorElement3
|
|
1035
|
+
include SAXMachine
|
|
1036
|
+
value :name
|
|
1037
|
+
attribute :role
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
class ItemElement3
|
|
1041
|
+
include SAXMachine
|
|
1042
|
+
element :author, class: AuthorElement3
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
@item = ItemElement3.parse(@xml)
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
it "has the child elements" do
|
|
1049
|
+
expect(@item.author).not_to be_nil
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
it "has the author names" do
|
|
1053
|
+
expect(@item.author.name).to eq("John Doe")
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
it "has the author roles" do
|
|
1057
|
+
expect(@item.author.role).to eq("writer")
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
describe "with multiple mixed attributes and element values" do
|
|
1062
|
+
before do
|
|
1063
|
+
@xml = %[
|
|
1064
|
+
<item id="1">
|
|
1065
|
+
<title>sweet</title>
|
|
1066
|
+
<author role="writer">John Doe</author>
|
|
1067
|
+
<author role="artist">Jane Doe</author>
|
|
1068
|
+
</item>
|
|
1069
|
+
]
|
|
1070
|
+
|
|
1071
|
+
class AuthorElement4
|
|
1072
|
+
include SAXMachine
|
|
1073
|
+
value :name
|
|
1074
|
+
attribute :role
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
class ItemElement4
|
|
1078
|
+
include SAXMachine
|
|
1079
|
+
element :title
|
|
1080
|
+
elements :author, as: :authors, class: AuthorElement4
|
|
1081
|
+
|
|
1082
|
+
def title=(blah)
|
|
1083
|
+
@title = blah
|
|
1084
|
+
end
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
@item = ItemElement4.parse(@xml)
|
|
1088
|
+
end
|
|
1089
|
+
|
|
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")
|
|
1102
|
+
end
|
|
1103
|
+
|
|
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")
|
|
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
|
|
1200
|
+
|
|
1201
|
+
@item = ItemElement5.parse(@io)
|
|
521
1202
|
end
|
|
522
1203
|
|
|
523
|
-
it "
|
|
524
|
-
@item.title.
|
|
1204
|
+
it "parses" do
|
|
1205
|
+
expect(@item.title).to eq("sweet")
|
|
525
1206
|
end
|
|
526
1207
|
end
|
|
527
1208
|
end
|