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 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