saxerator 0.0.1

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.
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ .idea/
7
+ coverage/
8
+
data/.rvmrc ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="1.9.3@saxerator"
8
+
9
+ #
10
+ # First we attempt to load the desired environment directly from the environment
11
+ # file. This is very fast and efficient compared to running through the entire
12
+ # CLI and selector. If you want feedback on which environment was used then
13
+ # insert the word 'use' after --create as this triggers verbose mode.
14
+ #
15
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
16
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
17
+ then
18
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
19
+
20
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
21
+ then
22
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
23
+ fi
24
+ else
25
+ # If the environment file has not yet been created, use the RVM CLI to select.
26
+ if ! rvm --create "$environment_id"
27
+ then
28
+ echo "Failed to create RVM environment '${environment_id}'."
29
+ return 1
30
+ fi
31
+ fi
32
+
33
+ if [[ $- == *i* ]] # check for interactive shells
34
+ then
35
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
36
+ else
37
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
38
+ fi
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in saxerator.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+
3
+ guard :bundler do
4
+ watch('Gemfile')
5
+ watch(/^saxerator\.gemspec$/)
6
+ end
7
+
8
+ guard :rspec, :cli => '--color --format doc' do
9
+ watch(%r{^spec/.+_spec\.rb$})
10
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
11
+ watch(%r{^spec/fixtures/.+\.xml$}) { :spec }
12
+ watch('spec/spec_helper.rb') { :spec }
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2012 Bradley Schaefer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6
+ persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9
+ Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ Saxerator
2
+ =========
3
+
4
+ Saxerator is a SAX-based xml parser designed for parsing very large files into manageable chunks. Rather than
5
+ dealing directly with SAX callback methods, Saxerator gives you Enumerable access to chunks of an xml document.
6
+ This approach is ideal for large xml files that represent a collection of elements.
7
+
8
+ Examples
9
+ --------
10
+
11
+ ```ruby
12
+ Saxerator.parser(File.new("rss.xml")).for_tag(:item).each do |item|
13
+ puts "#{item['title']}: #{item['author']}"
14
+ end
15
+ ```
16
+
17
+ Compatibility
18
+ -------------
19
+ This library is known to work with the following rubies:
20
+
21
+ * MRI 1.9.3-p125
22
+ * MRI 1.9.2-p318
23
+ * JRuby 1.6.7 (with JRUBY_OPTS=--1.9)
24
+
25
+ Saxerator may work with other versions with support for Fiber.
26
+
27
+ Known incompatible rubies:
28
+
29
+ * MRI 1.9.2-p290 (Fiber segfaults)
30
+
31
+ FAQ
32
+ ---
33
+ Why the name 'Saxerator'?
34
+
35
+ > It's a combination of SAX + Enumerator.
36
+
37
+ Why use Saxerator over regular SAX parsing?
38
+
39
+ > Much of the SAX parsing code I've written over the years has fallen into a pattern that Saxerator encapsulates:
40
+ > marshall a chunk of an XML document into an object, operate on that object, then move on to the
41
+ > next chunk. Saxerator alleviates the pain of marshalling and allows you to focus solely on operating on the
42
+ > document chunk.
43
+
44
+ Why not DOM parsing?
45
+
46
+ > DOM parsers load the entire document into memory. Saxerator only holds a single chunk in memory at a time. If your
47
+ > document is very large, this can be an important consideration.
48
+
49
+ ### Acknowledgements ###
50
+ Saxerator was inspired by [Nori](https://github.com/rubiii/nori) and [Gregory Brown](http://majesticseacreature.com/)'s
51
+ [Practicing Ruby](http://practicingruby.com/)
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path('../../lib', __FILE__)
2
+ require 'saxerator'
3
+ require 'benchmark'
4
+
5
+ file = ARGV.shift
6
+ if !File.exists?(file)
7
+ puts "Cannot find file #{file}"
8
+ exit 1
9
+ end
10
+ file = File.new(file)
11
+
12
+ tag = ARGV.shift || :artist
13
+
14
+ count = 0
15
+ Benchmark.bm do |x|
16
+ x.report { Saxerator.parser(file).for_tag(tag).each { count = count + 1 } }
17
+ end
18
+ puts "#{count} #{tag} elements parsed"
@@ -0,0 +1,32 @@
1
+ require 'ipsum'
2
+
3
+ filename = ARGV.shift
4
+
5
+ unless filename
6
+ puts "Syntax: ruby #{__FILE__} <filename>"
7
+ exit 1
8
+ end
9
+
10
+ num_records = ARGV.shift || 100000
11
+ num_records = num_records.to_i
12
+
13
+ element = <<-eos
14
+ <artist id="%i">
15
+ <name>Rock Star %i</name>
16
+ <active>true</active>
17
+ <description>%s</description>
18
+ <genre>Rock,Metal</genre>
19
+ </artist>
20
+ eos
21
+
22
+ puts "Writing #{num_records} sample records to #{filename}, this will take a while..."
23
+
24
+ File.open(filename, 'w') do |f|
25
+ f.puts '<?xml version="1.0" encoding="UTF-8"?>'
26
+ f.puts '<artists>'
27
+ num_records.times do |count|
28
+ f.puts(element % [count, count, 5.sentences])
29
+ end
30
+ f.puts '</artists>'
31
+ end
32
+ puts "DONE!"
@@ -0,0 +1,12 @@
1
+ require "saxerator/version"
2
+ require 'saxerator/configuration'
3
+ require 'saxerator/xml_node'
4
+ require 'saxerator/parser/nokogiri'
5
+
6
+ module Saxerator
7
+ extend self
8
+
9
+ def parser(xml)
10
+ Saxerator::Configuration.new(xml)
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Saxerator
2
+ class Configuration
3
+ attr_reader :source
4
+
5
+ def initialize(source)
6
+ @source = source
7
+ end
8
+
9
+ def for_tag(tag)
10
+ tag = tag.to_s
11
+ Saxerator::Parser::Nokogiri.new(self, source, tag)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ require 'nokogiri'
2
+ require 'fiber'
3
+
4
+ module Saxerator
5
+ module Parser
6
+ class Nokogiri
7
+ include Enumerable
8
+
9
+ def initialize(config, source, tag)
10
+ @config = config
11
+ @source = source
12
+ @tag = tag
13
+ end
14
+
15
+ def each(&block)
16
+ begin
17
+ fiber = Fiber.new do
18
+ document = Document.new(@config, @tag)
19
+ parser = ::Nokogiri::XML::SAX::Parser.new document
20
+ parser.parse(@source)
21
+ end
22
+ while fiber.alive? do
23
+ result = fiber.resume
24
+ yield(result) unless result.nil?
25
+ end
26
+ rescue FiberError
27
+ end
28
+ end
29
+
30
+ class Document < ::Nokogiri::XML::SAX::Document
31
+ attr_accessor :stack
32
+
33
+ def initialize(config, tag)
34
+ @config = config
35
+ @tag = tag
36
+ @stack = []
37
+ end
38
+
39
+ def start_element(name, attrs = [])
40
+ if stack.size > 0 || name == @tag
41
+ stack.push Saxerator::XmlNode.new(@config, name, Hash[*attrs.flatten])
42
+ end
43
+ end
44
+
45
+ def end_element(name)
46
+ if stack.size > 1
47
+ last = stack.pop
48
+ stack.last.add_node last
49
+ elsif stack.size == 1
50
+ Fiber.yield(stack.pop.to_hash)
51
+ end
52
+ end
53
+
54
+ def characters(string)
55
+ stack.last.add_node(string) unless string.strip.length == 0 || stack.empty?
56
+ end
57
+
58
+ alias cdata_block characters
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,9 @@
1
+ module Saxerator
2
+ class StringWithAttributes < String
3
+ attr_accessor :attributes
4
+
5
+ def attribrutes
6
+ @attributes ||= {}
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Saxerator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,38 @@
1
+ module Saxerator
2
+ class XmlNode
3
+ attr_accessor :name, :attributes, :children, :type
4
+
5
+ def initialize(config, name, attributes)
6
+ @config = config
7
+ self.name = name
8
+ self.attributes = attributes
9
+ self.children = []
10
+ @text = false
11
+ end
12
+
13
+ def add_node(node)
14
+ @text = true if node.is_a? String
15
+ children << node
16
+ end
17
+
18
+ def to_hash
19
+ if @text
20
+ return children.join(' ')
21
+ else
22
+ out = {}
23
+ @children.each do |child|
24
+ name = child.name
25
+ if out[name]
26
+ if !out[name].is_a?(Array)
27
+ out[name] = [out[name]]
28
+ end
29
+ out[name] << child.to_hash
30
+ else
31
+ out[name] = child.to_hash
32
+ end
33
+ end
34
+ out
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'saxerator/version'
4
+ require 'rubygems/package_task'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'saxerator'
8
+ s.version = Saxerator::VERSION
9
+ s.authors = ['Bradley Schaefer']
10
+ s.email = ['bradley.schaefer@gmail.com']
11
+ s.homepage = 'https://github.com/soulcutter/saxerator'
12
+ s.summary = 'A SAX-based XML parser for parsing large files into manageable chunks'
13
+ s.description = 'A SAX-based XML parser for parsing large files into manageable chunks'
14
+
15
+ s.required_ruby_version = '>= 1.9.2'
16
+
17
+ s.rubyforge_project = 'saxerator'
18
+
19
+ s.files = FileList[
20
+ 'LICENSE',
21
+ 'README.md',
22
+ 'saxerator.gemspec',
23
+ 'lib/**/*.rb',
24
+ 'spec/**/*.*',
25
+ 'benchmark/**/*.rb',
26
+ 'Gemfile',
27
+ 'Guardfile',
28
+ 'Rakefile',
29
+ '.rvmrc',
30
+ '.gitignore'
31
+ ]
32
+ s.test_files = FileList['spec/**/*.*']
33
+ s.executables = []
34
+ s.require_paths = ['lib']
35
+
36
+ s.add_runtime_dependency 'nokogiri'
37
+
38
+ s.add_development_dependency 'rspec'
39
+ s.add_development_dependency 'guard'
40
+ s.add_development_dependency 'guard-bundler'
41
+ s.add_development_dependency 'guard-rspec'
42
+ s.add_development_dependency 'simplecov'
43
+ s.add_development_dependency 'ipsum'
44
+ end
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <blurbs>
3
+ <blurb>one</blurb>
4
+ <blurb>two</blurb>
5
+ <blurb>three</blurb>
6
+ </blurbs>
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xml:lang="en-US" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
3
+ <id>/blog</id>
4
+ <link type="text/html" href="https://example.com/blog" rel="alternate"/>
5
+ <link type="application/atom+xml" href="https://example.com/blog.atom" rel="self"/>
6
+ <title>Example Blog</title>
7
+ <updated>2012-01-01T16:17:00-06:00</updated>
8
+ <entry>
9
+ <id>1</id>
10
+ <published>2012-01-01T16:17:00-06:00</published>
11
+ <updated>2012-01-01T16:17:00-06:00</updated>
12
+ <link type="text/html" href="https://example.com/blog/how-to-eat-an-airplane" rel="alternate"/>
13
+ <title>How to eat an airplane</title>
14
+ <content type="html">&lt;p&gt;Airplanes are very large, which can present difficulty in digestion.&lt;/p&gt;
15
+
16
+ &lt;p&gt;The key is to break it down into bite-sized portions and go slowly.&lt;/p&gt;
17
+ </content>
18
+ <media:thumbnail url="http://www.gravatar.com/avatar/a9eb6ba22e482b71b266daadf9c9a080?s=80"/>
19
+ <author>
20
+ <name>Soulcutter</name>
21
+ </author>
22
+ <contributor>
23
+ <name>Jane Doe</name>
24
+ </contributor>
25
+ <contributor>
26
+ <name>Leviticus Alabaster</name>
27
+ </contributor>
28
+ </entry>
29
+ <entry>
30
+ <id>2</id>
31
+ <published>2012-01-02T18:19:00-06:00</published>
32
+ <updated>2012-01-02T18:19:00-06:00</updated>
33
+ <link type="text/html" href="https://example.com/blog/sample-data-is-hard-work" rel="alternate"/>
34
+ <title>Sample data is hard work</title>
35
+ <content type="html">&lt;p&gt;People don't believe me when I tell them how tedious it is.&lt;/p&gt;</content>
36
+ <media:thumbnail url="http://www.gravatar.com/avatar/742798d92b705cfeba3937e122974982?s=80"/>
37
+ <author>
38
+ <name>Bradley Schaefer</name>
39
+ </author>
40
+ </entry>
41
+ <entry>
42
+ <id>3</id>
43
+ <published>2012-01-15T21:22:00-06:00</published>
44
+ <updated>2012-01-15T21:22:00-06:00</updated>
45
+ <link type="text/html" href="https://example.com/blog/three-should-be-enough" rel="alternate"/>
46
+ <title>Three should be enough</title>
47
+ <content type="html">&lt;p&gt;EOM&lt;/p&gt;</content>
48
+ <media:thumbnail url="http://www.gravatar.com/avatar/a9eb6ba22e482b71b266daadf9c9a080?s=80"/>
49
+ <author>
50
+ <name>Soulcutter</name>
51
+ </author>
52
+ </entry>
53
+ </feed>
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Saxerator do
4
+ def fixture_file(name)
5
+ File.new(File.join(File.dirname(__FILE__), '..', 'fixtures', name))
6
+ end
7
+
8
+ context ".parser" do
9
+ subject { parser }
10
+ let(:parser) { Saxerator.parser(xml) }
11
+
12
+ context "with a string with blurbs" do
13
+ let(:xml) { "<blurbs><blurb>one</blurb><blurb>two</blurb><blurb>three</blurb></blurbs>" }
14
+
15
+ it "should parse simple strings" do
16
+ results = []
17
+ subject.for_tag(:blurb).each { |x| results << x }
18
+ results.should == ['one', 'two', 'three']
19
+ end
20
+
21
+ context "and one non-blurb" do
22
+ let(:xml) { "<blurbs><blurb>one</blurb><blurb>two</blurb><blurb>three</blurb><notablurb>four</notablurb></blurbs>" }
23
+ it "should only parse the requested tag" do
24
+ results = []
25
+ subject.for_tag(:blurb).each { |x| results << x }
26
+ results.should == ['one', 'two', 'three']
27
+ subject.for_tag(:notablurb).each { |x| results << x }
28
+ results.should == ['one', 'two', 'three', 'four']
29
+ end
30
+ end
31
+ end
32
+
33
+ context "with a file with blurbs" do
34
+ let(:xml) { fixture_file('flat_blurbs.xml') }
35
+
36
+ it "should parse simple strings" do
37
+ results = []
38
+ subject.for_tag(:blurb).each { |x| results << x }
39
+ results.should == ['one', 'two', 'three']
40
+ end
41
+ end
42
+
43
+ context "with a file with nested elements" do
44
+ let(:xml) { fixture_file('nested_elements.xml') }
45
+ subject { parser.for_tag(:entry).first }
46
+
47
+ specify { subject['title'].should == 'How to eat an airplane' }
48
+ specify { subject['author'].should == {'name' => 'Soulcutter'} }
49
+ specify { subject['contributor'].should == [{'name' => 'Jane Doe'}, {'name' => 'Leviticus Alabaster'}] }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter "spec/"
4
+ end
5
+
6
+ require 'saxerator'
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: saxerator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bradley Schaefer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: &70348029624860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70348029624860
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70348029624300 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70348029624300
36
+ - !ruby/object:Gem::Dependency
37
+ name: guard
38
+ requirement: &70348029623840 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70348029623840
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-bundler
49
+ requirement: &70348029623320 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70348029623320
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &70348029622860 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70348029622860
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: &70348029622300 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70348029622300
80
+ - !ruby/object:Gem::Dependency
81
+ name: ipsum
82
+ requirement: &70348029621880 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70348029621880
91
+ description: A SAX-based XML parser for parsing large files into manageable chunks
92
+ email:
93
+ - bradley.schaefer@gmail.com
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - LICENSE
99
+ - README.md
100
+ - saxerator.gemspec
101
+ - lib/saxerator/configuration.rb
102
+ - lib/saxerator/parser/nokogiri.rb
103
+ - lib/saxerator/string_with_attributes.rb
104
+ - lib/saxerator/version.rb
105
+ - lib/saxerator/xml_node.rb
106
+ - lib/saxerator.rb
107
+ - spec/fixtures/flat_blurbs.xml
108
+ - spec/fixtures/nested_elements.xml
109
+ - spec/lib/saxerator_spec.rb
110
+ - spec/spec_helper.rb
111
+ - benchmark/benchmark.rb
112
+ - benchmark/generate_sample_file.rb
113
+ - Gemfile
114
+ - Guardfile
115
+ - Rakefile
116
+ - .rvmrc
117
+ - .gitignore
118
+ homepage: https://github.com/soulcutter/saxerator
119
+ licenses: []
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: 1.9.2
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project: saxerator
138
+ rubygems_version: 1.8.11
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A SAX-based XML parser for parsing large files into manageable chunks
142
+ test_files:
143
+ - spec/fixtures/flat_blurbs.xml
144
+ - spec/fixtures/nested_elements.xml
145
+ - spec/lib/saxerator_spec.rb
146
+ - spec/spec_helper.rb