sax-machine-patched 0.2.0.rc2
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.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Gemfile +9 -0
- data/Guardfile +5 -0
- data/HISTORY.md +13 -0
- data/README.md +107 -0
- data/Rakefile +6 -0
- data/lib/sax-machine.rb +8 -0
- data/lib/sax-machine/sax_ancestor_config.rb +21 -0
- data/lib/sax-machine/sax_attribute_config.rb +40 -0
- data/lib/sax-machine/sax_collection_config.rb +37 -0
- data/lib/sax-machine/sax_config.rb +74 -0
- data/lib/sax-machine/sax_configure.rb +38 -0
- data/lib/sax-machine/sax_document.rb +118 -0
- data/lib/sax-machine/sax_element_config.rb +65 -0
- data/lib/sax-machine/sax_element_value_config.rb +24 -0
- data/lib/sax-machine/sax_handler.rb +192 -0
- data/lib/sax-machine/version.rb +3 -0
- data/sax-machine.gemspec +24 -0
- data/spec/benchmarks/amazon.xml +40 -0
- data/spec/benchmarks/benchmark.rb +158 -0
- data/spec/benchmarks/public_timeline.xml +411 -0
- data/spec/sax-machine/atom.xml +165 -0
- data/spec/sax-machine/configure_sax_machine_spec.rb +53 -0
- data/spec/sax-machine/include_sax_machine_spec.rb +42 -0
- data/spec/sax-machine/sax_document_spec.rb +838 -0
- data/spec/spec_helper.rb +15 -0
- metadata +115 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class A
|
4
|
+
|
5
|
+
SAXMachine.configure(A) do |c|
|
6
|
+
c.element :title
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class B < A
|
12
|
+
|
13
|
+
SAXMachine.configure(B) do |c|
|
14
|
+
c.element :b
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class C < B
|
20
|
+
|
21
|
+
SAXMachine.configure(C) do |c|
|
22
|
+
c.element :c
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "SAXMachine configure" do
|
28
|
+
before do
|
29
|
+
xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
|
30
|
+
@a = A.new
|
31
|
+
@a.parse xml
|
32
|
+
@b = B.new
|
33
|
+
@b.parse xml
|
34
|
+
@c = C.new
|
35
|
+
@c.parse xml
|
36
|
+
end
|
37
|
+
it { @a.should be_a(A) }
|
38
|
+
it { @a.should_not be_a(B) }
|
39
|
+
it { @a.should be_a(SAXMachine) }
|
40
|
+
it { @a.title.should == "Test" }
|
41
|
+
it { @b.should be_a(A) }
|
42
|
+
it { @b.should be_a(B) }
|
43
|
+
it { @b.should be_a(SAXMachine) }
|
44
|
+
it { @b.title.should == "Test" }
|
45
|
+
it { @b.b.should == "Matched!" }
|
46
|
+
it { @c.should be_a(A) }
|
47
|
+
it { @c.should be_a(B) }
|
48
|
+
it { @c.should be_a(C) }
|
49
|
+
it { @c.should be_a(SAXMachine) }
|
50
|
+
it { @c.title.should == "Test" }
|
51
|
+
it { @c.b.should == "Matched!" }
|
52
|
+
it { @c.c.should == "And Again" }
|
53
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class A
|
4
|
+
include SAXMachine
|
5
|
+
element :title
|
6
|
+
end
|
7
|
+
|
8
|
+
class B < A
|
9
|
+
element :b
|
10
|
+
end
|
11
|
+
|
12
|
+
class C < B
|
13
|
+
element :c
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "SAXMachine inheritance" do
|
17
|
+
before do
|
18
|
+
xml = "<top><title>Test</title><b>Matched!</b><c>And Again</c></top>"
|
19
|
+
@a = A.new
|
20
|
+
@a.parse xml
|
21
|
+
@b = B.new
|
22
|
+
@b.parse xml
|
23
|
+
@c = C.new
|
24
|
+
@c.parse xml
|
25
|
+
end
|
26
|
+
it { @a.should be_a(A) }
|
27
|
+
it { @a.should_not be_a(B) }
|
28
|
+
it { @a.should be_a(SAXMachine) }
|
29
|
+
it { @a.title.should == "Test" }
|
30
|
+
it { @b.should be_a(A) }
|
31
|
+
it { @b.should be_a(B) }
|
32
|
+
it { @b.should be_a(SAXMachine) }
|
33
|
+
it { @b.title.should == "Test" }
|
34
|
+
it { @b.b.should == "Matched!" }
|
35
|
+
it { @c.should be_a(A) }
|
36
|
+
it { @c.should be_a(B) }
|
37
|
+
it { @c.should be_a(C) }
|
38
|
+
it { @c.should be_a(SAXMachine) }
|
39
|
+
it { @c.title.should == "Test" }
|
40
|
+
it { @c.b.should == "Matched!" }
|
41
|
+
it { @c.c.should == "And Again" }
|
42
|
+
end
|
@@ -0,0 +1,838 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "SAXMachine" do
|
4
|
+
describe "element" do
|
5
|
+
describe "when parsing a single element" do
|
6
|
+
before :each do
|
7
|
+
@klass = Class.new do
|
8
|
+
include SAXMachine
|
9
|
+
element :title
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should provide mass assignment through initialize method" do
|
14
|
+
document = @klass.new(title: 'Title')
|
15
|
+
document.title.should == 'Title'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should provide an accessor" do
|
19
|
+
document = @klass.new
|
20
|
+
document.title = "Title"
|
21
|
+
document.title.should == "Title"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should allow introspection of the elements" do
|
25
|
+
@klass.column_names.should =~ [:title]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not overwrite the getter is there is already one present" do
|
29
|
+
@klass = Class.new do
|
30
|
+
def title
|
31
|
+
"#{@title} ***"
|
32
|
+
end
|
33
|
+
|
34
|
+
include SAXMachine
|
35
|
+
element :title
|
36
|
+
end
|
37
|
+
document = @klass.new
|
38
|
+
document.title = "Title"
|
39
|
+
document.title.should == "Title ***"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not overwrite the setter if there is already one present" do
|
43
|
+
@klass = Class.new do
|
44
|
+
def title=(val)
|
45
|
+
@title = "#{val} **"
|
46
|
+
end
|
47
|
+
|
48
|
+
include SAXMachine
|
49
|
+
element :title
|
50
|
+
end
|
51
|
+
document = @klass.new
|
52
|
+
document.title = "Title"
|
53
|
+
document.title.should == "Title **"
|
54
|
+
end
|
55
|
+
describe "the class attribute" do
|
56
|
+
before(:each) do
|
57
|
+
@klass = Class.new do
|
58
|
+
include SAXMachine
|
59
|
+
element :date, :class => DateTime
|
60
|
+
end
|
61
|
+
@document = @klass.new
|
62
|
+
@document.date = Time.now.iso8601
|
63
|
+
end
|
64
|
+
it "should be available" do
|
65
|
+
@klass.data_class(:date).should == DateTime
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should handle an integer class" do
|
69
|
+
@klass = Class.new do
|
70
|
+
include SAXMachine
|
71
|
+
element :number, :class => Integer
|
72
|
+
end
|
73
|
+
document = @klass.parse("<number>5</number>")
|
74
|
+
document.number.should == 5
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should handle an float class" do
|
78
|
+
@klass = Class.new do
|
79
|
+
include SAXMachine
|
80
|
+
element :number, :class => Float
|
81
|
+
end
|
82
|
+
document = @klass.parse("<number>5.5</number>")
|
83
|
+
document.number.should == 5.5
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should handle an string class" do
|
87
|
+
@klass = Class.new do
|
88
|
+
include SAXMachine
|
89
|
+
element :number, :class => String
|
90
|
+
end
|
91
|
+
document = @klass.parse("<number>5.5</number>")
|
92
|
+
document.number.should == "5.5"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should handle a time class" do
|
96
|
+
@klass = Class.new do
|
97
|
+
include SAXMachine
|
98
|
+
element :time, :class => Time
|
99
|
+
end
|
100
|
+
document = @klass.parse("<time>1994-02-04T06:20:00Z</time>")
|
101
|
+
document.time.should == Time.utc(1994, 2, 4, 6, 20, 0, 0)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
describe "the required attribute" do
|
106
|
+
it "should be available" do
|
107
|
+
@klass = Class.new do
|
108
|
+
include SAXMachine
|
109
|
+
element :date, :required => true
|
110
|
+
end
|
111
|
+
@klass.required?(:date).should be_true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should not overwrite the accessor when the element is not present" do
|
116
|
+
document = @klass.new
|
117
|
+
document.title = "Title"
|
118
|
+
document.parse("<foo></foo>")
|
119
|
+
document.title.should == "Title"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should overwrite the value when the element is present" do
|
123
|
+
document = @klass.new
|
124
|
+
document.title = "Old title"
|
125
|
+
document.parse("<title>New title</title>")
|
126
|
+
document.title.should == "New title"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should save the element text into an accessor" do
|
130
|
+
document = @klass.parse("<title>My Title</title>")
|
131
|
+
document.title.should == "My Title"
|
132
|
+
end
|
133
|
+
|
134
|
+
if RUBY_VERSION >= "1.9.0"
|
135
|
+
it "should keep the document encoding for elements" do
|
136
|
+
data = "<title>My Title</title>"
|
137
|
+
data.encode!("utf-8")
|
138
|
+
|
139
|
+
document = @klass.parse(data)
|
140
|
+
document.title.encoding.should == data.encoding
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should save cdata into an accessor" do
|
145
|
+
document = @klass.parse("<title><![CDATA[A Title]]></title>")
|
146
|
+
document.title.should == "A Title"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should save the element text into an accessor when there are multiple elements" do
|
150
|
+
document = @klass.parse("<xml><title>My Title</title><foo>bar</foo></xml>")
|
151
|
+
document.title.should == "My Title"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should save the first element text when there are multiple of the same element" do
|
155
|
+
document = @klass.parse("<xml><title>My Title</title><title>bar</title></xml>")
|
156
|
+
document.title.should == "My Title"
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "when parsing multiple elements" do
|
162
|
+
before :each do
|
163
|
+
@klass = Class.new do
|
164
|
+
include SAXMachine
|
165
|
+
element :title
|
166
|
+
element :name
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should save the element text for a second tag" do
|
171
|
+
document = @klass.parse("<xml><title>My Title</title><name>Paul</name></xml>")
|
172
|
+
document.name.should == "Paul"
|
173
|
+
document.title.should == "My Title"
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
it "should not overwrite the getter is there is already one present" do
|
178
|
+
@klass = Class.new do
|
179
|
+
def items
|
180
|
+
[]
|
181
|
+
end
|
182
|
+
|
183
|
+
include SAXMachine
|
184
|
+
elements :items
|
185
|
+
end
|
186
|
+
document = @klass.new
|
187
|
+
document.items = [1, 2, 3, 4]
|
188
|
+
document.items.should == []
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should not overwrite the setter if there is already one present" do
|
192
|
+
@klass = Class.new do
|
193
|
+
def items=(val)
|
194
|
+
@items = [1, *val]
|
195
|
+
end
|
196
|
+
|
197
|
+
include SAXMachine
|
198
|
+
elements :items
|
199
|
+
end
|
200
|
+
document = @klass.new
|
201
|
+
document.items = [2, 3]
|
202
|
+
document.items.should == [1, 2, 3]
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "when using options for parsing elements" do
|
208
|
+
describe "using the 'as' option" do
|
209
|
+
before :each do
|
210
|
+
@klass = Class.new do
|
211
|
+
include SAXMachine
|
212
|
+
element :description, :as => :summary
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should provide an accessor using the 'as' name" do
|
217
|
+
document = @klass.new
|
218
|
+
document.summary = "a small summary"
|
219
|
+
document.summary.should == "a small summary"
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should save the element text into the 'as' accessor" do
|
223
|
+
document = @klass.parse("<description>here is a description</description>")
|
224
|
+
document.summary.should == "here is a description"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "using the :with option" do
|
229
|
+
describe "and the :value option" do
|
230
|
+
before :each do
|
231
|
+
@klass = Class.new do
|
232
|
+
include SAXMachine
|
233
|
+
element :link, :value => :href, :with => {:foo => "bar"}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should save the value of a matching element" do
|
238
|
+
document = @klass.parse("<link href='test' foo='bar'>asdf</link>")
|
239
|
+
document.link.should == "test"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should save the value of the first matching element" do
|
243
|
+
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' foo='bar' /></xml>")
|
244
|
+
document.link.should == "first"
|
245
|
+
end
|
246
|
+
|
247
|
+
describe "and the :as option" do
|
248
|
+
before :each do
|
249
|
+
@klass = Class.new do
|
250
|
+
include SAXMachine
|
251
|
+
element :link, :value => :href, :as => :url, :with => {:foo => "bar"}
|
252
|
+
element :link, :value => :href, :as => :second_url, :with => {:asdf => "jkl"}
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should save the value of the first matching element" do
|
257
|
+
document = @klass.parse("<xml><link href='first' foo='bar' /><link href='second' asdf='jkl' /><link href='second' foo='bar' /></xml>")
|
258
|
+
document.url.should == "first"
|
259
|
+
document.second_url.should == "second"
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "with only one element" do
|
266
|
+
before :each do
|
267
|
+
@klass = Class.new do
|
268
|
+
include SAXMachine
|
269
|
+
element :link, :with => {:foo => "bar"}
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should save the text of an element that has matching attributes" do
|
274
|
+
document = @klass.parse("<link foo=\"bar\">match</link>")
|
275
|
+
document.link.should == "match"
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should not save the text of an element that doesn't have matching attributes" do
|
279
|
+
document = @klass.parse("<link>no match</link>")
|
280
|
+
document.link.should be_nil
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should save the text of an element that has matching attributes when it is the second of that type" do
|
284
|
+
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">match</link></xml>")
|
285
|
+
document.link.should == "match"
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should save the text of an element that has matching attributes plus a few more" do
|
290
|
+
document = @klass.parse("<xml><link>no match</link><link asdf='jkl' foo='bar'>match</link>")
|
291
|
+
document.link.should == "match"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "with multiple elements of same tag" do
|
296
|
+
before :each do
|
297
|
+
@klass = Class.new do
|
298
|
+
include SAXMachine
|
299
|
+
element :link, :as => :first, :with => {:foo => "bar"}
|
300
|
+
element :link, :as => :second, :with => {:asdf => "jkl"}
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should match the first element" do
|
305
|
+
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">first match</link><link>no match</link></xml>")
|
306
|
+
document.first.should == "first match"
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should match the second element" do
|
310
|
+
document = @klass.parse("<xml><link>no match</link><link foo='bar'>first match</link><link asdf='jkl'>second match</link><link>hi</link></xml>")
|
311
|
+
document.second.should == "second match"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe "with only one element as a regular expression" do
|
316
|
+
before :each do
|
317
|
+
@klass = Class.new do
|
318
|
+
include SAXMachine
|
319
|
+
element :link, :with => {:foo => /ar$/}
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
it "should save the text of an element that has matching attributes" do
|
324
|
+
document = @klass.parse("<link foo=\"bar\">match</link>")
|
325
|
+
document.link.should == "match"
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should not save the text of an element that doesn't have matching attributes" do
|
329
|
+
document = @klass.parse("<link>no match</link>")
|
330
|
+
document.link.should be_nil
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should save the text of an element that has matching attributes when it is the second of that type" do
|
334
|
+
document = @klass.parse("<xml><link>no match</link><link foo=\"bar\">match</link></xml>")
|
335
|
+
document.link.should == "match"
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should save the text of an element that has matching attributes plus a few more" do
|
339
|
+
document = @klass.parse("<xml><link>no match</link><link asdf='jkl' foo='bar'>match</link>")
|
340
|
+
document.link.should == "match"
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end # using the 'with' option
|
344
|
+
|
345
|
+
describe "using the 'value' option" do
|
346
|
+
before :each do
|
347
|
+
@klass = Class.new do
|
348
|
+
include SAXMachine
|
349
|
+
element :link, :value => :foo
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should save the attribute value" do
|
354
|
+
document = @klass.parse("<link foo='test'>hello</link>")
|
355
|
+
document.link.should == 'test'
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should save the attribute value when there is no text enclosed by the tag" do
|
359
|
+
document = @klass.parse("<link foo='test'></link>")
|
360
|
+
document.link.should == 'test'
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should save the attribute value when the tag close is in the open" do
|
364
|
+
document = @klass.parse("<link foo='test'/>")
|
365
|
+
document.link.should == 'test'
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should save two different attribute values on a single tag" do
|
369
|
+
@klass = Class.new do
|
370
|
+
include SAXMachine
|
371
|
+
element :link, :value => :foo, :as => :first
|
372
|
+
element :link, :value => :bar, :as => :second
|
373
|
+
end
|
374
|
+
document = @klass.parse("<link foo='foo value' bar='bar value'></link>")
|
375
|
+
document.first.should == "foo value"
|
376
|
+
document.second.should == "bar value"
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should not fail if one of the attribute hasn't been defined" do
|
380
|
+
@klass = Class.new do
|
381
|
+
include SAXMachine
|
382
|
+
element :link, :value => :foo, :as => :first
|
383
|
+
element :link, :value => :bar, :as => :second
|
384
|
+
end
|
385
|
+
document = @klass.parse("<link foo='foo value'></link>")
|
386
|
+
document.first.should == "foo value"
|
387
|
+
document.second.should be_nil
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "when desiring both the content and attributes of an element" do
|
392
|
+
before :each do
|
393
|
+
@klass = Class.new do
|
394
|
+
include SAXMachine
|
395
|
+
element :link
|
396
|
+
element :link, :value => :foo, :as => :link_foo
|
397
|
+
element :link, :value => :bar, :as => :link_bar
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
it "should parse the element and attribute values" do
|
402
|
+
document = @klass.parse("<link foo='test1' bar='test2'>hello</link>")
|
403
|
+
document.link.should == 'hello'
|
404
|
+
document.link_foo.should == 'test1'
|
405
|
+
document.link_bar.should == 'test2'
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
describe "elements" do
|
413
|
+
describe "when parsing multiple elements" do
|
414
|
+
before :each do
|
415
|
+
@klass = Class.new do
|
416
|
+
include SAXMachine
|
417
|
+
elements :entry, :as => :entries
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should provide a collection accessor" do
|
422
|
+
document = @klass.new
|
423
|
+
document.entries << :foo
|
424
|
+
document.entries.should == [:foo]
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should parse a single element" do
|
428
|
+
document = @klass.parse("<entry>hello</entry>")
|
429
|
+
document.entries.should == ["hello"]
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should parse multiple elements" do
|
433
|
+
document = @klass.parse("<xml><entry>hello</entry><entry>world</entry></xml>")
|
434
|
+
document.entries.should == ["hello", "world"]
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should parse multiple elements when taking an attribute value" do
|
438
|
+
attribute_klass = Class.new do
|
439
|
+
include SAXMachine
|
440
|
+
elements :entry, :as => :entries, :value => :foo
|
441
|
+
end
|
442
|
+
doc = attribute_klass.parse("<xml><entry foo='asdf' /><entry foo='jkl' /></xml>")
|
443
|
+
doc.entries.should == ["asdf", "jkl"]
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe "when using the with and class options" do
|
448
|
+
before :each do
|
449
|
+
class Bar
|
450
|
+
include SAXMachine
|
451
|
+
element :title
|
452
|
+
end
|
453
|
+
|
454
|
+
class Foo
|
455
|
+
include SAXMachine
|
456
|
+
element :title
|
457
|
+
end
|
458
|
+
|
459
|
+
class Item
|
460
|
+
include SAXMachine
|
461
|
+
|
462
|
+
end
|
463
|
+
@klass = Class.new do
|
464
|
+
include SAXMachine
|
465
|
+
elements :item, :as => :items, :with => {:type => 'Bar'}, :class => Bar
|
466
|
+
elements :item, :as => :items, :with => {:type => /Foo/}, :class => Foo
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
it "should cast into the correct class" do
|
471
|
+
document = @klass.parse("<items><item type=\"Bar\"><title>Bar title</title></item><item type=\"Foo\"><title>Foo title</title></item></items>")
|
472
|
+
document.items.size.should == 2
|
473
|
+
document.items.first.should be_a(Bar)
|
474
|
+
document.items.first.title.should == "Bar title"
|
475
|
+
document.items.last.should be_a(Foo)
|
476
|
+
document.items.last.title.should == "Foo title"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
describe "when using the class option" do
|
481
|
+
before :each do
|
482
|
+
class Foo
|
483
|
+
include SAXMachine
|
484
|
+
element :title
|
485
|
+
end
|
486
|
+
@klass = Class.new do
|
487
|
+
include SAXMachine
|
488
|
+
elements :entry, :as => :entries, :class => Foo
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
it "should parse a single element with children" do
|
493
|
+
document = @klass.parse("<entry><title>a title</title></entry>")
|
494
|
+
document.entries.size.should == 1
|
495
|
+
document.entries.first.title.should == "a title"
|
496
|
+
end
|
497
|
+
|
498
|
+
it "should parse multiple elements with children" do
|
499
|
+
document = @klass.parse("<xml><entry><title>title 1</title></entry><entry><title>title 2</title></entry></xml>")
|
500
|
+
document.entries.size.should == 2
|
501
|
+
document.entries.first.title.should == "title 1"
|
502
|
+
document.entries.last.title.should == "title 2"
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should not parse a top level element that is specified only in a child" do
|
506
|
+
document = @klass.parse("<xml><title>no parse</title><entry><title>correct title</title></entry></xml>")
|
507
|
+
document.entries.size.should == 1
|
508
|
+
document.entries.first.title.should == "correct title"
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should parse elements, and make attributes and inner text available" do
|
512
|
+
class Related
|
513
|
+
include SAXMachine
|
514
|
+
element 'related', :as=>:item
|
515
|
+
element 'related', :as=>:attr, :value=>'attr'
|
516
|
+
end
|
517
|
+
class Foo
|
518
|
+
elements 'related', :as=>'items', :class=>Related
|
519
|
+
end
|
520
|
+
|
521
|
+
doc = Foo.parse(%{<xml><collection><related attr='baz'>something</related><related>somethingelse</related></collection></xml>})
|
522
|
+
doc.items.first.should_not be_nil
|
523
|
+
doc.items.size.should == 2
|
524
|
+
doc.items.first.item.should == 'something'
|
525
|
+
doc.items.last.item.should == 'somethingelse'
|
526
|
+
end
|
527
|
+
|
528
|
+
it "should parse out an attribute value from the tag that starts the collection" do
|
529
|
+
class Foo
|
530
|
+
element :entry, :value => :href, :as => :url
|
531
|
+
end
|
532
|
+
document = @klass.parse("<xml><entry href='http://pauldix.net'><title>paul</title></entry></xml>")
|
533
|
+
document.entries.size.should == 1
|
534
|
+
document.entries.first.title.should == "paul"
|
535
|
+
document.entries.first.url.should == "http://pauldix.net"
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
describe "when dealing with element names containing dashes" do
|
541
|
+
it 'should automatically convert dashes to underscores' do
|
542
|
+
class Dashes
|
543
|
+
include SAXMachine
|
544
|
+
element :dashed_element
|
545
|
+
end
|
546
|
+
|
547
|
+
parsed = Dashes.parse('<dashed-element>Text</dashed-element>')
|
548
|
+
parsed.dashed_element.should eq "Text"
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
describe "full example" do
|
553
|
+
before :each do
|
554
|
+
@xml = File.read('spec/sax-machine/atom.xml')
|
555
|
+
class AtomEntry
|
556
|
+
include SAXMachine
|
557
|
+
element :title
|
558
|
+
element :name, :as => :author
|
559
|
+
element "feedburner:origLink", :as => :url
|
560
|
+
element :link, :as => :alternate, :value => :href, :with => {:type => "text/html", :rel => "alternate"}
|
561
|
+
element :summary
|
562
|
+
element :content
|
563
|
+
element :published
|
564
|
+
end
|
565
|
+
|
566
|
+
class Atom
|
567
|
+
include SAXMachine
|
568
|
+
element :title
|
569
|
+
element :link, :value => :href, :as => :url, :with => {:type => "text/html"}
|
570
|
+
element :link, :value => :href, :as => :feed_url, :with => {:type => "application/atom+xml"}
|
571
|
+
elements :entry, :as => :entries, :class => AtomEntry
|
572
|
+
end
|
573
|
+
end # before
|
574
|
+
|
575
|
+
it "should parse the url" do
|
576
|
+
f = Atom.parse(@xml)
|
577
|
+
f.url.should == "http://www.pauldix.net/"
|
578
|
+
end
|
579
|
+
|
580
|
+
it "should parse entry url" do
|
581
|
+
f = Atom.parse(@xml)
|
582
|
+
f.entries.first.url.should == "http://www.pauldix.net/2008/09/marshal-data-to.html?param1=1¶m2=2"
|
583
|
+
f.entries.first.alternate.should == "http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/383536354/marshal-data-to.html?param1=1¶m2=2"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
describe "parsing a tree" do
|
588
|
+
before do
|
589
|
+
@xml = %[
|
590
|
+
<categories>
|
591
|
+
<category id="1">
|
592
|
+
<title>First</title>
|
593
|
+
<categories>
|
594
|
+
<category id="2">
|
595
|
+
<title>Second</title>
|
596
|
+
</category>
|
597
|
+
</categories>
|
598
|
+
</category>
|
599
|
+
</categories>
|
600
|
+
]
|
601
|
+
class CategoryCollection;
|
602
|
+
end
|
603
|
+
class Category
|
604
|
+
include SAXMachine
|
605
|
+
attr_accessor :id
|
606
|
+
element :category, :value => :id, :as => :id
|
607
|
+
element :title
|
608
|
+
element :categories, :as => :collection, :class => CategoryCollection
|
609
|
+
ancestor :ancestor
|
610
|
+
end
|
611
|
+
class CategoryCollection
|
612
|
+
include SAXMachine
|
613
|
+
elements :category, :as => :categories, :class => Category
|
614
|
+
end
|
615
|
+
@collection = CategoryCollection.parse(@xml)
|
616
|
+
end
|
617
|
+
|
618
|
+
it "should parse the first category" do
|
619
|
+
@collection.categories.first.id.should == "1"
|
620
|
+
@collection.categories.first.title.should == "First"
|
621
|
+
@collection.categories.first.ancestor.should == @collection
|
622
|
+
end
|
623
|
+
|
624
|
+
it "should parse the nested category" do
|
625
|
+
@collection.categories.first.collection.categories.first.id.should == "2"
|
626
|
+
@collection.categories.first.collection.categories.first.title.should == "Second"
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
describe "parsing a tree without a collection class" do
|
631
|
+
before do
|
632
|
+
@xml = %[
|
633
|
+
<categories>
|
634
|
+
<category id="1">
|
635
|
+
<title>First</title>
|
636
|
+
<categories>
|
637
|
+
<category id="2">
|
638
|
+
<title>Second</title>
|
639
|
+
</category>
|
640
|
+
</categories>
|
641
|
+
</category>
|
642
|
+
</categories>
|
643
|
+
]
|
644
|
+
class CategoryTree
|
645
|
+
include SAXMachine
|
646
|
+
attr_accessor :id
|
647
|
+
element :category, :value => :id, :as => :id
|
648
|
+
element :title
|
649
|
+
elements :category, :as => :categories, :class => CategoryTree
|
650
|
+
end
|
651
|
+
@collection = CategoryTree.parse(@xml)
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should parse the first category" do
|
655
|
+
@collection.categories.first.id.should == "1"
|
656
|
+
@collection.categories.first.title.should == "First"
|
657
|
+
end
|
658
|
+
|
659
|
+
it "should parse the nested category" do
|
660
|
+
@collection.categories.first.categories.first.id.should == "2"
|
661
|
+
@collection.categories.first.categories.first.title.should == "Second"
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
describe "with element deeper inside the xml structure" do
|
666
|
+
before do
|
667
|
+
@xml = %[
|
668
|
+
<item id="1">
|
669
|
+
<texts>
|
670
|
+
<title>Hello</title>
|
671
|
+
</texts>
|
672
|
+
</item>
|
673
|
+
]
|
674
|
+
@klass = Class.new do
|
675
|
+
include SAXMachine
|
676
|
+
attr_accessor :id
|
677
|
+
element :item, :value => "id", :as => :id
|
678
|
+
element :title
|
679
|
+
end
|
680
|
+
@item = @klass.parse(@xml)
|
681
|
+
end
|
682
|
+
|
683
|
+
it "should have an id" do
|
684
|
+
@item.id.should == "1"
|
685
|
+
end
|
686
|
+
|
687
|
+
it "should have a title" do
|
688
|
+
@item.title.should == "Hello"
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
describe "with config to pull multiple attributes" do
|
693
|
+
before do
|
694
|
+
@xml = %[
|
695
|
+
<item id="1">
|
696
|
+
<author name="John Doe" role="writer" />
|
697
|
+
</item>
|
698
|
+
]
|
699
|
+
class AuthorElement
|
700
|
+
include SAXMachine
|
701
|
+
attribute :name
|
702
|
+
attribute :role
|
703
|
+
end
|
704
|
+
class ItemElement
|
705
|
+
include SAXMachine
|
706
|
+
element :author, :class => AuthorElement
|
707
|
+
end
|
708
|
+
@item = ItemElement.parse(@xml)
|
709
|
+
end
|
710
|
+
|
711
|
+
it 'should have the child element' do
|
712
|
+
@item.author.should_not be_nil
|
713
|
+
end
|
714
|
+
|
715
|
+
it 'should have the author name' do
|
716
|
+
@item.author.name.should == 'John Doe'
|
717
|
+
end
|
718
|
+
|
719
|
+
it 'should have the author role' do
|
720
|
+
@item.author.role.should == 'writer'
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
describe "with multiple elements and multiple attributes" do
|
725
|
+
before do
|
726
|
+
@xml = %[
|
727
|
+
<item id="1">
|
728
|
+
<author name="John Doe" role="writer" />
|
729
|
+
<author name="Jane Doe" role="artist" />
|
730
|
+
</item>
|
731
|
+
]
|
732
|
+
class AuthorElement2
|
733
|
+
include SAXMachine
|
734
|
+
attribute :name
|
735
|
+
attribute :role
|
736
|
+
end
|
737
|
+
class ItemElement2
|
738
|
+
include SAXMachine
|
739
|
+
elements :author, :as => :authors, :class => AuthorElement2
|
740
|
+
end
|
741
|
+
@item = ItemElement2.parse(@xml)
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'should have the child elements' do
|
745
|
+
@item.authors.should_not be_nil
|
746
|
+
@item.authors.count.should == 2
|
747
|
+
end
|
748
|
+
|
749
|
+
it 'should have the author names' do
|
750
|
+
@item.authors.first.name.should == 'John Doe'
|
751
|
+
@item.authors.last.name.should == 'Jane Doe'
|
752
|
+
end
|
753
|
+
|
754
|
+
it 'should have the author roles' do
|
755
|
+
@item.authors.first.role.should == 'writer'
|
756
|
+
@item.authors.last.role.should == 'artist'
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
describe "with mixed attributes and element values" do
|
761
|
+
before do
|
762
|
+
@xml = %[
|
763
|
+
<item id="1">
|
764
|
+
<author role="writer">John Doe</author>
|
765
|
+
</item>
|
766
|
+
]
|
767
|
+
class AuthorElement3
|
768
|
+
include SAXMachine
|
769
|
+
value :name
|
770
|
+
attribute :role
|
771
|
+
end
|
772
|
+
class ItemElement3
|
773
|
+
include SAXMachine
|
774
|
+
element :author, :class => AuthorElement3
|
775
|
+
end
|
776
|
+
@item = ItemElement3.parse(@xml)
|
777
|
+
end
|
778
|
+
|
779
|
+
it 'should have the child elements' do
|
780
|
+
@item.author.should_not be_nil
|
781
|
+
end
|
782
|
+
|
783
|
+
it 'should have the author names' do
|
784
|
+
@item.author.name.should == 'John Doe'
|
785
|
+
end
|
786
|
+
|
787
|
+
it 'should have the author roles' do
|
788
|
+
@item.author.role.should == 'writer'
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
describe "with multiple mixed attributes and element values" do
|
793
|
+
before do
|
794
|
+
@xml = %[
|
795
|
+
<item id="1">
|
796
|
+
<title>sweet</title>
|
797
|
+
<author role="writer">John Doe</author>
|
798
|
+
<author role="artist">Jane Doe</author>
|
799
|
+
</item>
|
800
|
+
]
|
801
|
+
class AuthorElement4
|
802
|
+
include SAXMachine
|
803
|
+
value :name
|
804
|
+
attribute :role
|
805
|
+
end
|
806
|
+
class ItemElement4
|
807
|
+
include SAXMachine
|
808
|
+
element :title
|
809
|
+
elements :author, :as => :authors, :class => AuthorElement4
|
810
|
+
|
811
|
+
def title=(blah)
|
812
|
+
#raise 'woo'
|
813
|
+
@title = blah
|
814
|
+
end
|
815
|
+
end
|
816
|
+
@item = ItemElement4.parse(@xml)
|
817
|
+
end
|
818
|
+
|
819
|
+
it 'should have the title' do
|
820
|
+
@item.title.should == 'sweet'
|
821
|
+
end
|
822
|
+
|
823
|
+
it 'should have the child elements' do
|
824
|
+
@item.authors.should_not be_nil
|
825
|
+
@item.authors.count.should == 2
|
826
|
+
end
|
827
|
+
|
828
|
+
it 'should have the author names' do
|
829
|
+
@item.authors.first.name.should == 'John Doe'
|
830
|
+
@item.authors.last.name.should == 'Jane Doe'
|
831
|
+
end
|
832
|
+
|
833
|
+
it 'should have the author roles' do
|
834
|
+
@item.authors.first.role.should == 'writer'
|
835
|
+
@item.authors.last.role.should == 'artist'
|
836
|
+
end
|
837
|
+
end
|
838
|
+
end
|