decontaminate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 391a40ec6c400fea8dd7e0e0e236ccb54dd0d64c
4
+ data.tar.gz: ebc11740db0cd3ca3aa55dddcd6854a5b1b21d34
5
+ SHA512:
6
+ metadata.gz: 5ce173bd8e3aa615abc225a2cd5dc921fbff1e7ed0509d7dd485c5670f8d398acfbdefadef498bb9a0a6ec1b1d1666f9a5557cf856d042f4f2d452bd104d981e
7
+ data.tar.gz: 151a004aafa09dfe7778e0d55ee5374a2d59ac6393f8ecba57e46ef27b573e64f38e780afb0afbb148f6599bd846f5954d3eee767ff234ac2b90741fef57a90b
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /decontaminate-*.gem
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2.2
5
+
6
+ script:
7
+ - bundle exec rake
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015, Alexis King
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose
4
+ with or without fee is hereby granted, provided that the above copyright notice
5
+ and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
11
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
12
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
13
+ THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Decontaminate
2
+
3
+ Decontaminate is a tool for extracting information from large, potentially nested XML documents. It provides a simple Ruby DSL for selecting values from Nokogiri objects and storing the results in JSON-like Ruby hashes and arrays.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'decontaminate'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install decontaminate
20
+
21
+ ## Usage
22
+
23
+ Decontaminate provides a DSL for creating *decontaminators*, which, when instantiated, accept XML nodes or documents and produce a hash as a result. To start, create a class that inherits from `Decontaminate::Decontaminator`:
24
+
25
+ ```ruby
26
+ class MyDecontaminator < Decontaminate::Decontaminator
27
+ end
28
+ ```
29
+
30
+ If parsing an entire document, you should specify the name of the root element:
31
+
32
+ ```ruby
33
+ class MyDecontaminator < Decontaminate::Decontaminator
34
+ self.root = 'User'
35
+ end
36
+ ```
37
+
38
+ ### Scalar Values
39
+
40
+ To select values from the XML document, use the `scalar` class method:
41
+
42
+ ```ruby
43
+ class MyDecontaminator < Decontaminate::Decontaminator
44
+ self.root = 'User'
45
+
46
+ scalar 'Name'
47
+ scalar 'Age', type: :integer
48
+ scalar 'DateRegistered', key: 'registered_at'
49
+ end
50
+ ```
51
+
52
+ This might produce a result like the following:
53
+
54
+ ```ruby
55
+ => MyDecontaminator.new(xml_document).as_json
56
+ {
57
+ 'name' => 'Jane Smith',
58
+ 'age' => 28,
59
+ 'registered_at' => '2013-08-16T20:51:34.236Z'
60
+ }
61
+ ```
62
+
63
+ The first argument to `scalar` is the name of the node to extract data from. In fact, this can be any XPath string relative to the document root. By default, the resulting JSON key is inferred from the provided path, but it can also be overridden with the `key:` argument. Additionally, the type of the scalar can be specified with the `type:` argument, which defaults to `:string`.
64
+
65
+ Attributes can be specified with XPath syntax by prepending an `@` sign:
66
+
67
+ ```ruby
68
+ scalar '@id', type: :integer
69
+ ```
70
+
71
+ ### Nested Values
72
+
73
+ It's also possible to specify nested or even deeply nested hashes with the `hash` class method:
74
+
75
+ ```ruby
76
+ hash 'UserProfile', key: 'profile' do
77
+ scalar 'Description'
78
+
79
+ hash 'Specialization' do
80
+ scalar 'Area'
81
+ scalar 'Expertise', type: :float
82
+ end
83
+ end
84
+ ```
85
+
86
+ The `hash` method accepts a block, which works just like the class body, but all paths are scoped to the path passed to `hash`. The `key` argument is optional, just like with `scalar`.
87
+
88
+ ### Array Data
89
+
90
+ In addition to the `scalar` and `hash` methods, there are plural forms which allow parsing and extracting data that appears many times within a single document. These are named `scalars` and `hashes`, respectively. They work much like their singular counterparts, but the provided path should match multiple elements.
91
+
92
+ For example, given the following decontaminator:
93
+
94
+ ```ruby
95
+ class ArticlesDecontaminator < Decontaminate::Decontaminator
96
+ hashes 'Articles' do
97
+ scalar 'Name'
98
+ scalars 'Tags'
99
+ end
100
+ end
101
+ ```
102
+
103
+ And given the following XML document:
104
+
105
+ ```xml
106
+ <Articles>
107
+ <Article>
108
+ <Name>Article A</Name>
109
+ <Tags>
110
+ <Tag>News</Tag>
111
+ <Tag>Technology</Tag>
112
+ </Tags>
113
+ </Article>
114
+ <Article>
115
+ <Name>Article B</Name>
116
+ <Tags>
117
+ <Tag>Sports</Tag>
118
+ <Tag>Recreation</Tag>
119
+ </Tags>
120
+ </Article>
121
+ </Articles>
122
+ ```
123
+
124
+ The resulting object will have the following structure:
125
+
126
+ ```ruby
127
+ {
128
+ 'articles' => [
129
+ {
130
+ 'name' => 'Article A',
131
+ 'tags' => ['News', 'Technology']
132
+ },
133
+ {
134
+ 'name' => 'Article B',
135
+ 'tags' => ['Sports', 'Recreation']
136
+ }
137
+ ]
138
+ }
139
+ ```
140
+
141
+ There are some special things to note in the above example:
142
+
143
+ - **The name of the individual elements is inferred from the parent key.**
144
+
145
+ In both cases, the parent element was the plural form of its children (`Articles`/`Article` and `Tags`/`Tag`). Since this is common, the plural forms automatically perform this name inference.
146
+
147
+ Since this behavior is sometimes unwanted, it can be disabled by passing the path as an explicit `path:` keyword argument.
148
+
149
+ ```ruby
150
+ scalars path: 'Tags/TagName', key: 'tags' # Performs no name inference
151
+ ```
152
+
153
+ - **No `root` element was specified since the root element is a plural.**
154
+
155
+ When using name inference for a plural element at the root, specifying the root element is an error. By using the explicit `path:` form mentioned above, `root` could still be specified.
156
+
157
+ ```ruby
158
+ self.root = 'Articles'
159
+ hashes path: 'Article', key: 'articles' do; ...; end
160
+ ```
161
+
162
+ ### Flattening nested data
163
+
164
+ Since source data is sometimes more nested than is desired, the `with` method is a helper for scoping decontamination directives to a given XML element without increasing the nesting depth of the resulting object. Like `hash`, it accepts an XPath and a block, but the attributes created from within the block will not be wrapped in a hash.
165
+
166
+ ```ruby
167
+ with 'Some/Nested/Data' do
168
+ scalar 'Value'
169
+ end
170
+ ```
171
+
172
+ There is no plural form for `with` since it would, by necessity, create duplicate keys.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new :spec
5
+
6
+ task default: [:spec]
data/bin/pry ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'pry' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('pry', 'pry')
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'decontaminate/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'decontaminate'
8
+ spec.version = Decontaminate::VERSION
9
+ spec.authors = ['Alexis King']
10
+ spec.email = ['lexi.lambda@gmail.com']
11
+
12
+ spec.summary = 'Convert XML to JSON with a DSL'
13
+ spec.homepage = 'https://github.com/lexi-lambda/decontaminate'
14
+ spec.licenses = ['ISC']
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.required_ruby_version = '>= 1.9'
22
+
23
+ spec.add_runtime_dependency 'activesupport', '~> 4.2'
24
+ spec.add_runtime_dependency 'nokogiri', '~> 1.6'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.10'
27
+ spec.add_development_dependency 'pry', '~> 0.10.3'
28
+ spec.add_development_dependency 'pry-byebug', '~> 3.2'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.3'
31
+ spec.add_development_dependency 'yard', '~> 0.8.7'
32
+ end
@@ -0,0 +1,19 @@
1
+ module Decontaminate
2
+ module Decoder
3
+ class Array
4
+ attr_reader :xpath, :decoder
5
+
6
+ def initialize(xpath, decoder)
7
+ @xpath = xpath
8
+ @decoder = decoder
9
+ end
10
+
11
+ def decode(xml_node)
12
+ children = xml_node.xpath xpath
13
+ children.map do |child|
14
+ decoder.decode child
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Decontaminate
2
+ module Decoder
3
+ class ChildNodeProxy
4
+ attr_reader :xpath, :decoder
5
+
6
+ def initialize(xpath, decoder)
7
+ @xpath = xpath
8
+ @decoder = decoder
9
+ end
10
+
11
+ def decode(xml_node)
12
+ child = xml_node.at_xpath xpath
13
+ decoder.decode child
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Decontaminate
2
+ module Decoder
3
+ class Hash
4
+ attr_reader :xpath, :decontaminator
5
+
6
+ def initialize(xpath, decontaminator)
7
+ @xpath = xpath
8
+ @decontaminator = decontaminator
9
+ end
10
+
11
+ def decode(xml_node)
12
+ child = xml_node.at_xpath xpath
13
+ decontaminator.new(child).as_json
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Decontaminate
2
+ module Decoder
3
+ class Scalar
4
+ attr_reader :xpath, :type
5
+
6
+ def initialize(xpath, type)
7
+ @xpath = xpath
8
+ @type = type
9
+ end
10
+
11
+ def decode(xml_node)
12
+ child = xml_node.at_xpath xpath
13
+ text = coerce_node_to_text child
14
+
15
+ return unless text
16
+
17
+ case type
18
+ when :string
19
+ text
20
+ when :integer
21
+ text.to_i
22
+ when :float
23
+ text.to_f
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def coerce_node_to_text(node)
30
+ if node.is_a?(Nokogiri::XML::Text) || node.is_a?(Nokogiri::XML::Attr)
31
+ node.to_s
32
+ else
33
+ node.at_xpath('text()').to_s
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,109 @@
1
+ require 'active_support/inflector'
2
+
3
+ require_relative 'decoder/array'
4
+ require_relative 'decoder/child_node_proxy'
5
+ require_relative 'decoder/hash'
6
+ require_relative 'decoder/scalar'
7
+
8
+ module Decontaminate
9
+ # Decontaminate::Decontaminator is the base class for creating XML extraction
10
+ # parsers. A DSL is exposed via class methods to allow specifying how the XML
11
+ # should be parsed.
12
+ class Decontaminator
13
+ class << self
14
+ attr_accessor :root
15
+ attr_reader :decoders
16
+
17
+ def inherited(subclass)
18
+ subclass.instance_eval do
19
+ @root = '.'
20
+ @decoders = {}
21
+ end
22
+ end
23
+
24
+ def scalar(xpath, type: :string, key: infer_key(xpath))
25
+ add_decoder key, Decontaminate::Decoder::Scalar.new(xpath, type)
26
+ end
27
+
28
+ def scalars(xpath = nil, path: nil, type: :string, key: nil)
29
+ resolved_path = path || infer_plural_path(xpath)
30
+ key ||= infer_key(path || xpath)
31
+
32
+ singular = Decontaminate::Decoder::Scalar.new('.', type)
33
+ decoder = Decontaminate::Decoder::Array.new(resolved_path, singular)
34
+
35
+ add_decoder key, decoder
36
+ end
37
+
38
+ def hash(xpath, key: infer_key(xpath), &body)
39
+ decontaminator = Class.new(Decontaminate::Decontaminator, &body)
40
+ add_decoder key, Decontaminate::Decoder::Hash.new(xpath, decontaminator)
41
+ end
42
+
43
+ def hashes(xpath = nil, path: nil, key: nil, &body)
44
+ resolved_path = path || infer_plural_path(xpath)
45
+ key ||= infer_key(path || xpath)
46
+
47
+ decontaminator = Class.new(Decontaminate::Decontaminator, &body)
48
+ singular = Decontaminate::Decoder::Hash.new('.', decontaminator)
49
+ decoder = Decontaminate::Decoder::Array.new(resolved_path, singular)
50
+
51
+ add_decoder key, decoder
52
+ end
53
+
54
+ def with(xpath, &body)
55
+ this = self
56
+ decontaminator = Class.new(Decontaminate::Decontaminator)
57
+
58
+ decontaminator.instance_eval do
59
+ define_singleton_method :add_decoder do |key, decoder|
60
+ proxy = Decontaminate::Decoder::ChildNodeProxy.new(xpath, decoder)
61
+ this.add_decoder key, proxy
62
+ end
63
+ end
64
+
65
+ decontaminator.class_eval(&body)
66
+ end
67
+
68
+ def add_decoder(key, decoder)
69
+ fail "Decoder already registered for key #{key}" if decoders.key? key
70
+ decoders[key] = decoder
71
+ end
72
+
73
+ def infer_key(xpath)
74
+ xpath.delete('@').underscore
75
+ end
76
+
77
+ def infer_plural_path(xpath)
78
+ xpath + '/' + xpath.split('/').last.singularize
79
+ end
80
+ end
81
+
82
+ attr_reader :xml_node
83
+
84
+ def initialize(xml_node)
85
+ @xml_node = xml_node
86
+ end
87
+
88
+ def as_json
89
+ acc = {}
90
+
91
+ root_node = xml_node.at_xpath root
92
+ decoders.each do |key, decoder|
93
+ acc[key] = decoder.decode root_node
94
+ end
95
+
96
+ acc
97
+ end
98
+
99
+ private
100
+
101
+ def decoders
102
+ self.class.decoders
103
+ end
104
+
105
+ def root
106
+ self.class.root
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module Decontaminate
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'decontaminate/decontaminator'
2
+
3
+ module Decontaminate
4
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decontaminate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexis King
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.8.7
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.8.7
125
+ description:
126
+ email:
127
+ - lexi.lambda@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".travis.yml"
134
+ - Gemfile
135
+ - LICENSE.txt
136
+ - README.md
137
+ - Rakefile
138
+ - bin/pry
139
+ - bin/rake
140
+ - bin/rspec
141
+ - decontaminate.gemspec
142
+ - lib/decontaminate.rb
143
+ - lib/decontaminate/decoder/array.rb
144
+ - lib/decontaminate/decoder/child_node_proxy.rb
145
+ - lib/decontaminate/decoder/hash.rb
146
+ - lib/decontaminate/decoder/scalar.rb
147
+ - lib/decontaminate/decontaminator.rb
148
+ - lib/decontaminate/version.rb
149
+ homepage: https://github.com/lexi-lambda/decontaminate
150
+ licenses:
151
+ - ISC
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '1.9'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.4.5
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Convert XML to JSON with a DSL
173
+ test_files: []
174
+ has_rdoc: