multi_xml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of multi_xml might be problematic. Click here for more details.

data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem 'hpricot', "~> 0.8.2", :require => nil
5
+ gem 'libxml-ruby', "~> 1.1.4", :require => nil
6
+ gem 'nokogiri', "~> 1.4.3", :require => nil
7
+ end
8
+
9
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,25 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ multi_xml (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ hpricot (0.8.2)
10
+ libxml-ruby (1.1.4)
11
+ nokogiri (1.4.3.1)
12
+ rake (0.8.7)
13
+ rspec (1.3.0)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ bundler (~> 1.0.2)
20
+ hpricot (~> 0.8.2)
21
+ libxml-ruby (~> 1.1.4)
22
+ multi_xml!
23
+ nokogiri (~> 1.4.3)
24
+ rake (~> 0.8.7)
25
+ rspec (~> 1.3.0)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Erik Michaels-Ober
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,32 @@
1
+ = MultiXML
2
+
3
+ Lots of Ruby libraries utilize XML parsing in some form, and everyone has their favorite XML library. In order to best support multiple XML parsers and libraries, <tt>multi_xml</tt> is a general-purpose swappable XML backend library. You use it like so:
4
+
5
+ require 'multi_xml'
6
+
7
+ MultiXml.engine = :nokogiri
8
+ MultiXml.parse('<tag>This is the contents</tag>') # parsed using Nokogiri
9
+
10
+ MultiXml.engine = :rexml
11
+ MultiXml.engine = MultiJson::Engines::Rexml # equivalent to previous line
12
+ MultiXml.parse('<tag>This is the contents</tag>') # parsed using REXML
13
+
14
+ The <tt>engine</tt> setter takes either a symbol or a class (to allow for custom XML parsers) that responds to <tt>.parse</tt> at the class level.
15
+
16
+ MultiXML tries to have intelligent defaulting. That is, if you have any of the supported engines already loaded, it will utilize them before attempting to load any. When loading, libraries are ordered by speed. First LibXML, then Nokogiri, then Hpricot, then REXML.
17
+
18
+ == Inspriation
19
+
20
+ MultiXML was inspired by MultiJSON[http://github.com/intridea/multi_json/].
21
+
22
+ == Submitting Patches
23
+
24
+ 1. Fork the project.
25
+ 2. Commit your feature or bug fix.
26
+ 3. Add tests for it. This is important so it doesn't break in the future.
27
+ 4. Do not mess with gemspec, version, or history. (If you want to have your own version, that's fine, but please do so in a separate commit.)
28
+ 5. Submit a pull request. Bonus points for topic branches.
29
+
30
+ == Copyright
31
+
32
+ Copyright (c) 2010 Erik Michaels-Ober. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rake'
4
+ require 'rake/rdoctask'
5
+ require 'spec/rake/spectask'
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ Rake::RDocTask.new do |rdoc|
10
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = "multi_xml #{version}"
13
+ rdoc.rdoc_files.include('README*')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ Spec::Rake::SpecTask.new(:spec) do |spec|
18
+ spec.libs << 'lib' << 'spec'
19
+ spec.spec_files = FileList['spec/**/*_spec.rb']
20
+ end
21
+
22
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.pattern = 'spec/**/*_spec.rb'
25
+ spec.rcov = true
26
+ end
27
+
28
+ task :default => :spec
data/lib/multi_xml.rb ADDED
@@ -0,0 +1,65 @@
1
+ module MultiXml
2
+ module_function
3
+
4
+ # Get the current engine class.
5
+ def engine
6
+ return @engine if @engine
7
+ self.engine = self.default_engine
8
+ @engine
9
+ end
10
+
11
+ REQUIREMENT_MAP = [
12
+ ['libxml', :libxml],
13
+ ['nokogiri', :nokogiri],
14
+ ['hpricot', :hpricot],
15
+ ['rexml/document', :rexml]
16
+ ]
17
+
18
+ # The default engine based on what you currently
19
+ # have loaded and installed. First checks to see
20
+ # if any engines are already loaded, then checks
21
+ # to see which are installed if none are loaded.
22
+ def default_engine
23
+ return :libxml if defined?(::LibXML)
24
+ return :nokogiri if defined?(::Nokogiri)
25
+ return :hpricot if defined?(::Hpricot)
26
+
27
+ REQUIREMENT_MAP.each do |(library, engine)|
28
+ begin
29
+ require library
30
+ return engine
31
+ rescue LoadError
32
+ next
33
+ end
34
+ end
35
+ end
36
+
37
+ # Set the XML parser utilizing a symbol, string, or class.
38
+ # Supported by default are:
39
+ #
40
+ # * <tt>:libxml</tt>
41
+ # * <tt>:nokogiri</tt>
42
+ # * <tt>:hpricot</tt>
43
+ # * <tt>:rexml</tt>
44
+ def engine=(new_engine)
45
+ case new_engine
46
+ when String, Symbol
47
+ require "multi_xml/engines/#{new_engine}"
48
+ @engine = MultiXml::Engines.const_get("#{new_engine.to_s.split('_').map{|s| s.capitalize}.join('')}")
49
+ when Class
50
+ @engine = new_engine
51
+ else
52
+ raise "Did not recognize your engine specification. Please specify either a symbol or a class."
53
+ end
54
+ end
55
+
56
+ # Parse a XML string into Ruby.
57
+ #
58
+ # <b>Options</b>
59
+ #
60
+ # <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
61
+ def parse(string, options = {})
62
+ engine.parse(string, options)
63
+ end
64
+
65
+ end
@@ -0,0 +1,221 @@
1
+ require 'rexml/parsers/baseparser'
2
+ require 'rexml/text'
3
+ require 'date'
4
+ require 'time'
5
+ require 'yaml'
6
+ require 'bigdecimal'
7
+
8
+ module MultiXml
9
+ module Engines
10
+ # Use REXML to parse XML.
11
+ class Rexml
12
+
13
+ def self.parse(string, options = {}) #:nodoc:
14
+ string.strip!
15
+ stack = []
16
+ parser = ::REXML::Parsers::BaseParser.new(string)
17
+
18
+ while true
19
+ event = parser.pull
20
+ case event[0]
21
+ when :end_document
22
+ break
23
+ when :start_element
24
+ stack.push RexmlUtilityNode.new(event[1], event[2])
25
+ when :end_element
26
+ if stack.size > 1
27
+ temp = stack.pop
28
+ stack.last.add_node(temp)
29
+ end
30
+ when :text, :cdata
31
+ stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
32
+ end
33
+ end
34
+ hash = (stack.length > 0 ? stack.pop.to_hash : {})
35
+ options[:symbolize_keys] ? symbolize_keys(hash) : hash
36
+ end
37
+
38
+ def self.symbolize_keys(hash) #:nodoc:
39
+ hash.inject({}){|result, (key, value)|
40
+ new_key = case key
41
+ when String then key.to_sym
42
+ else key
43
+ end
44
+ new_value = case value
45
+ when Hash then symbolize_keys(value)
46
+ else value
47
+ end
48
+ result[new_key] = new_value
49
+ result
50
+ }
51
+ end
52
+ end
53
+
54
+ class RexmlUtilityNode #:nodoc:
55
+ attr_accessor :name, :attributes, :children, :type
56
+
57
+ def self.typecasts
58
+ @@typecasts
59
+ end
60
+
61
+ def self.typecasts=(obj)
62
+ @@typecasts = obj
63
+ end
64
+
65
+ def self.available_typecasts
66
+ @@available_typecasts
67
+ end
68
+
69
+ def self.available_typecasts=(obj)
70
+ @@available_typecasts = obj
71
+ end
72
+
73
+ self.typecasts = {}
74
+ self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
75
+ self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
76
+ self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
77
+ self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
78
+ self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
79
+ self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)}
80
+ self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
81
+ self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
82
+ self.typecasts["symbol"] = lambda{|v| v.nil? ? nil : v.to_sym}
83
+ self.typecasts["string"] = lambda{|v| v.to_s}
84
+ self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
85
+ self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
86
+
87
+ self.available_typecasts = self.typecasts.keys
88
+
89
+ def initialize(name, normalized_attributes = {})
90
+
91
+ # unnormalize attribute values
92
+ attributes = Hash[* normalized_attributes.map { |key, value|
93
+ [ key, unnormalize_xml_entities(value) ]
94
+ }.flatten]
95
+
96
+ @name = name.tr("-", "_")
97
+ # leave the type alone if we don't know what it is
98
+ @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
99
+
100
+ @nil_element = attributes.delete("nil") == "true"
101
+ @attributes = undasherize_keys(attributes)
102
+ @children = []
103
+ @text = false
104
+ end
105
+
106
+ def add_node(node)
107
+ @text = true if node.is_a? String
108
+ @children << node
109
+ end
110
+
111
+ def to_hash
112
+ if @type == "file"
113
+ f = StringIO.new((@children.first || '').unpack('m').first)
114
+ class << f
115
+ attr_accessor :original_filename, :content_type
116
+ end
117
+ f.original_filename = attributes['name'] || 'untitled'
118
+ f.content_type = attributes['content_type'] || 'application/octet-stream'
119
+ return {name => f}
120
+ end
121
+
122
+ if @text
123
+ t = typecast_value( unnormalize_xml_entities( inner_html ) )
124
+ t.class.send(:attr_accessor, :attributes)
125
+ t.attributes = attributes
126
+ return { name => t }
127
+ else
128
+ #change repeating groups into an array
129
+ groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
130
+
131
+ out = nil
132
+ if @type == "array"
133
+ out = []
134
+ groups.each do |k, v|
135
+ if v.size == 1
136
+ out << v.first.to_hash.entries.first.last
137
+ else
138
+ out << v.map{|e| e.to_hash[k]}
139
+ end
140
+ end
141
+ out = out.flatten
142
+
143
+ else # If Hash
144
+ out = {}
145
+ groups.each do |k,v|
146
+ if v.size == 1
147
+ out.merge!(v.first)
148
+ else
149
+ out.merge!( k => v.map{|e| e.to_hash[k]})
150
+ end
151
+ end
152
+ out.merge! attributes unless attributes.empty?
153
+ out = out.empty? ? nil : out
154
+ end
155
+
156
+ if @type && out.nil?
157
+ { name => typecast_value(out) }
158
+ else
159
+ { name => out }
160
+ end
161
+ end
162
+ end
163
+
164
+ # Typecasts a value based upon its type. For instance, if
165
+ # +node+ has #type == "integer",
166
+ # {{[node.typecast_value("12") #=> 12]}}
167
+ #
168
+ # @param value<String> The value that is being typecast.
169
+ #
170
+ # @details [:type options]
171
+ # "integer"::
172
+ # converts +value+ to an integer with #to_i
173
+ # "boolean"::
174
+ # checks whether +value+, after removing spaces, is the literal
175
+ # "true"
176
+ # "datetime"::
177
+ # Parses +value+ using Time.parse, and returns a UTC Time
178
+ # "date"::
179
+ # Parses +value+ using Date.parse
180
+ #
181
+ # @return <Integer, TrueClass, FalseClass, Time, Date, Object>
182
+ # The result of typecasting +value+.
183
+ #
184
+ # @note
185
+ # If +self+ does not have a "type" key, or if it's not one of the
186
+ # options specified above, the raw +value+ will be returned.
187
+ def typecast_value(value)
188
+ return value unless @type
189
+ proc = self.class.typecasts[@type]
190
+ proc.nil? ? value : proc.call(value)
191
+ end
192
+
193
+ # Take keys of the form foo-bar and convert them to foo_bar
194
+ def undasherize_keys(params)
195
+ params.keys.each do |key, value|
196
+ params[key.tr("-", "_")] = params.delete(key)
197
+ end
198
+ params
199
+ end
200
+
201
+ # Get the inner_html of the REXML node.
202
+ def inner_html
203
+ @children.join
204
+ end
205
+
206
+ # Converts the node into a readable HTML node.
207
+ #
208
+ # @return <String> The HTML node in text form.
209
+ def to_html
210
+ attributes.merge!(:type => @type ) if @type
211
+ "<#{name}#{attributes.to_xml_attributes}>#{@nil_element ? '' : inner_html}</#{name}>"
212
+ end
213
+ alias :to_s :to_html
214
+
215
+ def unnormalize_xml_entities value
216
+ ::REXML::Text.unnormalize(value)
217
+ end
218
+ end
219
+
220
+ end
221
+ end
@@ -0,0 +1,3 @@
1
+ module MultiXml
2
+ VERSION = "0.0.1"
3
+ end
data/multi_xml.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "multi_xml/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.add_development_dependency("bundler", ["~> 1.0.2"])
7
+ s.add_development_dependency("hpricot", ["~> 0.8.2"])
8
+ s.add_development_dependency("libxml-ruby", ["~> 1.1.4"])
9
+ s.add_development_dependency("nokogiri", ["~> 1.4.3"])
10
+ s.add_development_dependency("rake", ["~> 0.8.7"])
11
+ s.add_development_dependency("rspec", ["~> 1.3.0"])
12
+ s.name = "multi_xml"
13
+ s.version = MultiXml::VERSION
14
+ s.platform = Gem::Platform::RUBY
15
+ s.authors = ["Erik Michaels-Ober"]
16
+ s.email = ["sferik@gmail.com"]
17
+ s.homepage = "http://rubygems.org/gems/multi_xml"
18
+ s.summary = %q{A gem to provide swappable XML backends utilizing LibXML, Nokogiri, Hpricot, or REXML.}
19
+ s.description = s.summary
20
+ s.rubyforge_project = "multi_xml"
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ class MockDecoder
4
+ def self.parse(string)
5
+ '<tag>This is the contents</tag>'
6
+ end
7
+ end
8
+
9
+ describe "MultiXml" do
10
+ context 'engines' do
11
+ it 'should default to the best available gem' do
12
+ pending
13
+ require 'libxml'
14
+ MultiXml.engine.name.should == 'MultiXml::Engines::Libxml'
15
+ end
16
+
17
+ it 'should be settable via a symbol' do
18
+ pending
19
+ MultiXml.engine = :libxml
20
+ MultiXml.engine.name.should == 'MultiXml::Engines::Libxml'
21
+ end
22
+
23
+ it 'should be settable via a class' do
24
+ MultiXml.engine = MockDecoder
25
+ MultiXml.engine.name.should == 'MockDecoder'
26
+ end
27
+ end
28
+
29
+ %w(libxml nokogiri hpricot rexml).each do |engine|
30
+ context engine do
31
+ before do
32
+ begin
33
+ MultiXml.engine = engine
34
+ rescue LoadError
35
+ pending "Engine #{engine} couldn't be loaded (not installed?)"
36
+ end
37
+ end
38
+
39
+ describe '.parse' do
40
+ it 'should properly parse some XML' do
41
+ MultiXml.parse('<tag>This is the contents</tag>').should == {'tag' => 'This is the contents'}
42
+ end
43
+
44
+ it 'should allow for symbolization of keys' do
45
+ MultiXml.parse('<tag>This is the contents</tag>', :symbolize_keys => true).should == {:tag => 'This is the contents'}
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=nested
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'multi_xml'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+ require 'rubygems'
7
+ begin
8
+ require 'bundler'
9
+ Bundler.setup
10
+ rescue LoadError
11
+ $stderr.puts "Bundler (or a dependency) not available."
12
+ end
13
+
14
+ Spec::Runner.configure do |config|
15
+
16
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multi_xml
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Erik Michaels-Ober
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-02 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 19
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 2
34
+ version: 1.0.2
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hpricot
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 59
46
+ segments:
47
+ - 0
48
+ - 8
49
+ - 2
50
+ version: 0.8.2
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: libxml-ruby
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 27
62
+ segments:
63
+ - 1
64
+ - 1
65
+ - 4
66
+ version: 1.1.4
67
+ type: :development
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 1
78
+ segments:
79
+ - 1
80
+ - 4
81
+ - 3
82
+ version: 1.4.3
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: rake
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 49
94
+ segments:
95
+ - 0
96
+ - 8
97
+ - 7
98
+ version: 0.8.7
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: rspec
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 27
110
+ segments:
111
+ - 1
112
+ - 3
113
+ - 0
114
+ version: 1.3.0
115
+ type: :development
116
+ version_requirements: *id006
117
+ description: A gem to provide swappable XML backends utilizing LibXML, Nokogiri, Hpricot, or REXML.
118
+ email:
119
+ - sferik@gmail.com
120
+ executables: []
121
+
122
+ extensions: []
123
+
124
+ extra_rdoc_files: []
125
+
126
+ files:
127
+ - .gitignore
128
+ - Gemfile
129
+ - Gemfile.lock
130
+ - LICENSE
131
+ - README.rdoc
132
+ - Rakefile
133
+ - lib/multi_xml.rb
134
+ - lib/multi_xml/engines/rexml.rb
135
+ - lib/multi_xml/version.rb
136
+ - multi_xml.gemspec
137
+ - spec/multi_xml_spec.rb
138
+ - spec/spec.opts
139
+ - spec/spec_helper.rb
140
+ has_rdoc: true
141
+ homepage: http://rubygems.org/gems/multi_xml
142
+ licenses: []
143
+
144
+ post_install_message:
145
+ rdoc_options: []
146
+
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ requirements: []
168
+
169
+ rubyforge_project: multi_xml
170
+ rubygems_version: 1.3.7
171
+ signing_key:
172
+ specification_version: 3
173
+ summary: A gem to provide swappable XML backends utilizing LibXML, Nokogiri, Hpricot, or REXML.
174
+ test_files:
175
+ - spec/multi_xml_spec.rb
176
+ - spec/spec.opts
177
+ - spec/spec_helper.rb