saxerator 0.9.5 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +9 -12
- data/Gemfile +4 -2
- data/README.md +23 -3
- data/Rakefile +12 -1
- data/benchmark/benchmark.rb +54 -12
- data/benchmark/generate_sample_file.rb +1 -1
- data/lib/saxerator.rb +2 -3
- data/lib/saxerator/adapters/nokogiri.rb +39 -0
- data/lib/saxerator/adapters/ox.rb +69 -0
- data/lib/saxerator/adapters/rexml.rb +42 -0
- data/lib/saxerator/builder.rb +2 -2
- data/lib/saxerator/builder/array_element.rb +8 -2
- data/lib/saxerator/builder/empty_element.rb +2 -3
- data/lib/saxerator/builder/hash_builder.rb +8 -7
- data/lib/saxerator/builder/hash_element.rb +7 -8
- data/lib/saxerator/builder/string_element.rb +5 -7
- data/lib/saxerator/builder/xml_builder.rb +11 -7
- data/lib/saxerator/configuration.rb +27 -7
- data/lib/saxerator/document_fragment.rb +3 -5
- data/lib/saxerator/dsl.rb +6 -5
- data/lib/saxerator/full_document.rb +1 -1
- data/lib/saxerator/latches/abstract_latch.rb +1 -1
- data/lib/saxerator/latches/at_depth.rb +2 -2
- data/lib/saxerator/latches/child_of.rb +9 -14
- data/lib/saxerator/latches/for_tags.rb +2 -2
- data/lib/saxerator/latches/with_attributes.rb +2 -2
- data/lib/saxerator/latches/within.rb +6 -10
- data/lib/saxerator/parser/accumulator.rb +6 -9
- data/lib/saxerator/parser/latched_accumulator.rb +4 -49
- data/lib/saxerator/sax_handler.rb +9 -0
- data/lib/saxerator/version.rb +1 -1
- data/saxerator.gemspec +5 -2
- data/spec/lib/builder/hash_builder_spec.rb +25 -19
- data/spec/lib/builder/xml_builder_spec.rb +7 -26
- data/spec/lib/dsl/all_spec.rb +11 -6
- data/spec/lib/dsl/at_depth_spec.rb +10 -8
- data/spec/lib/dsl/child_of_spec.rb +6 -6
- data/spec/lib/dsl/for_tag_spec.rb +3 -3
- data/spec/lib/dsl/for_tags_spec.rb +5 -5
- data/spec/lib/dsl/with_attribute_spec.rb +4 -4
- data/spec/lib/dsl/with_attributes_spec.rb +8 -7
- data/spec/lib/dsl/within_spec.rb +6 -5
- data/spec/lib/saxerator_spec.rb +70 -58
- data/spec/spec_helper.rb +24 -3
- data/spec/support/fixture_file.rb +1 -1
- metadata +39 -5
@@ -1,21 +1,20 @@
|
|
1
1
|
require 'saxerator/builder/array_element'
|
2
|
+
require 'delegate'
|
2
3
|
|
3
4
|
module Saxerator
|
4
5
|
module Builder
|
5
|
-
class HashElement < Hash
|
6
|
+
class HashElement < DelegateClass(Hash)
|
6
7
|
attr_accessor :attributes
|
7
8
|
attr_accessor :name
|
8
9
|
|
9
|
-
def initialize(name, attributes)
|
10
|
-
|
11
|
-
|
10
|
+
def initialize(name = nil, attributes = nil)
|
11
|
+
@name = name
|
12
|
+
@attributes = attributes
|
13
|
+
super({})
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_a
|
15
|
-
|
16
|
-
array.name = name
|
17
|
-
array.concat super
|
18
|
-
array
|
17
|
+
ArrayElement.new(super, name)
|
19
18
|
end
|
20
19
|
|
21
20
|
def to_h; self end
|
@@ -1,22 +1,20 @@
|
|
1
1
|
require 'saxerator/builder/array_element'
|
2
|
+
require 'delegate'
|
2
3
|
|
3
4
|
module Saxerator
|
4
5
|
module Builder
|
5
|
-
class StringElement < String
|
6
|
+
class StringElement < DelegateClass(String)
|
6
7
|
attr_accessor :attributes
|
7
8
|
attr_accessor :name
|
8
9
|
|
9
10
|
def initialize(str, name, attributes)
|
10
|
-
|
11
|
-
|
11
|
+
@name = name
|
12
|
+
@attributes = attributes
|
12
13
|
super(str)
|
13
14
|
end
|
14
15
|
|
15
16
|
def to_a
|
16
|
-
|
17
|
-
array << self
|
18
|
-
array.name = name
|
19
|
-
array
|
17
|
+
ArrayElement.new(self, name)
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
1
3
|
module Saxerator
|
2
4
|
module Builder
|
3
5
|
class XmlBuilder
|
@@ -17,20 +19,22 @@ module Saxerator
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def to_xml(builder)
|
22
|
+
element = REXML::Element.new(name, nil, attribute_quote: :quote)
|
23
|
+
element.add_attributes(@attributes)
|
20
24
|
if @text
|
21
|
-
|
25
|
+
element.add_text(@children.join)
|
22
26
|
else
|
23
|
-
|
24
|
-
@children.each { |child| child.to_xml(xml) }
|
25
|
-
end
|
27
|
+
@children.each { |child| child.to_xml(element) }
|
26
28
|
end
|
29
|
+
builder.elements << element
|
27
30
|
end
|
28
31
|
|
29
32
|
def block_variable
|
30
|
-
builder =
|
33
|
+
builder = REXML::Document.new
|
34
|
+
builder << REXML::XMLDecl.new('1.0', 'UTF-8')
|
31
35
|
to_xml(builder)
|
32
|
-
builder
|
36
|
+
builder
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
36
|
-
end
|
40
|
+
end
|
@@ -1,26 +1,45 @@
|
|
1
1
|
module Saxerator
|
2
2
|
class Configuration
|
3
|
-
attr_reader :output_type
|
4
3
|
attr_writer :hash_key_generator
|
4
|
+
attr_reader :output_type
|
5
|
+
|
6
|
+
ADAPTER_TYPES = [:ox, :nokogiri, :rexml]
|
5
7
|
|
6
8
|
def initialize
|
9
|
+
@adapter = :rexml
|
7
10
|
@output_type = :hash
|
8
11
|
@put_attributes_in_hash = false
|
9
12
|
@ignore_namespaces = false
|
10
13
|
end
|
11
14
|
|
15
|
+
def adapter=(name)
|
16
|
+
unless ADAPTER_TYPES.include?(name)
|
17
|
+
raise ArgumentError, "Unknown adapter '#{name.inspect}'"
|
18
|
+
end
|
19
|
+
@adapter = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def adapter
|
23
|
+
require "saxerator/adapters/#{@adapter}"
|
24
|
+
Saxerator::Adapters.const_get(@adapter.to_s.capitalize, false)
|
25
|
+
end
|
26
|
+
|
12
27
|
def output_type=(val)
|
13
|
-
raise ArgumentError
|
28
|
+
raise ArgumentError, "Unknown output_type '#{val.inspect}'" unless Builder.valid?(val)
|
14
29
|
@output_type = val
|
15
30
|
raise_error_if_using_put_attributes_in_hash_with_xml
|
16
31
|
end
|
17
32
|
|
33
|
+
def output_type
|
34
|
+
@_output_type ||= Builder.to_class(@output_type)
|
35
|
+
end
|
36
|
+
|
18
37
|
def generate_key_for(val)
|
19
38
|
hash_key_generator.call val
|
20
39
|
end
|
21
40
|
|
22
41
|
def hash_key_normalizer
|
23
|
-
@hash_key_normalizer ||=
|
42
|
+
@hash_key_normalizer ||= -> (x) { x.to_s }
|
24
43
|
end
|
25
44
|
|
26
45
|
def hash_key_generator
|
@@ -28,15 +47,15 @@ module Saxerator
|
|
28
47
|
end
|
29
48
|
|
30
49
|
def symbolize_keys!
|
31
|
-
@hash_key_generator =
|
50
|
+
@hash_key_generator = -> (x) { hash_key_normalizer.call(x).to_sym }
|
32
51
|
end
|
33
52
|
|
34
53
|
def strip_namespaces!(*namespaces)
|
35
54
|
if namespaces.any?
|
36
55
|
matching_group = namespaces.join('|')
|
37
|
-
@hash_key_normalizer =
|
56
|
+
@hash_key_normalizer = -> (x) { x.to_s.gsub(/(#{matching_group}):/, '') }
|
38
57
|
else
|
39
|
-
@hash_key_normalizer =
|
58
|
+
@hash_key_normalizer = -> (x) { x.to_s.gsub(/\w+:/, '') }
|
40
59
|
end
|
41
60
|
end
|
42
61
|
|
@@ -59,7 +78,8 @@ module Saxerator
|
|
59
78
|
|
60
79
|
def raise_error_if_using_put_attributes_in_hash_with_xml
|
61
80
|
if @output_type != :hash && @put_attributes_in_hash
|
62
|
-
raise ArgumentError
|
81
|
+
raise ArgumentError, "put_attributes_in_hash! is only valid \
|
82
|
+
when using output_type = :hash (the default)'"
|
63
83
|
end
|
64
84
|
end
|
65
85
|
end
|
@@ -10,13 +10,11 @@ module Saxerator
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def each(&block)
|
13
|
-
reader = Parser::LatchedAccumulator.new(@config, @latches, block)
|
14
|
-
parser = Nokogiri::XML::SAX::Parser.new(reader)
|
15
|
-
|
16
13
|
# Always have to start at the beginning of a File
|
17
14
|
@source.rewind if @source.respond_to?(:rewind)
|
18
15
|
|
19
|
-
|
16
|
+
reader = Parser::LatchedAccumulator.new(@config, @latches, block)
|
17
|
+
@config.adapter.parse(@source, reader)
|
20
18
|
end
|
21
19
|
end
|
22
|
-
end
|
20
|
+
end
|
data/lib/saxerator/dsl.rb
CHANGED
@@ -5,7 +5,7 @@ module Saxerator
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def for_tags(tags)
|
8
|
-
raise ArgumentError
|
8
|
+
raise ArgumentError, '#for_tags requires an Array argument' unless tags.is_a? Array
|
9
9
|
specify Latches::ForTags.new(tags.map(&:to_s))
|
10
10
|
end
|
11
11
|
|
@@ -22,21 +22,22 @@ module Saxerator
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def with_attribute(name, value = nil)
|
25
|
-
specify Latches::WithAttributes.new(
|
25
|
+
specify Latches::WithAttributes.new(name.to_s => !value.nil? ? value.to_s : nil)
|
26
26
|
end
|
27
27
|
|
28
28
|
def with_attributes(attrs)
|
29
29
|
if attrs.is_a? Array
|
30
|
-
attrs = Hash[attrs.map { |k| [k, nil]}]
|
30
|
+
attrs = Hash[attrs.map { |k| [k, nil] }]
|
31
31
|
elsif attrs.is_a? Hash
|
32
|
-
attrs = Hash[attrs.map{ |k, v| [k.to_s, v ? v.to_s : v]}]
|
32
|
+
attrs = Hash[attrs.map { |k, v| [k.to_s, v ? v.to_s : v] }]
|
33
33
|
else
|
34
|
-
raise ArgumentError
|
34
|
+
raise ArgumentError, 'attributes should be a Hash or Array'
|
35
35
|
end
|
36
36
|
specify Latches::WithAttributes.new(attrs)
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
40
|
+
|
40
41
|
def specify(predicate)
|
41
42
|
DocumentFragment.new(@source, @config, @latches + [predicate])
|
42
43
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Saxerator
|
2
2
|
module Latches
|
3
|
-
class AtDepth <
|
3
|
+
class AtDepth < ::Saxerator::SaxHandler
|
4
4
|
def initialize(depth)
|
5
5
|
@target_depth = depth
|
6
6
|
@current_depth = -1
|
@@ -19,4 +19,4 @@ module Saxerator
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
@@ -1,25 +1,20 @@
|
|
1
1
|
module Saxerator
|
2
2
|
module Latches
|
3
|
-
class ChildOf <
|
3
|
+
class ChildOf < ::Saxerator::SaxHandler
|
4
4
|
def initialize(name)
|
5
5
|
@name = name
|
6
6
|
@depths = []
|
7
7
|
end
|
8
8
|
|
9
|
-
def start_element
|
10
|
-
if depth_within_element > 0
|
11
|
-
|
12
|
-
end
|
13
|
-
if @name == name
|
14
|
-
@depths.push 1
|
15
|
-
end
|
9
|
+
def start_element(name, _)
|
10
|
+
increment_depth(1) if depth_within_element > 0
|
11
|
+
@depths.push 1 if @name == name
|
16
12
|
end
|
17
13
|
|
18
|
-
def end_element
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
14
|
+
def end_element(_)
|
15
|
+
return unless depth_within_element > 0
|
16
|
+
increment_depth(-1)
|
17
|
+
@depths.pop if @depths[-1] == 0
|
23
18
|
end
|
24
19
|
|
25
20
|
def open?
|
@@ -31,7 +26,7 @@ module Saxerator
|
|
31
26
|
end
|
32
27
|
|
33
28
|
def depth_within_element
|
34
|
-
|
29
|
+
!@depths.empty? ? @depths[-1] : 0
|
35
30
|
end
|
36
31
|
end
|
37
32
|
end
|
@@ -7,7 +7,7 @@ module Saxerator
|
|
7
7
|
@attrs = attrs
|
8
8
|
end
|
9
9
|
|
10
|
-
def start_element
|
10
|
+
def start_element(_, attributes)
|
11
11
|
attributes = Hash[attributes]
|
12
12
|
if @attrs.all? { |k, v| attributes[k] && (v.nil? || attributes[k] == v) }
|
13
13
|
open
|
@@ -16,7 +16,7 @@ module Saxerator
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def end_element
|
19
|
+
def end_element(_)
|
20
20
|
close
|
21
21
|
end
|
22
22
|
end
|
@@ -1,21 +1,17 @@
|
|
1
1
|
module Saxerator
|
2
2
|
module Latches
|
3
|
-
class Within <
|
3
|
+
class Within < ::Saxerator::SaxHandler
|
4
4
|
def initialize(name)
|
5
5
|
@name = name
|
6
6
|
@depth_within_element = 0
|
7
7
|
end
|
8
8
|
|
9
|
-
def start_element
|
10
|
-
if name == @name || @depth_within_element > 0
|
11
|
-
@depth_within_element += 1
|
12
|
-
end
|
9
|
+
def start_element(name, _)
|
10
|
+
@depth_within_element += 1 if name == @name || @depth_within_element > 0
|
13
11
|
end
|
14
12
|
|
15
|
-
def end_element
|
16
|
-
if @depth_within_element > 0
|
17
|
-
@depth_within_element -= 1
|
18
|
-
end
|
13
|
+
def end_element(_)
|
14
|
+
@depth_within_element -= 1 if @depth_within_element > 0
|
19
15
|
end
|
20
16
|
|
21
17
|
def open?
|
@@ -23,4 +19,4 @@ module Saxerator
|
|
23
19
|
end
|
24
20
|
end
|
25
21
|
end
|
26
|
-
end
|
22
|
+
end
|
@@ -1,35 +1,32 @@
|
|
1
1
|
module Saxerator
|
2
2
|
module Parser
|
3
|
-
class Accumulator <
|
3
|
+
class Accumulator < ::Saxerator::SaxHandler
|
4
4
|
def initialize(config, block)
|
5
5
|
@stack = []
|
6
6
|
@config = config
|
7
7
|
@block = block
|
8
|
-
@builder = Builder.to_class(@config.output_type)
|
9
8
|
end
|
10
9
|
|
11
10
|
def start_element(name, attrs = [])
|
12
|
-
@stack.push @
|
11
|
+
@stack.push @config.output_type.new(@config, name, Hash[attrs])
|
13
12
|
end
|
14
13
|
|
15
14
|
def end_element(_)
|
16
15
|
if @stack.size > 1
|
17
16
|
last = @stack.pop
|
18
|
-
@stack.
|
17
|
+
@stack[-1].add_node last
|
19
18
|
else
|
20
19
|
@block.call(@stack.pop.block_variable)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
23
|
def characters(string)
|
25
|
-
@stack.
|
24
|
+
@stack[-1].add_node(string) unless string.strip.length == 0
|
26
25
|
end
|
27
26
|
|
28
|
-
alias cdata_block characters
|
29
|
-
|
30
27
|
def accumulating?
|
31
|
-
|
28
|
+
!@stack.empty?
|
32
29
|
end
|
33
30
|
end
|
34
31
|
end
|
35
|
-
end
|
32
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Saxerator
|
2
2
|
module Parser
|
3
|
-
class LatchedAccumulator <
|
3
|
+
class LatchedAccumulator < ::Saxerator::SaxHandler
|
4
4
|
def initialize(config, latches, block)
|
5
5
|
@latches = latches
|
6
6
|
@accumulator = Accumulator.new(config, block)
|
@@ -9,22 +9,7 @@ module Saxerator
|
|
9
9
|
|
10
10
|
def check_latches_and_passthrough(method, *args)
|
11
11
|
@latches.each { |latch| latch.send(method, *args) }
|
12
|
-
if @accumulator.accumulating? ||
|
13
|
-
@latches.all? { |latch| latch.open? }
|
14
|
-
@accumulator.send(method, *args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def xmldecl(version, encoding, standalone)
|
19
|
-
check_latches_and_passthrough(:xmldecl, version, encoding, standalone)
|
20
|
-
end
|
21
|
-
|
22
|
-
def start_document
|
23
|
-
check_latches_and_passthrough(:start_document)
|
24
|
-
end
|
25
|
-
|
26
|
-
def end_document
|
27
|
-
check_latches_and_passthrough(:end_document)
|
12
|
+
@accumulator.send(method, *args) if @accumulator.accumulating? || @latches.all?(&:open?)
|
28
13
|
end
|
29
14
|
|
30
15
|
def start_element(name, attrs = [])
|
@@ -35,42 +20,12 @@ module Saxerator
|
|
35
20
|
check_latches_and_passthrough(:end_element, name)
|
36
21
|
end
|
37
22
|
|
38
|
-
def start_element_namespace(name, attrs = [], prefix = nil, uri = nil, ns = [])
|
39
|
-
if @ignore_namespaces
|
40
|
-
prefix, uri, ns = nil, nil, []
|
41
|
-
attrs.each do |attr|
|
42
|
-
attr.prefix = nil if attr.respond_to? :prefix
|
43
|
-
attr.uri = nil if attr.respond_to? :uri
|
44
|
-
end
|
45
|
-
end
|
46
|
-
check_latches_and_passthrough(:start_element_namespace, name, attrs, prefix, uri, ns)
|
47
|
-
end
|
48
|
-
|
49
|
-
def end_element_namespace(name, prefix = nil, uri = nil)
|
50
|
-
if @ignore_namespaces
|
51
|
-
prefix, uri = nil
|
52
|
-
end
|
53
|
-
check_latches_and_passthrough(:end_element_namespace, name, prefix, uri)
|
54
|
-
end
|
55
|
-
|
56
23
|
def characters(string)
|
57
24
|
check_latches_and_passthrough(:characters, string)
|
58
25
|
end
|
59
26
|
|
60
|
-
def
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
def warning(string)
|
65
|
-
check_latches_and_passthrough(:warning, string)
|
66
|
-
end
|
67
|
-
|
68
|
-
def error(string)
|
69
|
-
check_latches_and_passthrough(:error, string)
|
70
|
-
end
|
71
|
-
|
72
|
-
def cdata_block(string)
|
73
|
-
check_latches_and_passthrough(:cdata_block, string)
|
27
|
+
def ignore_namespaces?
|
28
|
+
@ignore_namespaces
|
74
29
|
end
|
75
30
|
end
|
76
31
|
end
|