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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e50de59b7c72741fecdddd032de8e3bd32847aca
4
- data.tar.gz: 85c3625a970857a8eac135b1adf3eab16336c65d
3
+ metadata.gz: 579118a9ae1379d01aebacbd278344e384491251
4
+ data.tar.gz: 120fd509e2e662fbcd1fc63774c07e6d5dd3fa1f
5
5
  SHA512:
6
- metadata.gz: bb57171c607cc67515433ee9faf216ea201faca1e1411a799c93dec1ceb3d966574dc754b605ba11ea1316967bd4f7dd1053cd4a1bf5a4790c32a235784170ee
7
- data.tar.gz: 0da2b2eddfa07ffaf1e0bf5b77450deb3d123878c14aebfa97a010d3c1de02bbdfd2dc3faafcd1e714d7ebbe6053188fd5fa6cf10af983f8a9be79257fbeb4e6
6
+ metadata.gz: 973c6a4310626adcabf496df4f265d40443e87d71e6855efe64979e5b56bb980fdc96ed5aa4f6172197b84093a5d962387095c3c39315e7e83564f78361917ff
7
+ data.tar.gz: 59b7fb6c09ae5fd00e88dadfa1decb1c36971b610e2922be2b69e10cd3e69bdec83dfded71699842d9edfcfbf651d9c67cbc2d7770dbc6742c72cf68338814f6
@@ -0,0 +1,2 @@
1
+ --no-private
2
+ -
@@ -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 extraction
11
- # parsers. A DSL is exposed via class methods to allow specifying how the XML
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(Decontaminate::Decontaminator, &body)
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(Decontaminate::Decontaminator, &body)
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(Decontaminate::Decontaminator)
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
 
@@ -1,3 +1,3 @@
1
1
  module Decontaminate
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
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.2.2
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-21 00:00:00.000000000 Z
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