serialbench 0.1.1 → 0.1.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 +4 -4
- data/.github/workflows/benchmark.yml +13 -5
- data/.github/workflows/docker.yml +35 -9
- data/.github/workflows/rake.yml +15 -0
- data/Gemfile +2 -1
- data/README.adoc +267 -1129
- data/Rakefile +0 -55
- data/config/benchmarks/full.yml +29 -0
- data/config/benchmarks/short.yml +26 -0
- data/config/environments/asdf-ruby-3.2.yml +8 -0
- data/config/environments/asdf-ruby-3.3.yml +8 -0
- data/config/environments/docker-ruby-3.0.yml +9 -0
- data/config/environments/docker-ruby-3.1.yml +9 -0
- data/config/environments/docker-ruby-3.2.yml +9 -0
- data/config/environments/docker-ruby-3.3.yml +9 -0
- data/config/environments/docker-ruby-3.4.yml +9 -0
- data/docker/Dockerfile.alpine +33 -0
- data/docker/{Dockerfile.benchmark → Dockerfile.ubuntu} +4 -3
- data/docker/README.md +2 -2
- data/exe/serialbench +1 -1
- data/lib/serialbench/benchmark_runner.rb +261 -423
- data/lib/serialbench/cli/base_cli.rb +51 -0
- data/lib/serialbench/cli/benchmark_cli.rb +380 -0
- data/lib/serialbench/cli/environment_cli.rb +181 -0
- data/lib/serialbench/cli/resultset_cli.rb +215 -0
- data/lib/serialbench/cli/ruby_build_cli.rb +238 -0
- data/lib/serialbench/cli.rb +58 -601
- data/lib/serialbench/config_manager.rb +140 -0
- data/lib/serialbench/models/benchmark_config.rb +63 -0
- data/lib/serialbench/models/benchmark_result.rb +45 -0
- data/lib/serialbench/models/environment_config.rb +71 -0
- data/lib/serialbench/models/platform.rb +59 -0
- data/lib/serialbench/models/result.rb +53 -0
- data/lib/serialbench/models/result_set.rb +71 -0
- data/lib/serialbench/models/result_store.rb +108 -0
- data/lib/serialbench/models.rb +54 -0
- data/lib/serialbench/ruby_build_manager.rb +153 -0
- data/lib/serialbench/runners/asdf_runner.rb +296 -0
- data/lib/serialbench/runners/base.rb +32 -0
- data/lib/serialbench/runners/docker_runner.rb +142 -0
- data/lib/serialbench/serializers/base_serializer.rb +8 -16
- data/lib/serialbench/serializers/json/base_json_serializer.rb +4 -4
- data/lib/serialbench/serializers/json/json_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/oj_serializer.rb +0 -2
- data/lib/serialbench/serializers/json/yajl_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/base_toml_serializer.rb +5 -3
- data/lib/serialbench/serializers/toml/toml_rb_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/tomlib_serializer.rb +0 -2
- data/lib/serialbench/serializers/toml/tomlrb_serializer.rb +56 -0
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +4 -9
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +0 -2
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +0 -2
- data/lib/serialbench/serializers/xml/oga_serializer.rb +0 -2
- data/lib/serialbench/serializers/xml/ox_serializer.rb +0 -2
- data/lib/serialbench/serializers/xml/rexml_serializer.rb +0 -2
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +5 -1
- data/lib/serialbench/serializers/yaml/syck_serializer.rb +59 -22
- data/lib/serialbench/serializers.rb +23 -6
- data/lib/serialbench/site_generator.rb +105 -0
- data/lib/serialbench/templates/assets/css/benchmark_report.css +535 -0
- data/lib/serialbench/templates/assets/css/format_based.css +526 -0
- data/lib/serialbench/templates/assets/css/themes.css +588 -0
- data/lib/serialbench/templates/assets/js/chart_helpers.js +381 -0
- data/lib/serialbench/templates/assets/js/dashboard.js +796 -0
- data/lib/serialbench/templates/assets/js/navigation.js +142 -0
- data/lib/serialbench/templates/base.liquid +49 -0
- data/lib/serialbench/templates/format_based.liquid +279 -0
- data/lib/serialbench/templates/partials/chart_section.liquid +4 -0
- data/lib/serialbench/version.rb +1 -1
- data/lib/serialbench.rb +2 -31
- data/serialbench.gemspec +4 -1
- metadata +86 -16
- data/config/ci.yml +0 -22
- data/config/full.yml +0 -30
- data/docker/run-benchmarks.sh +0 -356
- data/lib/serialbench/chart_generator.rb +0 -821
- data/lib/serialbench/result_formatter.rb +0 -182
- data/lib/serialbench/result_merger.rb +0 -1201
- data/lib/serialbench/serializers/xml/base_parser.rb +0 -69
- data/lib/serialbench/serializers/xml/libxml_parser.rb +0 -98
- data/lib/serialbench/serializers/xml/nokogiri_parser.rb +0 -111
- data/lib/serialbench/serializers/xml/oga_parser.rb +0 -85
- data/lib/serialbench/serializers/xml/ox_parser.rb +0 -64
- data/lib/serialbench/serializers/xml/rexml_parser.rb +0 -129
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Serialbench
|
4
|
-
module Parsers
|
5
|
-
class BaseParser
|
6
|
-
attr_reader :name, :version
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@name = self.class.name.split('::').last.gsub('Parser', '').downcase
|
10
|
-
@version = detect_version
|
11
|
-
end
|
12
|
-
|
13
|
-
# Parse XML string into document object
|
14
|
-
def parse_dom(xml_string)
|
15
|
-
raise NotImplementedError, 'Subclasses must implement parse_dom'
|
16
|
-
end
|
17
|
-
|
18
|
-
# Parse XML with SAX-style streaming
|
19
|
-
def parse_sax(xml_string, handler = nil)
|
20
|
-
raise NotImplementedError, 'Subclasses must implement parse_sax'
|
21
|
-
end
|
22
|
-
|
23
|
-
# Generate XML string from document object
|
24
|
-
def generate_xml(document, options = {})
|
25
|
-
raise NotImplementedError, 'Subclasses must implement generate_xml'
|
26
|
-
end
|
27
|
-
|
28
|
-
# Check if library is available
|
29
|
-
def available?
|
30
|
-
require library_require_name
|
31
|
-
true
|
32
|
-
rescue LoadError
|
33
|
-
false
|
34
|
-
end
|
35
|
-
|
36
|
-
# Get library features
|
37
|
-
def features
|
38
|
-
{
|
39
|
-
xpath: supports_xpath?,
|
40
|
-
namespaces: supports_namespaces?,
|
41
|
-
validation: supports_validation?,
|
42
|
-
streaming: supports_streaming?
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
def supports_streaming?
|
47
|
-
false
|
48
|
-
end
|
49
|
-
|
50
|
-
protected
|
51
|
-
|
52
|
-
def detect_version
|
53
|
-
'unknown'
|
54
|
-
end
|
55
|
-
|
56
|
-
def supports_xpath?
|
57
|
-
false
|
58
|
-
end
|
59
|
-
|
60
|
-
def supports_namespaces?
|
61
|
-
true
|
62
|
-
end
|
63
|
-
|
64
|
-
def supports_validation?
|
65
|
-
false
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_parser'
|
4
|
-
|
5
|
-
module Serialbench
|
6
|
-
module Parsers
|
7
|
-
class LibxmlParser < BaseParser
|
8
|
-
def parse_dom(xml_string)
|
9
|
-
require 'libxml'
|
10
|
-
LibXML::XML::Document.string(xml_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse_sax(xml_string, handler = nil)
|
14
|
-
require 'libxml'
|
15
|
-
handler ||= LibxmlSaxHandler.new
|
16
|
-
parser = LibXML::XML::SaxParser.string(xml_string)
|
17
|
-
parser.callbacks = handler
|
18
|
-
parser.parse
|
19
|
-
handler
|
20
|
-
end
|
21
|
-
|
22
|
-
def generate_xml(document, options = {})
|
23
|
-
require 'libxml'
|
24
|
-
document.to_s
|
25
|
-
end
|
26
|
-
|
27
|
-
def supports_streaming?
|
28
|
-
true
|
29
|
-
end
|
30
|
-
|
31
|
-
# SAX handler for LibXML
|
32
|
-
class LibxmlSaxHandler
|
33
|
-
attr_reader :elements_processed, :text_nodes_processed
|
34
|
-
|
35
|
-
def initialize
|
36
|
-
@elements_processed = 0
|
37
|
-
@text_nodes_processed = 0
|
38
|
-
# Include LibXML callbacks if available
|
39
|
-
return unless defined?(::LibXML)
|
40
|
-
|
41
|
-
return if self.class.ancestors.include?(::LibXML::XML::SaxParser::Callbacks)
|
42
|
-
|
43
|
-
self.class.send(:include,
|
44
|
-
::LibXML::XML::SaxParser::Callbacks)
|
45
|
-
end
|
46
|
-
|
47
|
-
def on_start_document
|
48
|
-
# Document processing started
|
49
|
-
end
|
50
|
-
|
51
|
-
def on_end_document
|
52
|
-
# Document processing complete
|
53
|
-
end
|
54
|
-
|
55
|
-
def on_start_element(element, attributes)
|
56
|
-
@elements_processed += 1
|
57
|
-
end
|
58
|
-
|
59
|
-
def on_characters(chars)
|
60
|
-
@text_nodes_processed += 1 unless chars.strip.empty?
|
61
|
-
end
|
62
|
-
|
63
|
-
def on_end_element(element)
|
64
|
-
# Element processing complete
|
65
|
-
end
|
66
|
-
|
67
|
-
def on_error(msg)
|
68
|
-
# Handle parsing errors
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
protected
|
73
|
-
|
74
|
-
def library_require_name
|
75
|
-
'libxml'
|
76
|
-
end
|
77
|
-
|
78
|
-
def detect_version
|
79
|
-
require 'libxml'
|
80
|
-
LibXML::XML::VERSION
|
81
|
-
rescue LoadError
|
82
|
-
'not available'
|
83
|
-
end
|
84
|
-
|
85
|
-
def supports_xpath?
|
86
|
-
true
|
87
|
-
end
|
88
|
-
|
89
|
-
def supports_namespaces?
|
90
|
-
true
|
91
|
-
end
|
92
|
-
|
93
|
-
def supports_validation?
|
94
|
-
true
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_parser'
|
4
|
-
|
5
|
-
module Serialbench
|
6
|
-
module Parsers
|
7
|
-
class NokogiriParser < BaseParser
|
8
|
-
def parse_dom(xml_string)
|
9
|
-
require 'nokogiri'
|
10
|
-
Nokogiri::XML(xml_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse_sax(xml_string, handler = nil)
|
14
|
-
require 'nokogiri'
|
15
|
-
handler ||= NokogiriSaxHandler.new
|
16
|
-
parser = Nokogiri::XML::SAX::Parser.new(handler)
|
17
|
-
parser.parse(xml_string)
|
18
|
-
handler
|
19
|
-
end
|
20
|
-
|
21
|
-
def generate_xml(document, options = {})
|
22
|
-
require 'nokogiri'
|
23
|
-
indent = options.fetch(:indent, 0)
|
24
|
-
if indent > 0
|
25
|
-
document.to_xml(indent: indent)
|
26
|
-
else
|
27
|
-
document.to_xml
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def supports_streaming?
|
32
|
-
true
|
33
|
-
end
|
34
|
-
|
35
|
-
# SAX handler for Nokogiri
|
36
|
-
class NokogiriSaxHandler < (defined?(::Nokogiri) ? ::Nokogiri::XML::SAX::Document : Object)
|
37
|
-
attr_reader :elements_processed, :text_nodes_processed
|
38
|
-
|
39
|
-
def initialize
|
40
|
-
@elements_processed = 0
|
41
|
-
@text_nodes_processed = 0
|
42
|
-
end
|
43
|
-
|
44
|
-
def start_document
|
45
|
-
# Document processing started
|
46
|
-
end
|
47
|
-
|
48
|
-
def end_document
|
49
|
-
# Document processing complete
|
50
|
-
end
|
51
|
-
|
52
|
-
def start_element(name, attrs = [])
|
53
|
-
@elements_processed += 1
|
54
|
-
end
|
55
|
-
|
56
|
-
def start_element_namespace(name, attrs = [], prefix = nil, uri = nil, ns = [])
|
57
|
-
@elements_processed += 1
|
58
|
-
end
|
59
|
-
|
60
|
-
def characters(string)
|
61
|
-
@text_nodes_processed += 1 unless string.strip.empty?
|
62
|
-
end
|
63
|
-
|
64
|
-
def end_element(name)
|
65
|
-
# Element processing complete
|
66
|
-
end
|
67
|
-
|
68
|
-
def end_element_namespace(name, prefix = nil, uri = nil)
|
69
|
-
# Element processing complete
|
70
|
-
end
|
71
|
-
|
72
|
-
def error(message)
|
73
|
-
# Handle parsing errors
|
74
|
-
end
|
75
|
-
|
76
|
-
def warning(message)
|
77
|
-
# Handle parsing warnings
|
78
|
-
end
|
79
|
-
|
80
|
-
def xmldecl(version, encoding, standalone)
|
81
|
-
# Handle XML declaration
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
protected
|
86
|
-
|
87
|
-
def library_require_name
|
88
|
-
'nokogiri'
|
89
|
-
end
|
90
|
-
|
91
|
-
def detect_version
|
92
|
-
require 'nokogiri'
|
93
|
-
Nokogiri::VERSION
|
94
|
-
rescue LoadError
|
95
|
-
'not available'
|
96
|
-
end
|
97
|
-
|
98
|
-
def supports_xpath?
|
99
|
-
true
|
100
|
-
end
|
101
|
-
|
102
|
-
def supports_namespaces?
|
103
|
-
true
|
104
|
-
end
|
105
|
-
|
106
|
-
def supports_validation?
|
107
|
-
true
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_parser'
|
4
|
-
|
5
|
-
module Serialbench
|
6
|
-
module Parsers
|
7
|
-
class OgaParser < BaseParser
|
8
|
-
def parse_dom(xml_string)
|
9
|
-
require 'oga'
|
10
|
-
Oga.parse_xml(xml_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse_sax(xml_string, handler = nil)
|
14
|
-
require 'oga'
|
15
|
-
handler ||= DefaultSaxHandler.new
|
16
|
-
Oga.sax_parse_xml(xml_string, handler)
|
17
|
-
handler
|
18
|
-
end
|
19
|
-
|
20
|
-
def generate_xml(document, options = {})
|
21
|
-
require 'oga'
|
22
|
-
document.to_xml
|
23
|
-
end
|
24
|
-
|
25
|
-
def supports_streaming?
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
|
-
protected
|
30
|
-
|
31
|
-
def library_require_name
|
32
|
-
'oga'
|
33
|
-
end
|
34
|
-
|
35
|
-
def detect_version
|
36
|
-
require 'oga'
|
37
|
-
Oga::VERSION
|
38
|
-
rescue LoadError
|
39
|
-
'not available'
|
40
|
-
end
|
41
|
-
|
42
|
-
def supports_xpath?
|
43
|
-
true
|
44
|
-
end
|
45
|
-
|
46
|
-
def supports_namespaces?
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
def supports_validation?
|
51
|
-
false
|
52
|
-
end
|
53
|
-
|
54
|
-
# Default SAX handler for Oga
|
55
|
-
class DefaultSaxHandler
|
56
|
-
attr_reader :elements_processed, :text_nodes_processed
|
57
|
-
|
58
|
-
def initialize
|
59
|
-
@elements_processed = 0
|
60
|
-
@text_nodes_processed = 0
|
61
|
-
end
|
62
|
-
|
63
|
-
def on_element(namespace, name, attributes = {})
|
64
|
-
@elements_processed += 1
|
65
|
-
end
|
66
|
-
|
67
|
-
def on_text(text)
|
68
|
-
@text_nodes_processed += 1 unless text.strip.empty?
|
69
|
-
end
|
70
|
-
|
71
|
-
def on_cdata(text)
|
72
|
-
@text_nodes_processed += 1 unless text.strip.empty?
|
73
|
-
end
|
74
|
-
|
75
|
-
def on_comment(text)
|
76
|
-
# Handle comments
|
77
|
-
end
|
78
|
-
|
79
|
-
def on_processing_instruction(name, text)
|
80
|
-
# Handle processing instructions
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_parser'
|
4
|
-
|
5
|
-
module Serialbench
|
6
|
-
module Parsers
|
7
|
-
class OxParser < BaseParser
|
8
|
-
def parse_dom(xml_string)
|
9
|
-
require 'ox'
|
10
|
-
Ox.parse(xml_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse_sax(xml_string, handler = nil)
|
14
|
-
require 'ox'
|
15
|
-
require 'stringio'
|
16
|
-
handler ||= OxSaxHandler.new
|
17
|
-
Ox.sax_parse(handler, StringIO.new(xml_string))
|
18
|
-
handler
|
19
|
-
end
|
20
|
-
|
21
|
-
def generate_xml(document, options = {})
|
22
|
-
require 'ox'
|
23
|
-
indent = options.fetch(:indent, 0)
|
24
|
-
Ox.dump(document, indent: indent)
|
25
|
-
end
|
26
|
-
|
27
|
-
def supports_streaming?
|
28
|
-
true
|
29
|
-
end
|
30
|
-
|
31
|
-
# SAX handler for Ox
|
32
|
-
class OxSaxHandler < (defined?(::Ox) ? ::Ox::Sax : Object)
|
33
|
-
attr_reader :elements_processed, :text_nodes_processed
|
34
|
-
|
35
|
-
def initialize
|
36
|
-
@elements_processed = 0
|
37
|
-
@text_nodes_processed = 0
|
38
|
-
end
|
39
|
-
|
40
|
-
def start_element(name)
|
41
|
-
@elements_processed += 1
|
42
|
-
end
|
43
|
-
|
44
|
-
def end_element(name)
|
45
|
-
# Process end element
|
46
|
-
end
|
47
|
-
|
48
|
-
def text(value)
|
49
|
-
@text_nodes_processed += 1 unless value.strip.empty?
|
50
|
-
end
|
51
|
-
|
52
|
-
def attr(name, value)
|
53
|
-
# Process attribute
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
protected
|
58
|
-
|
59
|
-
def library_require_name
|
60
|
-
'ox'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base_parser'
|
4
|
-
|
5
|
-
module Serialbench
|
6
|
-
module Parsers
|
7
|
-
class RexmlParser < BaseParser
|
8
|
-
def parse_dom(xml_string)
|
9
|
-
require 'rexml/document'
|
10
|
-
REXML::Document.new(xml_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse_sax(xml_string, handler = nil)
|
14
|
-
require 'rexml/parsers/sax2parser'
|
15
|
-
handler ||= DefaultSaxHandler.new
|
16
|
-
parser = REXML::Parsers::SAX2Parser.new(xml_string)
|
17
|
-
parser.listen(handler)
|
18
|
-
parser.parse
|
19
|
-
handler
|
20
|
-
rescue LoadError
|
21
|
-
# Fallback if SAX2 is not available
|
22
|
-
require 'rexml/document'
|
23
|
-
doc = REXML::Document.new(xml_string)
|
24
|
-
handler ||= DefaultSaxHandler.new
|
25
|
-
# Simulate SAX events by walking the document
|
26
|
-
doc.root.each_recursive do |element|
|
27
|
-
if element.is_a?(REXML::Element)
|
28
|
-
handler.start_element(nil, element.name, element.name, element.attributes)
|
29
|
-
handler.end_element(nil, element.name, element.name)
|
30
|
-
elsif element.is_a?(REXML::Text)
|
31
|
-
handler.characters(element.to_s)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
handler
|
35
|
-
end
|
36
|
-
|
37
|
-
def generate_xml(document, options = {})
|
38
|
-
require 'rexml/document'
|
39
|
-
indent = options.fetch(:indent, 0)
|
40
|
-
output = String.new
|
41
|
-
if indent > 0
|
42
|
-
document.write(output, indent)
|
43
|
-
else
|
44
|
-
document.write(output)
|
45
|
-
end
|
46
|
-
output
|
47
|
-
end
|
48
|
-
|
49
|
-
def supports_streaming?
|
50
|
-
true
|
51
|
-
end
|
52
|
-
|
53
|
-
protected
|
54
|
-
|
55
|
-
def library_require_name
|
56
|
-
'rexml/document'
|
57
|
-
end
|
58
|
-
|
59
|
-
def detect_version
|
60
|
-
require 'rexml/rexml'
|
61
|
-
REXML::VERSION
|
62
|
-
rescue LoadError, NameError
|
63
|
-
'built-in'
|
64
|
-
end
|
65
|
-
|
66
|
-
def supports_xpath?
|
67
|
-
true
|
68
|
-
end
|
69
|
-
|
70
|
-
def supports_namespaces?
|
71
|
-
true
|
72
|
-
end
|
73
|
-
|
74
|
-
def supports_validation?
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Default SAX handler for REXML - only define if REXML SAX is available
|
80
|
-
begin
|
81
|
-
require 'rexml/sax2listener'
|
82
|
-
|
83
|
-
class DefaultSaxHandler
|
84
|
-
include ::REXML::SAX2Listener
|
85
|
-
|
86
|
-
attr_reader :elements_processed, :text_nodes_processed
|
87
|
-
|
88
|
-
def initialize
|
89
|
-
@elements_processed = 0
|
90
|
-
@text_nodes_processed = 0
|
91
|
-
end
|
92
|
-
|
93
|
-
def start_element(uri, localname, qname, attributes)
|
94
|
-
@elements_processed += 1
|
95
|
-
end
|
96
|
-
|
97
|
-
def characters(text)
|
98
|
-
@text_nodes_processed += 1 unless text.strip.empty?
|
99
|
-
end
|
100
|
-
|
101
|
-
def end_element(uri, localname, qname)
|
102
|
-
# Element processing complete
|
103
|
-
end
|
104
|
-
end
|
105
|
-
rescue LoadError
|
106
|
-
# SAX2Listener not available, define a simple handler
|
107
|
-
class DefaultSaxHandler
|
108
|
-
attr_reader :elements_processed, :text_nodes_processed
|
109
|
-
|
110
|
-
def initialize
|
111
|
-
@elements_processed = 0
|
112
|
-
@text_nodes_processed = 0
|
113
|
-
end
|
114
|
-
|
115
|
-
def start_element(uri, localname, qname, attributes)
|
116
|
-
@elements_processed += 1
|
117
|
-
end
|
118
|
-
|
119
|
-
def characters(text)
|
120
|
-
@text_nodes_processed += 1 unless text.strip.empty?
|
121
|
-
end
|
122
|
-
|
123
|
-
def end_element(uri, localname, qname)
|
124
|
-
# Element processing complete
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|