rxerces 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 301821063fa998d1a4522029ef825aaa7bddc45370ed0c1bb904f8b31fd9f036
4
- data.tar.gz: cabdd59089a44485d847b7ff3637197d2c24e38abb3e5c68423c397a9869e843
3
+ metadata.gz: 3a1129744191c92a7a15cb783fc74cbccfd2dbeedfbcf5741e295686c38b8108
4
+ data.tar.gz: 0b03758412cff12acccf3232f0f135ec69e0eaabcb6aa015091a44ed75e53081
5
5
  SHA512:
6
- metadata.gz: 9edd86524fc0ac49a51a9b2658d723a84f85c295d362e8dad500bcd8a47e95c3ad4b41d073f7bdcee0927bd38a6b1710537ee190693ec739de20e65b22e04d8a
7
- data.tar.gz: 035ed74771b3d3e6125c1567d5452ac773f80a2f9ae220efd063358f7fcc93326f1beb027bf67e45ad042a950a8372b208426f07ab5320c58bb453f40c4d4c19
6
+ metadata.gz: e2ba8796e66c0163d1f3bd9ae91d2fde804b462b39fd750e8f600b073e1ad02e36a2978cd533ea7256a9ad7b7d906c4462927bc609b1f59ac7c33f39b5423b83
7
+ data.tar.gz: 9b7b522fc2c5f203a2de65ba2d112ef644cf84fb18128503432ab37240f4b31cdc7acde58c83d00e5b17e006433e595aa19f46f2a5a11345c1778c55a76c1820
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.6.0 - 17-Dec-2025
2
+ * Some internal refactoring, better initialization, some better string
3
+ handling, that sort of stuff.
4
+ * Added the Document#errors method with more detailed information. Also
5
+ helps with Nokogiri compatibility.
6
+ * Added some benchmarks (they're not great compared to others, oh well).
7
+
1
8
  ## 0.5.0 - 16-Dec-2025
2
9
  * Implemented a real css method. Requires Xalan to be installed.
3
10
  * Added text/content methods for most classes.
@@ -0,0 +1,68 @@
1
+ # RXerces Performance Benchmarks
2
+
3
+ This directory contains performance benchmarks comparing RXerces with other popular Ruby XML libraries (Nokogiri and Ox).
4
+
5
+ ## Setup
6
+
7
+ Install the required gems:
8
+
9
+ ```bash
10
+ gem install benchmark-ips nokogiri ox
11
+ ```
12
+
13
+ ## Running Benchmarks
14
+
15
+ Run all benchmarks:
16
+
17
+ ```bash
18
+ ruby benchmarks/parse_benchmark.rb
19
+ ruby benchmarks/xpath_benchmark.rb
20
+ ruby benchmarks/css_benchmark.rb
21
+ ruby benchmarks/traversal_benchmark.rb
22
+ ruby benchmarks/serialization_benchmark.rb
23
+ ```
24
+
25
+ Or run a specific benchmark:
26
+
27
+ ```bash
28
+ ruby benchmarks/parse_benchmark.rb
29
+ ```
30
+
31
+ ## Benchmark Categories
32
+
33
+ ### 1. Parse Benchmark (`parse_benchmark.rb`)
34
+ Tests XML document parsing performance with small, medium, and large documents.
35
+
36
+ ### 2. XPath Benchmark (`xpath_benchmark.rb`)
37
+ Tests XPath query performance including:
38
+ - Simple queries (`//book`)
39
+ - Attribute-based queries (`//book[@category='fiction']`)
40
+ - Complex queries with predicates
41
+ - `at_xpath` for first-match queries
42
+
43
+ ### 3. CSS Benchmark (`css_benchmark.rb`)
44
+ Tests CSS selector performance including:
45
+ - Simple selectors (`div`)
46
+ - Class selectors (`.title`)
47
+ - ID selectors (`#div100`)
48
+ - Descendant combinators (`div.content p.text`)
49
+ - `at_css` for first-match queries
50
+
51
+ ### 4. Traversal Benchmark (`traversal_benchmark.rb`)
52
+ Tests DOM traversal operations:
53
+ - `.children` access
54
+ - `.element_children` access
55
+ - `.parent` access
56
+ - `.ancestors` access
57
+ - `.next_sibling` access
58
+ - `.text` extraction
59
+
60
+ ### 5. Serialization Benchmark (`serialization_benchmark.rb`)
61
+ Tests document serialization (`to_s`/`to_xml`) with various document sizes.
62
+
63
+ ## Notes
64
+
65
+ - All benchmarks use `benchmark-ips` for accurate iterations-per-second measurements
66
+ - Each benchmark runs with a 2-second warmup and 5-second measurement period
67
+ - Nokogiri and Ox tests are skipped if not installed
68
+ - RXerces requires Xalan-C for full XPath 1.0 support (CSS selectors need XPath)
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require 'rxerces'
6
+
7
+ begin
8
+ require 'nokogiri'
9
+ NOKOGIRI_AVAILABLE = true
10
+ rescue LoadError
11
+ NOKOGIRI_AVAILABLE = false
12
+ puts "Nokogiri not available - install with: gem install nokogiri"
13
+ end
14
+
15
+ # Sample HTML/XML for CSS selector queries
16
+ HTML_DATA = begin
17
+ divs = (1..200).map do |i|
18
+ "<div class=\"content\" id=\"div#{i}\">
19
+ <h2 class=\"title\">Heading #{i}</h2>
20
+ <p class=\"text\">Paragraph #{i} with <span class=\"highlight\">highlighted</span> text.</p>
21
+ <ul class=\"list\">
22
+ <li>Item 1</li>
23
+ <li>Item 2</li>
24
+ <li class=\"special\">Special Item</li>
25
+ </ul>
26
+ </div>"
27
+ end
28
+ "<html><body>#{divs.join}</body></html>"
29
+ end
30
+
31
+ puts "=" * 80
32
+ puts "CSS Selector Benchmarks"
33
+ puts "=" * 80
34
+ puts "Document Size: #{HTML_DATA.bytesize} bytes"
35
+ puts
36
+
37
+ # Parse documents once
38
+ rxerces_doc = RXerces::XML::Document.parse(HTML_DATA)
39
+ nokogiri_doc = Nokogiri::HTML(HTML_DATA) if NOKOGIRI_AVAILABLE
40
+
41
+ # Simple CSS selector
42
+ puts "Simple CSS: div"
43
+ puts "-" * 80
44
+
45
+ Benchmark.ips do |x|
46
+ x.config(time: 5, warmup: 2)
47
+
48
+ x.report("rxerces") { rxerces_doc.css('div') }
49
+ x.report("nokogiri") { nokogiri_doc.css('div') } if NOKOGIRI_AVAILABLE
50
+
51
+ x.compare!
52
+ end
53
+
54
+ puts
55
+
56
+ # Class selector
57
+ puts "Class CSS: .title"
58
+ puts "-" * 80
59
+
60
+ Benchmark.ips do |x|
61
+ x.config(time: 5, warmup: 2)
62
+
63
+ x.report("rxerces") { rxerces_doc.css('.title') }
64
+ x.report("nokogiri") { nokogiri_doc.css('.title') } if NOKOGIRI_AVAILABLE
65
+
66
+ x.compare!
67
+ end
68
+
69
+ puts
70
+
71
+ # ID selector
72
+ puts "ID CSS: #div100"
73
+ puts "-" * 80
74
+
75
+ Benchmark.ips do |x|
76
+ x.config(time: 5, warmup: 2)
77
+
78
+ x.report("rxerces") { rxerces_doc.css('#div100') }
79
+ x.report("nokogiri") { nokogiri_doc.css('#div100') } if NOKOGIRI_AVAILABLE
80
+
81
+ x.compare!
82
+ end
83
+
84
+ puts
85
+
86
+ # Descendant combinator
87
+ puts "Descendant CSS: div.content p.text"
88
+ puts "-" * 80
89
+
90
+ Benchmark.ips do |x|
91
+ x.config(time: 5, warmup: 2)
92
+
93
+ x.report("rxerces") { rxerces_doc.css('div.content p.text') }
94
+ x.report("nokogiri") { nokogiri_doc.css('div.content p.text') } if NOKOGIRI_AVAILABLE
95
+
96
+ x.compare!
97
+ end
98
+
99
+ puts
100
+
101
+ # at_css (first match)
102
+ puts "at_css: .title (first match only)"
103
+ puts "-" * 80
104
+
105
+ Benchmark.ips do |x|
106
+ x.config(time: 5, warmup: 2)
107
+
108
+ x.report("rxerces") { rxerces_doc.at_css('.title') }
109
+ x.report("nokogiri") { nokogiri_doc.at_css('.title') } if NOKOGIRI_AVAILABLE
110
+
111
+ x.compare!
112
+ end
113
+
114
+ puts
115
+ puts "=" * 80
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require 'rxerces'
6
+
7
+ # Try to load Nokogiri and Ox
8
+ begin
9
+ require 'nokogiri'
10
+ NOKOGIRI_AVAILABLE = true
11
+ rescue LoadError
12
+ NOKOGIRI_AVAILABLE = false
13
+ puts "Nokogiri not available - install with: gem install nokogiri"
14
+ end
15
+
16
+ begin
17
+ require 'ox'
18
+ OX_AVAILABLE = true
19
+ rescue LoadError
20
+ OX_AVAILABLE = false
21
+ puts "Ox not available - install with: gem install ox"
22
+ end
23
+
24
+ # Sample XML documents of varying sizes
25
+ SMALL_XML = <<~XML
26
+ <root>
27
+ <person id="1" name="Alice">
28
+ <age>30</age>
29
+ <city>New York</city>
30
+ </person>
31
+ </root>
32
+ XML
33
+
34
+ MEDIUM_XML = begin
35
+ people = (1..100).map do |i|
36
+ "<person id=\"#{i}\" name=\"Person#{i}\"><age>#{20 + (i % 50)}</age><city>City#{i % 20}</city></person>"
37
+ end
38
+ "<root>#{people.join}</root>"
39
+ end
40
+
41
+ # Generate a large XML document
42
+ def generate_large_xml(count = 1000)
43
+ people = (1..count).map do |i|
44
+ "<person id=\"#{i}\" name=\"Person#{i}\"><age>#{20 + (i % 50)}</age><city>City#{i % 20}</city></person>"
45
+ end
46
+ "<root>#{people.join}</root>"
47
+ end
48
+
49
+ LARGE_XML = generate_large_xml(1000)
50
+
51
+ puts "=" * 80
52
+ puts "XML Parsing Benchmarks"
53
+ puts "=" * 80
54
+ puts
55
+
56
+ # Small XML parsing
57
+ puts "Small XML Parsing (#{SMALL_XML.bytesize} bytes)"
58
+ puts "-" * 80
59
+
60
+ Benchmark.ips do |x|
61
+ x.config(time: 5, warmup: 2)
62
+
63
+ x.report("rxerces") { RXerces::XML::Document.parse(SMALL_XML) }
64
+ x.report("nokogiri") { Nokogiri::XML(SMALL_XML) } if NOKOGIRI_AVAILABLE
65
+ x.report("ox") { Ox.parse(SMALL_XML) } if OX_AVAILABLE
66
+
67
+ x.compare!
68
+ end
69
+
70
+ puts
71
+
72
+ # Medium XML parsing
73
+ puts "Medium XML Parsing (#{MEDIUM_XML.bytesize} bytes)"
74
+ puts "-" * 80
75
+
76
+ Benchmark.ips do |x|
77
+ x.config(time: 5, warmup: 2)
78
+
79
+ x.report("rxerces") { RXerces::XML::Document.parse(MEDIUM_XML) }
80
+ x.report("nokogiri") { Nokogiri::XML(MEDIUM_XML) } if NOKOGIRI_AVAILABLE
81
+ x.report("ox") { Ox.parse(MEDIUM_XML) } if OX_AVAILABLE
82
+
83
+ x.compare!
84
+ end
85
+
86
+ puts
87
+
88
+ # Large XML parsing
89
+ puts "Large XML Parsing (#{LARGE_XML.bytesize} bytes)"
90
+ puts "-" * 80
91
+
92
+ Benchmark.ips do |x|
93
+ x.config(time: 5, warmup: 2)
94
+
95
+ x.report("rxerces") { RXerces::XML::Document.parse(LARGE_XML) }
96
+ x.report("nokogiri") { Nokogiri::XML(LARGE_XML) } if NOKOGIRI_AVAILABLE
97
+ x.report("ox") { Ox.parse(LARGE_XML) } if OX_AVAILABLE
98
+
99
+ x.compare!
100
+ end
101
+
102
+ puts
103
+ puts "=" * 80
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Run all benchmarks in sequence
5
+
6
+ benchmarks = %w[
7
+ parse_benchmark.rb
8
+ xpath_benchmark.rb
9
+ css_benchmark.rb
10
+ traversal_benchmark.rb
11
+ serialization_benchmark.rb
12
+ ]
13
+
14
+ puts "Running all RXerces benchmarks..."
15
+ puts "=" * 80
16
+ puts
17
+
18
+ benchmarks.each do |benchmark|
19
+ puts "\nRunning #{benchmark}...\n\n"
20
+ system("ruby -Ilib benchmarks/#{benchmark}")
21
+ puts "\n"
22
+ end
23
+
24
+ puts "=" * 80
25
+ puts "All benchmarks complete!"
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require 'rxerces'
6
+
7
+ begin
8
+ require 'nokogiri'
9
+ NOKOGIRI_AVAILABLE = true
10
+ rescue LoadError
11
+ NOKOGIRI_AVAILABLE = false
12
+ puts "Nokogiri not available - install with: gem install nokogiri"
13
+ end
14
+
15
+ puts "=" * 80
16
+ puts "Document Serialization Benchmarks"
17
+ puts "=" * 80
18
+ puts
19
+
20
+ # Small document
21
+ SMALL_XML = <<~XML
22
+ <root>
23
+ <person id="1" name="Alice">
24
+ <age>30</age>
25
+ <city>New York</city>
26
+ </person>
27
+ </root>
28
+ XML
29
+
30
+ rxerces_small = RXerces::XML::Document.parse(SMALL_XML)
31
+ nokogiri_small = Nokogiri::XML(SMALL_XML) if NOKOGIRI_AVAILABLE
32
+
33
+ puts "Small document to_s (#{SMALL_XML.bytesize} bytes)"
34
+ puts "-" * 80
35
+
36
+ Benchmark.ips do |x|
37
+ x.config(time: 5, warmup: 2)
38
+
39
+ x.report("rxerces") { rxerces_small.to_s }
40
+ x.report("nokogiri") { nokogiri_small.to_xml } if NOKOGIRI_AVAILABLE
41
+
42
+ x.compare!
43
+ end
44
+
45
+ puts
46
+
47
+ # Medium document
48
+ def generate_xml(count)
49
+ people = (1..count).map do |i|
50
+ "<person id=\"#{i}\" name=\"Person#{i}\"><age>#{20 + (i % 50)}</age><city>City#{i % 20}</city></person>"
51
+ end
52
+ "<root>#{people.join}</root>"
53
+ end
54
+
55
+ MEDIUM_XML = generate_xml(100)
56
+
57
+ rxerces_medium = RXerces::XML::Document.parse(MEDIUM_XML)
58
+ nokogiri_medium = Nokogiri::XML(MEDIUM_XML) if NOKOGIRI_AVAILABLE
59
+
60
+ puts "Medium document to_s (#{MEDIUM_XML.bytesize} bytes)"
61
+ puts "-" * 80
62
+
63
+ Benchmark.ips do |x|
64
+ x.config(time: 5, warmup: 2)
65
+
66
+ x.report("rxerces") { rxerces_medium.to_s }
67
+ x.report("nokogiri") { nokogiri_medium.to_xml } if NOKOGIRI_AVAILABLE
68
+
69
+ x.compare!
70
+ end
71
+
72
+ puts
73
+
74
+ # Large document
75
+ LARGE_XML = generate_xml(1000)
76
+
77
+ rxerces_large = RXerces::XML::Document.parse(LARGE_XML)
78
+ nokogiri_large = Nokogiri::XML(LARGE_XML) if NOKOGIRI_AVAILABLE
79
+
80
+ puts "Large document to_s (#{LARGE_XML.bytesize} bytes)"
81
+ puts "-" * 80
82
+
83
+ Benchmark.ips do |x|
84
+ x.config(time: 5, warmup: 2)
85
+
86
+ x.report("rxerces") { rxerces_large.to_s }
87
+ x.report("nokogiri") { nokogiri_large.to_xml } if NOKOGIRI_AVAILABLE
88
+
89
+ x.compare!
90
+ end
91
+
92
+ puts
93
+ puts "=" * 80
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require 'rxerces'
6
+
7
+ begin
8
+ require 'nokogiri'
9
+ NOKOGIRI_AVAILABLE = true
10
+ rescue LoadError
11
+ NOKOGIRI_AVAILABLE = false
12
+ puts "Nokogiri not available - install with: gem install nokogiri"
13
+ end
14
+
15
+ # Sample XML for DOM traversal
16
+ XML_DATA = begin
17
+ sections = (1..100).map do |i|
18
+ "<section id=\"s#{i}\">
19
+ <article id=\"a#{i}\">
20
+ <header>
21
+ <title>Title #{i}</title>
22
+ <author>Author #{i}</author>
23
+ </header>
24
+ <content>
25
+ <paragraph>Paragraph 1</paragraph>
26
+ <paragraph>Paragraph 2</paragraph>
27
+ <paragraph>Paragraph 3</paragraph>
28
+ </content>
29
+ <footer>
30
+ <date>2024-01-01</date>
31
+ </footer>
32
+ </article>
33
+ </section>"
34
+ end
35
+ "<root>#{sections.join}</root>"
36
+ end
37
+
38
+ puts "=" * 80
39
+ puts "DOM Traversal Benchmarks"
40
+ puts "=" * 80
41
+ puts "Document Size: #{XML_DATA.bytesize} bytes"
42
+ puts
43
+
44
+ # Parse documents once
45
+ rxerces_doc = RXerces::XML::Document.parse(XML_DATA)
46
+ nokogiri_doc = Nokogiri::XML(XML_DATA) if NOKOGIRI_AVAILABLE
47
+
48
+ rxerces_root = rxerces_doc.root
49
+ nokogiri_root = nokogiri_doc.root if NOKOGIRI_AVAILABLE
50
+
51
+ # Children access
52
+ puts "Access .children"
53
+ puts "-" * 80
54
+
55
+ Benchmark.ips do |x|
56
+ x.config(time: 5, warmup: 2)
57
+
58
+ x.report("rxerces") { rxerces_root.children }
59
+ x.report("nokogiri") { nokogiri_root.children } if NOKOGIRI_AVAILABLE
60
+
61
+ x.compare!
62
+ end
63
+
64
+ puts
65
+
66
+ # Element children access
67
+ puts "Access .element_children (elements only)"
68
+ puts "-" * 80
69
+
70
+ Benchmark.ips do |x|
71
+ x.config(time: 5, warmup: 2)
72
+
73
+ x.report("rxerces") { rxerces_root.element_children }
74
+ x.report("nokogiri") { nokogiri_root.element_children } if NOKOGIRI_AVAILABLE
75
+
76
+ x.compare!
77
+ end
78
+
79
+ puts
80
+
81
+ # Parent access
82
+ puts "Access .parent on deep node"
83
+ puts "-" * 80
84
+
85
+ rxerces_deep = rxerces_doc.xpath('//paragraph').first
86
+ nokogiri_deep = nokogiri_doc.xpath('//paragraph').first if NOKOGIRI_AVAILABLE
87
+
88
+ Benchmark.ips do |x|
89
+ x.config(time: 5, warmup: 2)
90
+
91
+ x.report("rxerces") { rxerces_deep.parent }
92
+ x.report("nokogiri") { nokogiri_deep.parent } if NOKOGIRI_AVAILABLE
93
+
94
+ x.compare!
95
+ end
96
+
97
+ puts
98
+
99
+ # Ancestors access
100
+ puts "Access .ancestors on deep node"
101
+ puts "-" * 80
102
+
103
+ Benchmark.ips do |x|
104
+ x.config(time: 5, warmup: 2)
105
+
106
+ x.report("rxerces") { rxerces_deep.ancestors }
107
+ x.report("nokogiri") { nokogiri_deep.ancestors } if NOKOGIRI_AVAILABLE
108
+
109
+ x.compare!
110
+ end
111
+
112
+ puts
113
+
114
+ # Next sibling
115
+ puts "Access .next_sibling"
116
+ puts "-" * 80
117
+
118
+ rxerces_para = rxerces_doc.xpath('//paragraph').first
119
+ nokogiri_para = nokogiri_doc.xpath('//paragraph').first if NOKOGIRI_AVAILABLE
120
+
121
+ Benchmark.ips do |x|
122
+ x.config(time: 5, warmup: 2)
123
+
124
+ x.report("rxerces") { rxerces_para.next_sibling }
125
+ x.report("nokogiri") { nokogiri_para.next_sibling } if NOKOGIRI_AVAILABLE
126
+
127
+ x.compare!
128
+ end
129
+
130
+ puts
131
+
132
+ # Text extraction
133
+ puts "Extract .text from element"
134
+ puts "-" * 80
135
+
136
+ rxerces_section = rxerces_doc.xpath('//section').first
137
+ nokogiri_section = nokogiri_doc.xpath('//section').first if NOKOGIRI_AVAILABLE
138
+
139
+ Benchmark.ips do |x|
140
+ x.config(time: 5, warmup: 2)
141
+
142
+ x.report("rxerces") { rxerces_section.text }
143
+ x.report("nokogiri") { nokogiri_section.text } if NOKOGIRI_AVAILABLE
144
+
145
+ x.compare!
146
+ end
147
+
148
+ puts
149
+ puts "=" * 80
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'benchmark/ips'
5
+ require 'rxerces'
6
+
7
+ begin
8
+ require 'nokogiri'
9
+ NOKOGIRI_AVAILABLE = true
10
+ rescue LoadError
11
+ NOKOGIRI_AVAILABLE = false
12
+ puts "Nokogiri not available - install with: gem install nokogiri"
13
+ end
14
+
15
+ # Sample XML for XPath queries
16
+ XML_DATA = begin
17
+ books = (1..500).map do |i|
18
+ category = ['fiction', 'science', 'biography', 'history'][i % 4]
19
+ year = 1990 + (i % 30)
20
+ price = 10.0 + (i % 50)
21
+ "<book id=\"book#{i}\" category=\"#{category}\">
22
+ <title lang=\"en\">Title #{i}</title>
23
+ <author>Author #{i}</author>
24
+ <year>#{year}</year>
25
+ <price>#{'%.2f' % price}</price>
26
+ </book>"
27
+ end
28
+ "<catalog>#{books.join}</catalog>"
29
+ end
30
+
31
+ puts "=" * 80
32
+ puts "XPath Query Benchmarks"
33
+ puts "=" * 80
34
+ puts "XML Size: #{XML_DATA.bytesize} bytes"
35
+ puts
36
+
37
+ # Parse documents once
38
+ rxerces_doc = RXerces::XML::Document.parse(XML_DATA)
39
+ nokogiri_doc = Nokogiri::XML(XML_DATA) if NOKOGIRI_AVAILABLE
40
+
41
+ # Simple XPath queries
42
+ puts "Simple XPath: //book"
43
+ puts "-" * 80
44
+
45
+ Benchmark.ips do |x|
46
+ x.config(time: 5, warmup: 2)
47
+
48
+ x.report("rxerces") { rxerces_doc.xpath('//book') }
49
+ x.report("nokogiri") { nokogiri_doc.xpath('//book') } if NOKOGIRI_AVAILABLE
50
+
51
+ x.compare!
52
+ end
53
+
54
+ puts
55
+
56
+ # Attribute-based XPath queries
57
+ puts "Attribute XPath: //book[@category='fiction']"
58
+ puts "-" * 80
59
+
60
+ Benchmark.ips do |x|
61
+ x.config(time: 5, warmup: 2)
62
+
63
+ x.report("rxerces") { rxerces_doc.xpath("//book[@category='fiction']") }
64
+ x.report("nokogiri") { nokogiri_doc.xpath("//book[@category='fiction']") } if NOKOGIRI_AVAILABLE
65
+
66
+ x.compare!
67
+ end
68
+
69
+ puts
70
+
71
+ # Complex XPath queries
72
+ puts "Complex XPath: //book[year > 2000 and price < 30]/title"
73
+ puts "-" * 80
74
+
75
+ Benchmark.ips do |x|
76
+ x.config(time: 5, warmup: 2)
77
+
78
+ x.report("rxerces") { rxerces_doc.xpath('//book[year > 2000 and price < 30]/title') }
79
+ x.report("nokogiri") { nokogiri_doc.xpath('//book[year > 2000 and price < 30]/title') } if NOKOGIRI_AVAILABLE
80
+
81
+ x.compare!
82
+ end
83
+
84
+ puts
85
+
86
+ # at_xpath (first match)
87
+ puts "at_xpath: //book (first match only)"
88
+ puts "-" * 80
89
+
90
+ Benchmark.ips do |x|
91
+ x.config(time: 5, warmup: 2)
92
+
93
+ x.report("rxerces") { rxerces_doc.root.at_xpath('//book') }
94
+ x.report("nokogiri") { nokogiri_doc.at_xpath('//book') } if NOKOGIRI_AVAILABLE
95
+
96
+ x.compare!
97
+ end
98
+
99
+ puts
100
+ puts "=" * 80