flex_args 0.1.2 → 0.1.3

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
  SHA256:
3
- metadata.gz: 5700aa1d881f51e7f2f44d30075e68367202e010ec71779193042f7efea70d95
4
- data.tar.gz: aa46d04734fed4cf7776c3fe0c18c8fad391f5dc66a446c4fc664b8284deec79
3
+ metadata.gz: 4e573bedff931ba6d00a805a59c72c1501b454ce2de48efd1787d4e9d3582541
4
+ data.tar.gz: a33de0609ca8726513b5576f9ce5b9dec7c6ae7a0d9438d3151ce2e03a87c0c3
5
5
  SHA512:
6
- metadata.gz: bc38f0a24e8d3589adacadabfeac2e1dfa7ce84ec2bd7b9def3fa6d2ef5bc2159ac1856136da273ea8a0f34c072ecfbb7008925544f9c27dec93edbf37206791
7
- data.tar.gz: c349ad91b2f10bb6b463d88388cd34c7f48fba0c736de18754545b88f6aa0f02f5ec60e39ea685537046cf6629bae9188b12531935a98f2bd7db37b8902223da
6
+ metadata.gz: e6a44342146b8d223a593a2982f2930bec00db7c93ee8f604ddfa10fd0380562d0ce8577bd636e8fe54082acaf49a39627e4c1dd969898a79c363e911bcf968b
7
+ data.tar.gz: 45955b80f48f89a16d0b64caf3fbb6a14fbbf0e39fb84e625a7201678bd656d246c73c6d6bb86930e4b8e8a3d5bd9ab5fa927ea4489723674cc69f19448bcca5
data/.rdoc_options CHANGED
@@ -8,7 +8,7 @@ autolink_excluded_words: []
8
8
  charset: UTF-8
9
9
  class_module_path_prefix:
10
10
  embed_mixins: false
