decontaminate 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +2 -0
- data/lib/decontaminate/decontaminator.rb +173 -6
- data/lib/decontaminate/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 579118a9ae1379d01aebacbd278344e384491251
|
4
|
+
data.tar.gz: 120fd509e2e662fbcd1fc63774c07e6d5dd3fa1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 973c6a4310626adcabf496df4f265d40443e87d71e6855efe64979e5b56bb980fdc96ed5aa4f6172197b84093a5d962387095c3c39315e7e83564f78361917ff
|
7
|
+
data.tar.gz: 59b7fb6c09ae5fd00e88dadfa1decb1c36971b610e2922be2b69e10cd3e69bdec83dfded71699842d9edfcfbf651d9c67cbc2d7770dbc6742c72cf68338814f6
|
data/.yardopts
ADDED
@@ -7,14 +7,19 @@ require_relative 'decoder/scalar'
|
|
7
7
|
require_relative 'decoder/tuple'
|
8
8
|
|
9
9
|
module Decontaminate
|
10
|
-
# Decontaminate::Decontaminator is the base class for creating XML
|
11
|
-
# parsers. A DSL is exposed via class methods to allow specifying
|
12
|
-
# should be parsed.
|
10
|
+
# {Decontaminate::Decontaminator} is the base class for creating XML
|
11
|
+
# extraction parsers. A DSL is exposed via class methods to allow specifying
|
12
|
+
# how the XML should be parsed.
|
13
13
|
class Decontaminator
|
14
14
|
class << self
|
15
|
+
# An attribute that controls what the base element should be for all XPath
|
16
|
+
# queries against the provided XML node.
|
15
17
|
attr_accessor :root
|
18
|
+
|
19
|
+
# @private
|
16
20
|
attr_reader :decoders
|
17
21
|
|
22
|
+
# @private
|
18
23
|
def inherited(subclass)
|
19
24
|
subclass.instance_eval do
|
20
25
|
@root = '.'
|
@@ -22,6 +27,37 @@ module Decontaminate
|
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
30
|
+
# @!group DSL Directives
|
31
|
+
|
32
|
+
# Produces a singular scalar value in the resulting JSON, which can be
|
33
|
+
# a string, an integer, a float, or a boolean, depending on the value
|
34
|
+
# provided for the +type+ argument. The default is +:string+. If the node
|
35
|
+
# pointed to by +xpath+ is plain text or an attribute value, then that
|
36
|
+
# value will be used directly. Otherwise, the node's text will be used
|
37
|
+
# when parsing the scalar value.
|
38
|
+
#
|
39
|
+
# The +key+ argument is optional. If it is not provided, its value will be
|
40
|
+
# inferred from +xpath+ using {infer_key}.
|
41
|
+
#
|
42
|
+
# If the +xpath+ does not point to an element, the result will be +nil+.
|
43
|
+
#
|
44
|
+
# The result may be customized by either passing a block or providing a
|
45
|
+
# value for the +transformer+ argument (passing both is an error). If
|
46
|
+
# +transformer+ is provided, then the instance method with the same name
|
47
|
+
# is called with the resulting value, and the method's return value is
|
48
|
+
# what is included in the resulting JSON. If a block is provided, it is
|
49
|
+
# called with the resulting value in the context of the instance (so
|
50
|
+
# +self+ points to an instance of the decontaminator), and its return
|
51
|
+
# value is what is included in the resulting JSON.
|
52
|
+
#
|
53
|
+
# @param xpath [String] A relative XPath string that points to the scalar
|
54
|
+
# value
|
55
|
+
# @param type [Symbol] One of either +:string+, +:integer+, +:float+, or
|
56
|
+
# +:boolean+
|
57
|
+
# @param key [String] The key for the value in the resulting JSON
|
58
|
+
# @param transformer [Symbol] A symbol with the name of an instance method
|
59
|
+
# used to transform the result
|
60
|
+
# @param block A block used to transform the result
|
25
61
|
def scalar(xpath,
|
26
62
|
type: :string,
|
27
63
|
key: infer_key(xpath),
|
@@ -31,6 +67,26 @@ module Decontaminate
|
|
31
67
|
add_decoder key, Decontaminate::Decoder::Scalar.new(xpath, type, block)
|
32
68
|
end
|
33
69
|
|
70
|
+
# The plural version of {scalar}. Produces an array of scalars for the
|
71
|
+
# given key in the resulting JSON. If the element path is specified using
|
72
|
+
# the +xpath+ argument, then the individual elements are inferred using
|
73
|
+
# {infer_plural_path}. Otherwise, if the elements are specified using the
|
74
|
+
# +path+ argument, no inference is performed, and the path must return a
|
75
|
+
# set of multiple elements.
|
76
|
+
#
|
77
|
+
# If +xpath+ is provided, but the parent element does not exist, the
|
78
|
+
# result will be an empty array, not +nil+.
|
79
|
+
#
|
80
|
+
# @param xpath [String] A relative XPath string that points to a parent
|
81
|
+
# element containing singular values (mutually exclusive with +path+)
|
82
|
+
# @param path [String] A relative XPath string that points to the singular
|
83
|
+
# values (mutually exclusive with +xpath+)
|
84
|
+
# @param type [Symbol] One of either +:string+, +:integer+, +:float+, or
|
85
|
+
# +:boolean+
|
86
|
+
# @param key [String] The key for the value in the resulting JSON
|
87
|
+
# @param transformer [Symbol] A symbol with the name of an instance method
|
88
|
+
# used to transform the result
|
89
|
+
# @param block A block used to transform the result
|
34
90
|
def scalars(xpath = nil,
|
35
91
|
path: nil,
|
36
92
|
type: :string,
|
@@ -47,6 +103,23 @@ module Decontaminate
|
|
47
103
|
add_decoder key, decoder
|
48
104
|
end
|
49
105
|
|
106
|
+
# Like {scalar} but using multiple +paths+. By default, this will produce
|
107
|
+
# a fixed-length array in the resulting JSON containing an element for
|
108
|
+
# each path.
|
109
|
+
#
|
110
|
+
# This is especially useful when combined with a block or transformer,
|
111
|
+
# since the result can be a value computed from the individual element
|
112
|
+
# values. When the block or transformer method is called, it is provided
|
113
|
+
# a _separate_ argument for _each_ value in the tuple.
|
114
|
+
#
|
115
|
+
# @param paths [Array<String>] An array of relative XPath strings that
|
116
|
+
# each point to a scalar value
|
117
|
+
# @param key [String] The key for the value in the resulting JSON
|
118
|
+
# @param type [Symbol] One of either +:string+, +:integer+, +:float+, or
|
119
|
+
# +:boolean+
|
120
|
+
# @param transformer [Symbol] A symbol with the name of an instance method
|
121
|
+
# used to transform the result
|
122
|
+
# @param block A block used to transform the result
|
50
123
|
def tuple(paths,
|
51
124
|
key:,
|
52
125
|
type: :string,
|
@@ -60,51 +133,138 @@ module Decontaminate
|
|
60
133
|
add_decoder key, decoder
|
61
134
|
end
|
62
135
|
|
136
|
+
# Produces a hash in the resulting JSON with values determined by the body
|
137
|
+
# of the provided block. The {hash} directive allows for logical grouping
|
138
|
+
# of values by nesting them within separate hash structures. The body of
|
139
|
+
# the provided block works just like the surrounding context---directives
|
140
|
+
# such as {scalar}, {tuple}, and even {hash} may be further nested within
|
141
|
+
# the hash body.
|
142
|
+
#
|
143
|
+
# The +xpath+ argument is optional if +key+ is provided. If +xpath+ is
|
144
|
+
# provided, it will scope the block to the element referenced by +xpath+
|
145
|
+
# like {with}. In fact, providing +xpath+ works exactly the same as
|
146
|
+
# wrapping the body of {hash} with a {with} directive.
|
147
|
+
#
|
148
|
+
# The +key+ argument is optional if +xpath+ is provided. If +key+ is not
|
149
|
+
# provided, its value will be inferred from +xpath+ using {infer_key}.
|
150
|
+
#
|
151
|
+
# If the +xpath+ does not point to an element, the result will be +nil+.
|
152
|
+
#
|
153
|
+
# @param xpath [String] A relative XPath string that points to the parent
|
154
|
+
# node for all values within the hash
|
155
|
+
# @param key [String] The key for the value in the resulting JSON
|
156
|
+
# @param body A block that includes directives that specify the contents
|
157
|
+
# of the hash
|
63
158
|
def hash(xpath = '.', key: infer_key(xpath), &body)
|
64
|
-
decontaminator = Class.new(
|
159
|
+
decontaminator = Class.new(class_to_inherit_from, &body)
|
65
160
|
add_decoder key, Decontaminate::Decoder::Hash.new(xpath, decontaminator)
|
66
161
|
end
|
67
162
|
|
163
|
+
# The plural version of {hash}. Produces an array of hashes for the given
|
164
|
+
# key in the resulting JSON. If the element path is specified using the
|
165
|
+
# +xpath+ argument, then the individual elements are inferred using
|
166
|
+
# {infer_plural_path}. Otherwise, if the elements are specified using the
|
167
|
+
# +path+ argument, no inference is performed, and the path must return a
|
168
|
+
# set of multiple elements.
|
169
|
+
#
|
170
|
+
# If +xpath+ is provided, but the parent element does not exist, the
|
171
|
+
# result will be an empty array, not +nil+.
|
172
|
+
#
|
173
|
+
# @param xpath [String] A relative XPath string that points to a parent
|
174
|
+
# element containing singular values (mutually exclusive with +path+)
|
175
|
+
# @param path [String] A relative XPath string that points to the singular
|
176
|
+
# values (mutually exclusive with +xpath+)
|
177
|
+
# @param key [String] The key for the value in the resulting JSON
|
178
|
+
# @param body A block that includes directives that specify the contents
|
179
|
+
# of the hash
|
68
180
|
def hashes(xpath = nil, path: nil, key: nil, &body)
|
69
181
|
resolved_path = path || infer_plural_path(xpath)
|
70
182
|
key ||= infer_key(path || xpath)
|
71
183
|
|
72
|
-
decontaminator = Class.new(
|
184
|
+
decontaminator = Class.new(class_to_inherit_from, &body)
|
73
185
|
singular = Decontaminate::Decoder::Hash.new('.', decontaminator)
|
74
186
|
decoder = Decontaminate::Decoder::Array.new(resolved_path, singular)
|
75
187
|
|
76
188
|
add_decoder key, decoder
|
77
189
|
end
|
78
190
|
|
191
|
+
# Scopes all nested directives to the node referenced by the +xpath+
|
192
|
+
# argument. Unlike other directives, {with} does not produce any value in
|
193
|
+
# the resulting JSON. Instead, it just adjusts all nested paths so that
|
194
|
+
# they are relative to the node referenced by +xpath+.
|
195
|
+
#
|
196
|
+
# @param xpath [String] A relative XPath string that points to a single
|
197
|
+
# node
|
198
|
+
# @param body A block that includes directives that are scoped to the
|
199
|
+
# referenced node
|
79
200
|
def with(xpath, &body)
|
80
201
|
this = self
|
81
|
-
decontaminator = Class.new(
|
202
|
+
decontaminator = Class.new(class_to_inherit_from)
|
82
203
|
|
83
204
|
decontaminator.instance_eval do
|
205
|
+
# proxy add_decoder to the parent class
|
84
206
|
define_singleton_method :add_decoder do |key, decoder|
|
85
207
|
proxy = Decontaminate::Decoder::ChildNodeProxy.new(xpath, decoder)
|
86
208
|
this.add_decoder key, proxy
|
87
209
|
end
|
210
|
+
|
211
|
+
# also, don't let subclasses inherit from this; they would also pick
|
212
|
+
# up the overridden add_decoder method
|
213
|
+
define_singleton_method :class_to_inherit_from do
|
214
|
+
this
|
215
|
+
end
|
88
216
|
end
|
89
217
|
|
90
218
|
decontaminator.class_eval(&body)
|
91
219
|
end
|
92
220
|
|
221
|
+
# @!endgroup
|
222
|
+
|
223
|
+
# Registers an XML decoder under the given key. This method is called by
|
224
|
+
# all of the DSL directives when registering a decoder into the internal
|
225
|
+
# list of decoders. It can be overridden by subclasses to customize how
|
226
|
+
# adding decoders works (this is done anonymously by {with}).
|
227
|
+
#
|
228
|
+
# @param key [String] The key the decoder will produce in the resulting
|
229
|
+
# JSON
|
230
|
+
# @param decoder [Decontaminate::Decoder] A decoder instance used to
|
231
|
+
# produce the result value
|
93
232
|
def add_decoder(key, decoder)
|
94
233
|
fail "Decoder already registered for key #{key}" if decoders.key? key
|
95
234
|
decoders[key] = decoder
|
96
235
|
end
|
97
236
|
|
237
|
+
# Infers the name of a key for use in a JSON object from an XPath query
|
238
|
+
# string. This is used by the DSL directives to infer a key when one is
|
239
|
+
# not provided. By default, it strips a leading +@+ sign, removes all
|
240
|
+
# leading or trailing underscores, then converts the string to an
|
241
|
+
# underscore-separated string using {ActiveSupport::Inflector#underscore}.
|
242
|
+
#
|
243
|
+
# @param xpath [String] An XPath query string
|
244
|
+
# @return [String] A key for use in a JSON object
|
98
245
|
def infer_key(xpath)
|
99
246
|
xpath.delete('@').gsub(/^_+|_+$/, '').underscore
|
100
247
|
end
|
101
248
|
|
249
|
+
# Infers that path for singular elements from an XPath string referring to
|
250
|
+
# a plural parent element. This is used by {scalars} and {hashes} to infer
|
251
|
+
# their singular elements. By default, it finds the last path element,
|
252
|
+
# splitting the string on forward-slash characters, converts it to a
|
253
|
+
# singular by using {ActiveSupport::Inflector#singularize}, and appends
|
254
|
+
# the result to the whole path.
|
255
|
+
#
|
256
|
+
# @param xpath [String] An XPath query string
|
257
|
+
# @return [String] An inferred path to the singular elements
|
102
258
|
def infer_plural_path(xpath)
|
103
259
|
xpath + '/' + xpath.split('/').last.singularize
|
104
260
|
end
|
105
261
|
|
106
262
|
private
|
107
263
|
|
264
|
+
def class_to_inherit_from
|
265
|
+
self
|
266
|
+
end
|
267
|
+
|
108
268
|
def self_proc_for_method(sym)
|
109
269
|
sym && proc { |*args| send sym, *args }
|
110
270
|
end
|
@@ -112,11 +272,18 @@ module Decontaminate
|
|
112
272
|
|
113
273
|
attr_reader :xml_node
|
114
274
|
|
275
|
+
# Instantiates a decontaminator with a Nokogiri XML node or document.
|
276
|
+
#
|
277
|
+
# @param xml_node [Nokogiri::XML::Node] The XML node or document to
|
278
|
+
# decontaminate
|
115
279
|
def initialize(xml_node, instance: nil)
|
116
280
|
@xml_node = xml_node
|
117
281
|
@instance = instance
|
118
282
|
end
|
119
283
|
|
284
|
+
# Retrieves the decontaminated JSON representation of the given XML node.
|
285
|
+
#
|
286
|
+
# @return [Hash] The decontaminated JSON object
|
120
287
|
def as_json
|
121
288
|
acc = {}
|
122
289
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decontaminate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexis King
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -131,6 +131,7 @@ extra_rdoc_files: []
|
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
133
|
- ".travis.yml"
|
134
|
+
- ".yardopts"
|
134
135
|
- Gemfile
|
135
136
|
- LICENSE.txt
|
136
137
|
- README.md
|