saxerator 0.9.5 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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