decontaminate 0.2.2 → 0.3.0
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.
- 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
|