saxerator 0.9.5 → 0.9.8

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +9 -12
  4. data/Gemfile +4 -2
  5. data/README.md +23 -3
  6. data/Rakefile +12 -1
  7. data/benchmark/benchmark.rb +54 -12
  8. data/benchmark/generate_sample_file.rb +1 -1
  9. data/lib/saxerator.rb +2 -3
  10. data/lib/saxerator/adapters/nokogiri.rb +39 -0
  11. data/lib/saxerator/adapters/ox.rb +69 -0
  12. data/lib/saxerator/adapters/rexml.rb +42 -0
  13. data/lib/saxerator/builder.rb +2 -2
  14. data/lib/saxerator/builder/array_element.rb +8 -2
  15. data/lib/saxerator/builder/empty_element.rb +2 -3
  16. data/lib/saxerator/builder/hash_builder.rb +8 -7
  17. data/lib/saxerator/builder/hash_element.rb +7 -8
  18. data/lib/saxerator/builder/string_element.rb +5 -7
  19. data/lib/saxerator/builder/xml_builder.rb +11 -7
  20. data/lib/saxerator/configuration.rb +27 -7
  21. data/lib/saxerator/document_fragment.rb +3 -5
  22. data/lib/saxerator/dsl.rb +6 -5
  23. data/lib/saxerator/full_document.rb +1 -1
  24. data/lib/saxerator/latches/abstract_latch.rb +1 -1
  25. data/lib/saxerator/latches/at_depth.rb +2 -2
  26. data/lib/saxerator/latches/child_of.rb +9 -14
  27. data/lib/saxerator/latches/for_tags.rb +2 -2
  28. data/lib/saxerator/latches/with_attributes.rb +2 -2
  29. data/lib/saxerator/latches/within.rb +6 -10
  30. data/lib/saxerator/parser/accumulator.rb +6 -9
  31. data/lib/saxerator/parser/latched_accumulator.rb +4 -49
  32. data/lib/saxerator/sax_handler.rb +9 -0
  33. data/lib/saxerator/version.rb +1 -1
  34. data/saxerator.gemspec +5 -2
  35. data/spec/lib/builder/hash_builder_spec.rb +25 -19
  36. data/spec/lib/builder/xml_builder_spec.rb +7 -26
  37. data/spec/lib/dsl/all_spec.rb +11 -6
  38. data/spec/lib/dsl/at_depth_spec.rb +10 -8
  39. data/spec/lib/dsl/child_of_spec.rb +6 -6
  40. data/spec/lib/dsl/for_tag_spec.rb +3 -3
  41. data/spec/lib/dsl/for_tags_spec.rb +5 -5
  42. data/spec/lib/dsl/with_attribute_spec.rb +4 -4
  43. data/spec/lib/dsl/with_attributes_spec.rb +8 -7
  44. data/spec/lib/dsl/within_spec.rb +6 -5
  45. data/spec/lib/saxerator_spec.rb +70 -58
  46. data/spec/spec_helper.rb +24 -3
  47. data/spec/support/fixture_file.rb +1 -1
  48. 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
- self.name = name
11
- self.attributes = attributes
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
- array = ArrayElement.new
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
- self.name = name
11
- self.attributes = attributes
11
+ @name = name
12
+ @attributes = attributes
12
13
  super(str)
13
14
  end
14
15
 
15
16
  def to_a
16
- array = ArrayElement.new
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
- builder.send("#{name}_", @attributes, @children.join)
25
+ element.add_text(@children.join)
22
26
  else
23
- builder.send("#{name}_", @attributes) do |xml|
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 = Nokogiri::XML::Builder.new
33
+ builder = REXML::Document.new
34
+ builder << REXML::XMLDecl.new('1.0', 'UTF-8')
31
35
  to_xml(builder)
32
- builder.doc
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.new("Unknown output_type '#{val.inspect}'") unless Builder.valid?(val)
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 ||= lambda { |x| x.to_s }
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 = lambda { |x| hash_key_normalizer.call(x).to_sym }
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 = lambda { |x| x.to_s.gsub(/(#{matching_group}):/, '') }
56
+ @hash_key_normalizer = -> (x) { x.to_s.gsub(/(#{matching_group}):/, '') }
38
57
  else
39
- @hash_key_normalizer = lambda { |x| x.to_s.gsub(/\w+:/, '') }
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.new("put_attributes_in_hash! is only valid when using output_type = :hash (the default)'")
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
- parser.parse(@source)
16
+ reader = Parser::LatchedAccumulator.new(@config, @latches, block)
17
+ @config.adapter.parse(@source, reader)
20
18
  end
21
19
  end
22
- end
20
+ end
@@ -5,7 +5,7 @@ module Saxerator
5
5
  end
6
6
 
7
7
  def for_tags(tags)
8
- raise ArgumentError.new('#for_tags requires an Array argument') unless tags.is_a? Array
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({name.to_s => !!value ? value.to_s : nil })
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.new("attributes should be a Hash or Array")
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
@@ -12,4 +12,4 @@ module Saxerator
12
12
  DocumentFragment.new(@source, @config, @latches).first
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -1,6 +1,6 @@
1
1
  module Saxerator
2
2
  module Latches
3
- class AbstractLatch < Nokogiri::XML::SAX::Document
3
+ class AbstractLatch < ::Saxerator::SaxHandler
4
4
  def open
5
5
  @open = true
6
6
  end
@@ -1,6 +1,6 @@
1
1
  module Saxerator
2
2
  module Latches
3
- class AtDepth < Nokogiri::XML::SAX::Document
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 < Nokogiri::XML::SAX::Document
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 name, _
10
- if depth_within_element > 0
11
- increment_depth(1)
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
- if depth_within_element > 0
20
- increment_depth(-1)
21
- @depths.pop if @depths.last == 0
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
- @depths.size > 0 ? @depths.last : 0
29
+ !@depths.empty? ? @depths[-1] : 0
35
30
  end
36
31
  end
37
32
  end
@@ -7,11 +7,11 @@ module Saxerator
7
7
  @names = names
8
8
  end
9
9
 
10
- def start_element name, _
10
+ def start_element(name, _)
11
11
  @names.include?(name) ? open : close
12
12
  end
13
13
 
14
- def end_element name
14
+ def end_element(name)
15
15
  close if @names.include?(name)
16
16
  end
17
17
  end
@@ -7,7 +7,7 @@ module Saxerator
7
7
  @attrs = attrs
8
8
  end
9
9
 
10
- def start_element _, attributes
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 < Nokogiri::XML::SAX::Document
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 name, _
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 < Nokogiri::XML::SAX::Document
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 @builder.new(@config, name, Hash[*attrs.flatten])
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.last.add_node last
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.last.add_node(string) unless string.strip.length == 0
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
- @stack.size > 0
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 < Nokogiri::XML::SAX::Document
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 comment(string)
61
- check_latches_and_passthrough(:comment, string)
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