11
- exclude: [tmp/*]
11
+ exclude: [tmp/*,coverage/*]
12
12
  file_path_prefix:
13
13
  hyperlink_all: false
14
14
  line_numbers: false
data/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # flex_args
2
2
 
3
- Flexible Argument Parse
3
+ Flexible Argument Parser
@@ -31,8 +31,44 @@ class FlexArgs
31
31
  # new(:for_some_int, 1..9).("1") => [:ok, 1]
32
32
  # new(:for_some_int, 1..9).("0") => [:error, "range constraint for_some_int: 1..9 violated by value \"0\""]
33
33
  # new(:for_some_int, 1..9).("y") => [:error, "range constraint for_some_int: 1..9 violated by value \"y\""]
34
+ #
35
+ # new(:for_string, "a".."c").("a") => [:ok, "a"]
36
+ # new(:for_string, "a".."c").("d") => [:error, 'member constraint for_string: "a".."c" violated by value "d"']
34
37
  #
35
38
  # ```
39
+ #
40
+ # **N.B.** that ranges not of type integer are treated as `Member` contraints below.
41
+ #
42
+ # ### `Member` constraint
43
+ #
44
+ # While the `Range` constraint above does int type casting if needed, the `Member` constraint cannot do
45
+ # that, because it cannot access the type of the container against which membership will be tested.
46
+ #
47
+ # `Member` constraints can be triggered by providing a `Set`, `Array` or `Hash` second parameter
48
+ #
49
+ # ```spec # Various membership rdoc specs
50
+ # vowels = %w[a e i o u y]
51
+ # hashy = Hash[vowels.product([true])]
52
+ # set = Set.new(vowels)
53
+ # in_array = new(:in_array, vowels)
54
+ # in_set = new(:in_set, set)
55
+ # in_hash = new(:in_hash, hashy)
56
+ #
57
+ # expect(in_array.("a")).to eq([:ok, "a"])
58
+ # expect(in_array.("y")).to eq([:ok, "y"])
59
+ # expect(in_set.("y")).to eq([:ok, "y"])
60
+ # expect(in_hash.("o")).to eq([:ok, "o"])
61
+ #
62
+ # expect(in_array.("b"))
63
+ # .to eq([:error, "member constraint in_array: #{vowels.inspect} violated by value \"b\""])
64
+ #
65
+ # expect(in_hash.("ec"))
66
+ # .to eq([:error, "member constraint in_hash: #{hashy.inspect} violated by value \"ec\""])
67
+ #
68
+ # expect(in_set.("aa"))
69
+ # .to eq([:error, "member constraint in_set: #{set.inspect} violated by value \"aa\""])
70
+ # ```
71
+ #
36
72
  # ### Custom constraint
37
73
  #
38
74
  # This is easily implemented by passing a block which will return a pair as described above, but also note
@@ -71,6 +107,12 @@ class FlexArgs
71
107
  _init_regexp(name, constraint)
72
108
  in [Range => range]
73
109
  _init_range(name, range)
110
+ in [Array | Hash | Set => container]
111
+ _init_membership(name, container, false)
112
+ in [Array | Hash | Set => container, :autocast]
113
+ _init_membership(name, container, true)
114
+ in [:member, container]
115
+ _init_membership(name, container)
74
116
  else
75
117
  raise ArgumentError,
76
118
  "illegal constraint spec #{constraints.inspect} for value #{name}" unless block
@@ -102,7 +144,36 @@ class FlexArgs
102
144
  end
103
145
  end
104
146
 
147
+ def _init_membership(name, container, autocast)
148
+ @constrainer = -> value do
149
+ if container.member?(value)
150
+ [:ok, value]
151
+ elsif autocast
152
+ case _to_int(value)
153
+ in [:ok, int_val]
154
+ if container.member(int_val)
155
+ [:ok, int_val]
156
+ else
157
+ _make_error(:member, name, container.inspect, int_value)
158
+ end
159
+ else
160
+ _make_error(:member, name, container.inspect, value)
161
+ end
162
+ else
163
+ _make_error(:member, name, container.inspect, value)
164
+ end
165
+ end
166
+ end
167
+
105
168
  def _init_range(name, range)
169
+ if Integer === range.first
170
+ _init_int_range(name, range)
171
+ else
172
+ _init_membership(name, range, false)
173
+ end
174
+ end
175
+
176
+ def _init_int_range(name, range)
106
177
  @constrainer = -> value do
107
178
  case _to_int(value)
108
179
  in [:ok, int_val]
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FlexArgs
4
+ class Transform
5
+
6
+ def call(value) = [:ok, @transformer.(value)]
7
+
8
+ private
9
+ def initialize(*transformers, &transformer)
10
+ case transformers
11
+ in [_]
12
+ raise ArgumentError, "NOT YET IMPLEMENTED"
13
+ else
14
+ _init_transformer(&transformer)
15
+ end
16
+ end
17
+
18
+ def _init_transformer(&transformer)
19
+ @transformer = transformer
20
+ end
21
+ end
22
+ end
23
+ # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -2,7 +2,7 @@
2
2
 
3
3
  class FlexArgs
4
4
  module Version
5
- VERSION="0.1.2"
5
+ VERSION="0.1.3"
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
data/lib/flex_args.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'ostruct'
4
4
  require_relative 'flex_args/constraint'
5
5
  require_relative 'flex_args/parser'
6
+ require_relative 'flex_args/transform'
6
7
 
7
8
  ##
8
9
  # ## Abstract
@@ -91,8 +92,8 @@ require_relative 'flex_args/parser'
91
92
  # - required
92
93
  # - domain checks
93
94
  # - format checks
94
- # - Custom Transformations
95
- # - Semantic Checks (v0.2)
95
+ # - Transformations
96
+ # - Semantic Checks - between multiple values (v0.2)
96
97
  #
97
98
  # and they are documented in the rdocs of the corresponding methods
98
99
  #
@@ -225,11 +226,76 @@ class FlexArgs
225
226
  #
226
227
  # ```
227
228
  #
228
- # Please find the documentation about more constraints [here](Constraint.html)
229
+ # #### Combining Constraints
230
+ #
231
+ # More than one constraint can be imposed on a value
232
+ #
233
+ # Firstly one can combine any constraint with a custom constraint.
234
+ #
235
+ # **N.B.** That in this case the custom constraint is executed last, therefore
236
+ #
237
+ # ```spec # Order of constraints
238
+ # parser = FlexArgs.new
239
+ # .constrain(:n, 1..10) { it == 5 ? [:ok, 100] : [:ok, 2*it] }
240
+ #
241
+ # expect(parser.parse(%w[n:5]).values).to eq(n: 100)
242
+ #
243
+ # ```
244
+ #
245
+ # However if we had defined the custom constraint (which actually is a transformer
246
+ # as it _always_ returns an `:ok` value, the range constraint would fail
247
+ #
248
+ # ```spec # Bad order
249
+ # parser = FlexArgs.new
250
+ # .constrain(:n) { it == 5 ? [:ok, 100] : [:ok, 2*it] }
251
+ # .constrain(:n, 1..10)
252
+ #
253
+ # expect { parser.parse(%w[n:5]) }
254
+ # .to raise_error(ArgumentError)
255
+ #
256
+ # ```
257
+ #
258
+ # As mentioned before, custom constraints that always return `:ok` values are indeed
259
+ # _transformers_ and can be written more with the `transform` [method below](/FlexArgs.html#method-i-transform-label-Transformers)
260
+ #
261
+ #
262
+ # Please find the documentation about more constraints [here](/FlexArgs/Constraint.html)
229
263
  #
230
264
  def constrain(value, *constraints, &block)
231
265
  @allowed_values << value.to_sym if @allowed_values
232
- @constraints[value.to_sym] << Constraint.new(value, *constraints, &block)
266
+ @constraints[value.to_sym] << Constraint.new(value, *constraints) unless constraints.empty?
267
+ @constraints[value.to_sym] << Constraint.new(value, &block) if block
268
+ self
269
+ end
270
+
271
+ ##
272
+ # ## Transformers
273
+ #
274
+ # Transformers and Constraints are executed in the order they are defined
275
+ #
276
+ # ```spec # Transformer assures constraint passes
277
+ #
278
+ # parser = FlexArgs.new
279
+ # .transform(:word) { it.downcase }
280
+ # .constrain(:word, "a".."z")
281
+ #
282
+ # expect(parser.parse(%w[word:A]).values).to eq(word: "a")
283
+ # ```
284
+ #
285
+ # And therefore
286
+ #
287
+ # ```spec # Transformer called too late to assure constraint passes
288
+ #
289
+ # parser = FlexArgs.new
290
+ # .constrain(:word, "a".."z")
291
+ # .transform(:word) { it.downcase }
292
+ #
293
+ # expect { parser.parse(%w[word:A]) }.to raise_error(ArgumentError)
294
+ # ```
295
+ def transform(value, *transforms, &transformer)
296
+ @allowed_values << value.to_sym if @allowed_values
297
+ @constraints[value.to_sym] << Transform.new(*transforms) unless transforms.empty?
298
+ @constraints[value.to_sym] << Transform.new(&transformer) if transformer
233
299
  self
234
300
  end
235
301
 
@@ -241,7 +307,7 @@ class FlexArgs
241
307
  def values
242
308
  default_values.merge(values_from_args)
243
309
  end
244
-
310
+
245
311
  private
246
312
  def initialize
247
313
  @alias_definitions = Hash.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flex_args
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
@@ -38,6 +38,7 @@ files:
38
38
  - lib/flex_args/constraint.rb
39
39
  - lib/flex_args/enumerable.rb
40
40
  - lib/flex_args/parser.rb
41
+ - lib/flex_args/transform.rb
41
42
  - lib/flex_args/version.rb
42
43
  homepage: https://codeberg.org/lab419/flex_args
43
44
  licenses: