cmdx 2.0.1 → 2.1.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/CHANGELOG.md +53 -8
- data/lib/cmdx/callbacks.rb +31 -11
- data/lib/cmdx/chain.rb +29 -10
- data/lib/cmdx/coercions/big_decimal.rb +1 -1
- data/lib/cmdx/coercions/boolean.rb +3 -9
- data/lib/cmdx/coercions/coerce.rb +4 -1
- data/lib/cmdx/coercions/date_time.rb +1 -1
- data/lib/cmdx/coercions/integer.rb +11 -2
- data/lib/cmdx/coercions/symbol.rb +23 -4
- data/lib/cmdx/coercions.rb +25 -10
- data/lib/cmdx/configuration.rb +31 -16
- data/lib/cmdx/context.rb +36 -52
- data/lib/cmdx/deprecation.rb +4 -7
- data/lib/cmdx/deprecators/error.rb +4 -1
- data/lib/cmdx/deprecators.rb +17 -8
- data/lib/cmdx/errors.rb +11 -10
- data/lib/cmdx/executors/fiber.rb +16 -4
- data/lib/cmdx/executors/thread.rb +18 -4
- data/lib/cmdx/executors.rb +22 -7
- data/lib/cmdx/fault.rb +15 -3
- data/lib/cmdx/i18n_proxy.rb +9 -5
- data/lib/cmdx/input.rb +23 -21
- data/lib/cmdx/inputs.rb +14 -26
- data/lib/cmdx/log_formatters/json.rb +8 -1
- data/lib/cmdx/log_formatters/logstash.rb +7 -1
- data/lib/cmdx/mergers.rb +22 -7
- data/lib/cmdx/middlewares.rb +40 -24
- data/lib/cmdx/output.rb +5 -2
- data/lib/cmdx/pipeline.rb +18 -3
- data/lib/cmdx/railtie.rb +1 -0
- data/lib/cmdx/result.rb +22 -6
- data/lib/cmdx/retriers/decorrelated_jitter.rb +10 -5
- data/lib/cmdx/retriers/exponential.rb +10 -2
- data/lib/cmdx/retriers/fibonacci.rb +29 -12
- data/lib/cmdx/retriers.rb +17 -8
- data/lib/cmdx/retry.rb +20 -13
- data/lib/cmdx/runtime.rb +18 -17
- data/lib/cmdx/settings.rb +9 -9
- data/lib/cmdx/signal.rb +1 -1
- data/lib/cmdx/task.rb +90 -45
- data/lib/cmdx/telemetry.rb +37 -10
- data/lib/cmdx/util.rb +50 -4
- data/lib/cmdx/validators/absence.rb +1 -1
- data/lib/cmdx/validators/exclusion.rb +15 -15
- data/lib/cmdx/validators/format.rb +12 -4
- data/lib/cmdx/validators/inclusion.rb +15 -15
- data/lib/cmdx/validators/length.rb +5 -49
- data/lib/cmdx/validators/numeric.rb +5 -49
- data/lib/cmdx/validators/presence.rb +1 -1
- data/lib/cmdx/validators/validate.rb +7 -1
- data/lib/cmdx/validators.rb +21 -9
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/workflow.rb +28 -14
- data/lib/cmdx.rb +24 -0
- data/lib/generators/cmdx/templates/install.rb +80 -39
- data/mkdocs.yml +1 -0
- metadata +1 -1
|
@@ -15,17 +15,25 @@ module CMDx
|
|
|
15
15
|
# @option options [String] :message override for the failure message
|
|
16
16
|
# @return [Validators::Failure, nil]
|
|
17
17
|
# @raise [ArgumentError] when neither `:with` nor `:without` is given
|
|
18
|
+
# @note Non-String values that do not respond to `#match?` fail with the
|
|
19
|
+
# regular format failure rather than raise `NoMethodError`. Coerce inputs
|
|
20
|
+
# to String beforehand when format checks are required.
|
|
18
21
|
def call(value, options = EMPTY_HASH)
|
|
22
|
+
str = value.nil? || value.respond_to?(:match?) ? value : value.to_s
|
|
23
|
+
|
|
19
24
|
match =
|
|
20
25
|
case options
|
|
21
26
|
in with:, without:
|
|
22
|
-
|
|
27
|
+
str&.match?(with) && !str&.match?(without)
|
|
23
28
|
in with:
|
|
24
|
-
|
|
29
|
+
str&.match?(with)
|
|
25
30
|
in without:
|
|
26
|
-
!
|
|
31
|
+
!str&.match?(without)
|
|
27
32
|
else
|
|
28
|
-
raise ArgumentError,
|
|
33
|
+
raise ArgumentError, <<~MSG.chomp
|
|
34
|
+
format validator requires :with and/or :without (got #{options.keys.inspect}).
|
|
35
|
+
See https://drexed.github.io/cmdx/inputs/validations/#format
|
|
36
|
+
MSG
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
return if match
|
|
@@ -19,22 +19,29 @@ module CMDx
|
|
|
19
19
|
# @raise [ArgumentError] when neither `:in` nor `:within` is given
|
|
20
20
|
def call(value, options = EMPTY_HASH)
|
|
21
21
|
values = options[:in] || options[:within]
|
|
22
|
-
|
|
22
|
+
if values.nil?
|
|
23
|
+
raise ArgumentError, <<~MSG.chomp
|
|
24
|
+
inclusion validator requires :in or :within (got #{options.keys.inspect}).
|
|
25
|
+
See https://drexed.github.io/cmdx/inputs/validations/#inclusion
|
|
26
|
+
MSG
|
|
27
|
+
elsif values.is_a?(Hash)
|
|
28
|
+
raise ArgumentError, <<~MSG.chomp
|
|
29
|
+
inclusion validator :in/:within does not accept a Hash; pass an Array,
|
|
30
|
+
Set, Range, or other Enumerable (e.g. `#{values.inspect}.keys`).
|
|
31
|
+
See https://drexed.github.io/cmdx/inputs/validations/#inclusion
|
|
32
|
+
MSG
|
|
33
|
+
end
|
|
23
34
|
|
|
24
35
|
if values.is_a?(Range)
|
|
25
36
|
within_failure(values.begin, values.end, options) unless values.cover?(value)
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
else
|
|
38
|
+
enum = values.is_a?(Enumerable) ? values : [values]
|
|
39
|
+
of_failure(enum, options) if enum.none? { |v| v === value }
|
|
28
40
|
end
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
private
|
|
32
44
|
|
|
33
|
-
# @param values [Enumerable] collection rendered into the failure message
|
|
34
|
-
# @param options [Hash{Symbol => Object}]
|
|
35
|
-
# @option options [String] :of_message
|
|
36
|
-
# @option options [String] :message
|
|
37
|
-
# @return [Validators::Failure]
|
|
38
45
|
def of_failure(values, options)
|
|
39
46
|
values = values.map(&:inspect).join(", ")
|
|
40
47
|
message = options[:of_message] || options[:message]
|
|
@@ -43,13 +50,6 @@ module CMDx
|
|
|
43
50
|
Failure.new(message || I18nProxy.t("cmdx.validators.inclusion.of", values:))
|
|
44
51
|
end
|
|
45
52
|
|
|
46
|
-
# @param min [Object] range/inclusion lower bound
|
|
47
|
-
# @param max [Object] range/inclusion upper bound
|
|
48
|
-
# @param options [Hash{Symbol => Object}]
|
|
49
|
-
# @option options [String] :in_message
|
|
50
|
-
# @option options [String] :within_message
|
|
51
|
-
# @option options [String] :message
|
|
52
|
-
# @return [Validators::Failure]
|
|
53
53
|
def within_failure(min, max, options)
|
|
54
54
|
message = options[:in_message] || options[:within_message] || options[:message]
|
|
55
55
|
message %= { min:, max: } unless message.nil?
|
|
@@ -63,28 +63,21 @@ module CMDx
|
|
|
63
63
|
in is_not:
|
|
64
64
|
is_not_failure(is_not, options) if length == is_not
|
|
65
65
|
else
|
|
66
|
-
raise ArgumentError,
|
|
66
|
+
raise ArgumentError, <<~MSG.chomp
|
|
67
|
+
unknown length validator options #{options.keys.inspect};
|
|
68
|
+
expected one of [:within, :not_within, :in, :not_in, :min, :max, :gt, :lt, :is, :is_not] (aliases: :gte, :lte, :eq, :not_eq).
|
|
69
|
+
See https://drexed.github.io/cmdx/inputs/validations/#length
|
|
70
|
+
MSG
|
|
67
71
|
end
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
private
|
|
71
75
|
|
|
72
|
-
# @param options [Hash{Symbol => Object}]
|
|
73
|
-
# @option options [String] :nil_message
|
|
74
|
-
# @option options [String] :message
|
|
75
|
-
# @return [Validators::Failure]
|
|
76
76
|
def nil_failure(options)
|
|
77
77
|
message = options[:nil_message] || options[:message]
|
|
78
78
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.nil_value"))
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
# @param min [Object]
|
|
82
|
-
# @param max [Object]
|
|
83
|
-
# @param options [Hash{Symbol => Object}]
|
|
84
|
-
# @option options [String] :within_message
|
|
85
|
-
# @option options [String] :in_message
|
|
86
|
-
# @option options [String] :message
|
|
87
|
-
# @return [Validators::Failure]
|
|
88
81
|
def within_failure(min, max, options)
|
|
89
82
|
message = options[:within_message] || options[:in_message] || options[:message]
|
|
90
83
|
message %= { min:, max: } unless message.nil?
|
|
@@ -92,13 +85,6 @@ module CMDx
|
|
|
92
85
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.within", min:, max:))
|
|
93
86
|
end
|
|
94
87
|
|
|
95
|
-
# @param min [Object]
|
|
96
|
-
# @param max [Object]
|
|
97
|
-
# @param options [Hash{Symbol => Object}]
|
|
98
|
-
# @option options [String] :not_within_message
|
|
99
|
-
# @option options [String] :not_in_message
|
|
100
|
-
# @option options [String] :message
|
|
101
|
-
# @return [Validators::Failure]
|
|
102
88
|
def not_within_failure(min, max, options)
|
|
103
89
|
message = options[:not_within_message] || options[:not_in_message] || options[:message]
|
|
104
90
|
message %= { min:, max: } unless message.nil?
|
|
@@ -106,11 +92,6 @@ module CMDx
|
|
|
106
92
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.not_within", min:, max:))
|
|
107
93
|
end
|
|
108
94
|
|
|
109
|
-
# @param min [Object]
|
|
110
|
-
# @param options [Hash{Symbol => Object}]
|
|
111
|
-
# @option options [String] :min_message
|
|
112
|
-
# @option options [String] :message
|
|
113
|
-
# @return [Validators::Failure]
|
|
114
95
|
def min_failure(min, options)
|
|
115
96
|
message = options[:min_message] || options[:message]
|
|
116
97
|
message %= { min: } unless message.nil?
|
|
@@ -118,11 +99,6 @@ module CMDx
|
|
|
118
99
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.min", min:))
|
|
119
100
|
end
|
|
120
101
|
|
|
121
|
-
# @param max [Object]
|
|
122
|
-
# @param options [Hash{Symbol => Object}]
|
|
123
|
-
# @option options [String] :max_message
|
|
124
|
-
# @option options [String] :message
|
|
125
|
-
# @return [Validators::Failure]
|
|
126
102
|
def max_failure(max, options)
|
|
127
103
|
message = options[:max_message] || options[:message]
|
|
128
104
|
message %= { max: } unless message.nil?
|
|
@@ -130,11 +106,6 @@ module CMDx
|
|
|
130
106
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.max", max:))
|
|
131
107
|
end
|
|
132
108
|
|
|
133
|
-
# @param gt [Object]
|
|
134
|
-
# @param options [Hash{Symbol => Object}]
|
|
135
|
-
# @option options [String] :gt_message
|
|
136
|
-
# @option options [String] :message
|
|
137
|
-
# @return [Validators::Failure]
|
|
138
109
|
def gt_failure(gt, options)
|
|
139
110
|
message = options[:gt_message] || options[:message]
|
|
140
111
|
message %= { gt: } unless message.nil?
|
|
@@ -142,11 +113,6 @@ module CMDx
|
|
|
142
113
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.gt", gt:))
|
|
143
114
|
end
|
|
144
115
|
|
|
145
|
-
# @param lt [Object]
|
|
146
|
-
# @param options [Hash{Symbol => Object}]
|
|
147
|
-
# @option options [String] :lt_message
|
|
148
|
-
# @option options [String] :message
|
|
149
|
-
# @return [Validators::Failure]
|
|
150
116
|
def lt_failure(lt, options)
|
|
151
117
|
message = options[:lt_message] || options[:message]
|
|
152
118
|
message %= { lt: } unless message.nil?
|
|
@@ -154,11 +120,6 @@ module CMDx
|
|
|
154
120
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.lt", lt:))
|
|
155
121
|
end
|
|
156
122
|
|
|
157
|
-
# @param is [Object]
|
|
158
|
-
# @param options [Hash{Symbol => Object}]
|
|
159
|
-
# @option options [String] :is_message
|
|
160
|
-
# @option options [String] :message
|
|
161
|
-
# @return [Validators::Failure]
|
|
162
123
|
def is_failure(is, options) # rubocop:disable Naming/PredicatePrefix
|
|
163
124
|
message = options[:is_message] || options[:message]
|
|
164
125
|
message %= { is: } unless message.nil?
|
|
@@ -166,11 +127,6 @@ module CMDx
|
|
|
166
127
|
Failure.new(message || I18nProxy.t("cmdx.validators.length.is", is:))
|
|
167
128
|
end
|
|
168
129
|
|
|
169
|
-
# @param is_not [Object]
|
|
170
|
-
# @param options [Hash{Symbol => Object}]
|
|
171
|
-
# @option options [String] :is_not_message
|
|
172
|
-
# @option options [String] :message
|
|
173
|
-
# @return [Validators::Failure]
|
|
174
130
|
def is_not_failure(is_not, options) # rubocop:disable Naming/PredicatePrefix
|
|
175
131
|
message = options[:is_not_message] || options[:message]
|
|
176
132
|
message %= { is_not: } unless message.nil?
|
|
@@ -60,28 +60,21 @@ module CMDx
|
|
|
60
60
|
in is_not:
|
|
61
61
|
is_not_failure(is_not, options) if value == is_not
|
|
62
62
|
else
|
|
63
|
-
raise ArgumentError,
|
|
63
|
+
raise ArgumentError, <<~MSG.chomp
|
|
64
|
+
unknown numeric validator options #{options.keys.inspect};
|
|
65
|
+
expected one of [:within, :not_within, :in, :not_in, :min, :max, :gt, :lt, :is, :is_not] (aliases: :gte, :lte, :eq, :not_eq).
|
|
66
|
+
See https://drexed.github.io/cmdx/inputs/validations/#numeric
|
|
67
|
+
MSG
|
|
64
68
|
end
|
|
65
69
|
end
|
|
66
70
|
|
|
67
71
|
private
|
|
68
72
|
|
|
69
|
-
# @param options [Hash{Symbol => Object}]
|
|
70
|
-
# @option options [String] :nil_message
|
|
71
|
-
# @option options [String] :message
|
|
72
|
-
# @return [Validators::Failure]
|
|
73
73
|
def nil_failure(options)
|
|
74
74
|
message = options[:nil_message] || options[:message]
|
|
75
75
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.nil_value"))
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# @param min [Object]
|
|
79
|
-
# @param max [Object]
|
|
80
|
-
# @param options [Hash{Symbol => Object}]
|
|
81
|
-
# @option options [String] :within_message
|
|
82
|
-
# @option options [String] :in_message
|
|
83
|
-
# @option options [String] :message
|
|
84
|
-
# @return [Validators::Failure]
|
|
85
78
|
def within_failure(min, max, options)
|
|
86
79
|
message = options[:within_message] || options[:in_message] || options[:message]
|
|
87
80
|
message %= { min:, max: } unless message.nil?
|
|
@@ -89,13 +82,6 @@ module CMDx
|
|
|
89
82
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.within", min:, max:))
|
|
90
83
|
end
|
|
91
84
|
|
|
92
|
-
# @param min [Object]
|
|
93
|
-
# @param max [Object]
|
|
94
|
-
# @param options [Hash{Symbol => Object}]
|
|
95
|
-
# @option options [String] :not_within_message
|
|
96
|
-
# @option options [String] :not_in_message
|
|
97
|
-
# @option options [String] :message
|
|
98
|
-
# @return [Validators::Failure]
|
|
99
85
|
def not_within_failure(min, max, options)
|
|
100
86
|
message = options[:not_within_message] || options[:not_in_message] || options[:message]
|
|
101
87
|
message %= { min:, max: } unless message.nil?
|
|
@@ -103,11 +89,6 @@ module CMDx
|
|
|
103
89
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.not_within", min:, max:))
|
|
104
90
|
end
|
|
105
91
|
|
|
106
|
-
# @param min [Object]
|
|
107
|
-
# @param options [Hash{Symbol => Object}]
|
|
108
|
-
# @option options [String] :min_message
|
|
109
|
-
# @option options [String] :message
|
|
110
|
-
# @return [Validators::Failure]
|
|
111
92
|
def min_failure(min, options)
|
|
112
93
|
message = options[:min_message] || options[:message]
|
|
113
94
|
message %= { min: } unless message.nil?
|
|
@@ -115,11 +96,6 @@ module CMDx
|
|
|
115
96
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.min", min:))
|
|
116
97
|
end
|
|
117
98
|
|
|
118
|
-
# @param max [Object]
|
|
119
|
-
# @param options [Hash{Symbol => Object}]
|
|
120
|
-
# @option options [String] :max_message
|
|
121
|
-
# @option options [String] :message
|
|
122
|
-
# @return [Validators::Failure]
|
|
123
99
|
def max_failure(max, options)
|
|
124
100
|
message = options[:max_message] || options[:message]
|
|
125
101
|
message %= { max: } unless message.nil?
|
|
@@ -127,11 +103,6 @@ module CMDx
|
|
|
127
103
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.max", max:))
|
|
128
104
|
end
|
|
129
105
|
|
|
130
|
-
# @param gt [Object]
|
|
131
|
-
# @param options [Hash{Symbol => Object}]
|
|
132
|
-
# @option options [String] :gt_message
|
|
133
|
-
# @option options [String] :message
|
|
134
|
-
# @return [Validators::Failure]
|
|
135
106
|
def gt_failure(gt, options)
|
|
136
107
|
message = options[:gt_message] || options[:message]
|
|
137
108
|
message %= { gt: } unless message.nil?
|
|
@@ -139,11 +110,6 @@ module CMDx
|
|
|
139
110
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.gt", gt:))
|
|
140
111
|
end
|
|
141
112
|
|
|
142
|
-
# @param lt [Object]
|
|
143
|
-
# @param options [Hash{Symbol => Object}]
|
|
144
|
-
# @option options [String] :lt_message
|
|
145
|
-
# @option options [String] :message
|
|
146
|
-
# @return [Validators::Failure]
|
|
147
113
|
def lt_failure(lt, options)
|
|
148
114
|
message = options[:lt_message] || options[:message]
|
|
149
115
|
message %= { lt: } unless message.nil?
|
|
@@ -151,11 +117,6 @@ module CMDx
|
|
|
151
117
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.lt", lt:))
|
|
152
118
|
end
|
|
153
119
|
|
|
154
|
-
# @param is [Object]
|
|
155
|
-
# @param options [Hash{Symbol => Object}]
|
|
156
|
-
# @option options [String] :is_message
|
|
157
|
-
# @option options [String] :message
|
|
158
|
-
# @return [Validators::Failure]
|
|
159
120
|
def is_failure(is, options) # rubocop:disable Naming/PredicatePrefix
|
|
160
121
|
message = options[:is_message] || options[:message]
|
|
161
122
|
message %= { is: } unless message.nil?
|
|
@@ -163,11 +124,6 @@ module CMDx
|
|
|
163
124
|
Failure.new(message || I18nProxy.t("cmdx.validators.numeric.is", is:))
|
|
164
125
|
end
|
|
165
126
|
|
|
166
|
-
# @param is_not [Object]
|
|
167
|
-
# @param options [Hash{Symbol => Object}]
|
|
168
|
-
# @option options [String] :is_not_message
|
|
169
|
-
# @option options [String] :message
|
|
170
|
-
# @return [Validators::Failure]
|
|
171
127
|
def is_not_failure(is_not, options) # rubocop:disable Naming/PredicatePrefix
|
|
172
128
|
message = options[:is_not_message] || options[:message]
|
|
173
129
|
message %= { is_not: } unless message.nil?
|
|
@@ -13,6 +13,9 @@ module CMDx
|
|
|
13
13
|
# @param handler [Symbol, Proc, #call]
|
|
14
14
|
# @return [Validators::Failure, nil, Object] handler's return value
|
|
15
15
|
# @raise [ArgumentError] when `handler` isn't a supported type
|
|
16
|
+
# @note Symbol handlers are dispatched via `send` so private helpers on
|
|
17
|
+
# the task are reachable. Handlers are baked into class definitions;
|
|
18
|
+
# never derive them from untrusted input.
|
|
16
19
|
def call(task, value, handler)
|
|
17
20
|
case handler
|
|
18
21
|
when Symbol
|
|
@@ -22,7 +25,10 @@ module CMDx
|
|
|
22
25
|
else
|
|
23
26
|
return handler.call(value, task) if handler.respond_to?(:call)
|
|
24
27
|
|
|
25
|
-
raise ArgumentError,
|
|
28
|
+
raise ArgumentError, <<~MSG.chomp
|
|
29
|
+
validate handler must be a Symbol, Proc, or respond to #call (got #{handler.class}).
|
|
30
|
+
See https://drexed.github.io/cmdx/inputs/validations/#inline-validate-callable
|
|
31
|
+
MSG
|
|
26
32
|
end
|
|
27
33
|
end
|
|
28
34
|
|
data/lib/cmdx/validators.rb
CHANGED
|
@@ -45,9 +45,12 @@ module CMDx
|
|
|
45
45
|
validator = callable || block
|
|
46
46
|
|
|
47
47
|
if callable && block
|
|
48
|
-
raise ArgumentError, "provide either a callable or a block, not both"
|
|
48
|
+
raise ArgumentError, "validator: provide either a callable or a block, not both"
|
|
49
49
|
elsif !validator.respond_to?(:call)
|
|
50
|
-
raise ArgumentError,
|
|
50
|
+
raise ArgumentError, <<~MSG.chomp
|
|
51
|
+
validator must respond to #call (got #{validator.class}).
|
|
52
|
+
See https://drexed.github.io/cmdx/inputs/validations/#declarations
|
|
53
|
+
MSG
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
registry[name.to_sym] = validator
|
|
@@ -57,16 +60,25 @@ module CMDx
|
|
|
57
60
|
# @param name [Symbol]
|
|
58
61
|
# @return [Validators] self for chaining
|
|
59
62
|
def deregister(name)
|
|
60
|
-
registry.delete(name
|
|
63
|
+
registry.delete(name)
|
|
61
64
|
self
|
|
62
65
|
end
|
|
63
66
|
|
|
67
|
+
# @param name [Symbol]
|
|
68
|
+
# @return [Boolean] whether a validator is registered under `name`
|
|
69
|
+
def key?(name)
|
|
70
|
+
registry.key?(name)
|
|
71
|
+
end
|
|
72
|
+
|
|
64
73
|
# @param name [Symbol]
|
|
65
74
|
# @return [#call]
|
|
66
|
-
# @raise [
|
|
75
|
+
# @raise [UnknownEntryError] when `name` isn't registered
|
|
67
76
|
def lookup(name)
|
|
68
77
|
registry[name] || begin
|
|
69
|
-
raise
|
|
78
|
+
raise UnknownEntryError, <<~MSG.chomp
|
|
79
|
+
unknown validator #{name.inspect}; registered: #{registry.keys.inspect}.
|
|
80
|
+
See https://drexed.github.io/cmdx/inputs/validations/#built-in-validators
|
|
81
|
+
MSG
|
|
70
82
|
end
|
|
71
83
|
end
|
|
72
84
|
|
|
@@ -137,9 +149,6 @@ module CMDx
|
|
|
137
149
|
|
|
138
150
|
private
|
|
139
151
|
|
|
140
|
-
# @param raw_options [Object] truthy flag, Hash, Array, Regexp, etc. from a declaration
|
|
141
|
-
# @return [Hash{Symbol => Object}, nil] normalized rule options, or nil when disabled
|
|
142
|
-
# @raise [ArgumentError] when `raw_options` has an unsupported shape
|
|
143
152
|
def normalize_options(raw_options)
|
|
144
153
|
case raw_options
|
|
145
154
|
when FalseClass, NilClass
|
|
@@ -153,7 +162,10 @@ module CMDx
|
|
|
153
162
|
when Regexp
|
|
154
163
|
{ with: raw_options }
|
|
155
164
|
else
|
|
156
|
-
raise ArgumentError,
|
|
165
|
+
raise ArgumentError, <<~MSG.chomp
|
|
166
|
+
unsupported validator option format #{raw_options.inspect}; expected Boolean, Hash, Array, or Regexp.
|
|
167
|
+
See https://drexed.github.io/cmdx/inputs/validations/#common-options
|
|
168
|
+
MSG
|
|
157
169
|
end
|
|
158
170
|
end
|
|
159
171
|
|
data/lib/cmdx/version.rb
CHANGED
data/lib/cmdx/workflow.rb
CHANGED
|
@@ -24,10 +24,11 @@ module CMDx
|
|
|
24
24
|
@pipeline ||= []
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
# Declares a task group.
|
|
28
|
-
#
|
|
27
|
+
# Declares a task group. At least one `Task` subclass is required —
|
|
28
|
+
# invoking with no arguments raises `DefinitionError`. Use {.pipeline}
|
|
29
|
+
# to read the existing group list.
|
|
29
30
|
#
|
|
30
|
-
# @param tasks [Array<Class<Task>>]
|
|
31
|
+
# @param tasks [Array<Class<Task>>] one or more `Task` subclasses
|
|
31
32
|
# @param options [Hash{Symbol => Object}]
|
|
32
33
|
# @option options [:sequential, :parallel] :strategy (:sequential)
|
|
33
34
|
# @option options [Integer] :pool_size parallel worker/fiber count
|
|
@@ -55,18 +56,26 @@ module CMDx
|
|
|
55
56
|
# `:parallel` cancels pending tasks (in-flight tasks still finish).
|
|
56
57
|
# @option options [Symbol, Proc, #call] :if
|
|
57
58
|
# @option options [Symbol, Proc, #call] :unless
|
|
58
|
-
# @return [Array<ExecutionGroup>] the full pipeline
|
|
59
|
-
# @raise [DefinitionError] when called with
|
|
59
|
+
# @return [Array<ExecutionGroup>] the full pipeline (with the new group appended)
|
|
60
|
+
# @raise [DefinitionError] when called with no tasks
|
|
60
61
|
# @raise [TypeError] when any element isn't a `Task` subclass
|
|
61
62
|
def tasks(*tasks, **options)
|
|
62
|
-
|
|
63
|
+
if tasks.empty?
|
|
64
|
+
raise DefinitionError, <<~MSG.chomp
|
|
65
|
+
#{name}: cannot declare an empty task group; pass at least one Task subclass.
|
|
66
|
+
See https://drexed.github.io/cmdx/workflows/#declarations
|
|
67
|
+
MSG
|
|
68
|
+
end
|
|
63
69
|
|
|
64
70
|
pipeline << ExecutionGroup.new(
|
|
65
71
|
tasks:
|
|
66
72
|
tasks.map do |task|
|
|
67
73
|
next task if task.is_a?(Class) && (task <= Task)
|
|
68
74
|
|
|
69
|
-
raise TypeError,
|
|
75
|
+
raise TypeError, <<~MSG.chomp
|
|
76
|
+
#{task.inspect} is not a Task subclass.
|
|
77
|
+
See https://drexed.github.io/cmdx/workflows/#declarations
|
|
78
|
+
MSG
|
|
70
79
|
end,
|
|
71
80
|
options:
|
|
72
81
|
)
|
|
@@ -75,16 +84,13 @@ module CMDx
|
|
|
75
84
|
|
|
76
85
|
private
|
|
77
86
|
|
|
78
|
-
# Forbids user-defined `work` on workflows; `Workflow#work` delegates
|
|
79
|
-
# to {Pipeline}.
|
|
80
|
-
#
|
|
81
|
-
# @param method_name [Symbol] hook name reported by Ruby
|
|
82
|
-
# @return [void]
|
|
83
|
-
# @raise [ImplementationError] when a workflow defines `work`
|
|
84
87
|
def method_added(method_name)
|
|
85
88
|
return super unless method_name == :work
|
|
86
89
|
|
|
87
|
-
raise ImplementationError,
|
|
90
|
+
raise ImplementationError, <<~MSG.chomp
|
|
91
|
+
cannot define #{name}##{method_name} in a workflow; #work is auto-generated to delegate to Pipeline.
|
|
92
|
+
See https://drexed.github.io/cmdx/workflows/#declarations
|
|
93
|
+
MSG
|
|
88
94
|
end
|
|
89
95
|
|
|
90
96
|
end
|
|
@@ -95,7 +101,15 @@ module CMDx
|
|
|
95
101
|
# @api private
|
|
96
102
|
# @param base [Class] task class including this mixin
|
|
97
103
|
# @return [void]
|
|
104
|
+
# @raise [ImplementationError] when `base` is not a {Task} subclass
|
|
98
105
|
def self.included(base)
|
|
106
|
+
unless base.is_a?(Class) && base <= Task
|
|
107
|
+
raise ImplementationError, <<~MSG.chomp
|
|
108
|
+
CMDx::Workflow can only be included in a CMDx::Task subclass (got #{base.inspect}).
|
|
109
|
+
See https://drexed.github.io/cmdx/workflows/#declarations
|
|
110
|
+
MSG
|
|
111
|
+
end
|
|
112
|
+
|
|
99
113
|
base.extend(ClassMethods)
|
|
100
114
|
end
|
|
101
115
|
|
data/lib/cmdx.rb
CHANGED
|
@@ -24,6 +24,13 @@ module CMDx
|
|
|
24
24
|
EMPTY_HASH = {}.freeze
|
|
25
25
|
private_constant :EMPTY_HASH
|
|
26
26
|
|
|
27
|
+
# Sentinel object used as a placeholder return value to avoid per-call
|
|
28
|
+
# allocations on hot paths.
|
|
29
|
+
#
|
|
30
|
+
# @api private
|
|
31
|
+
EMPTY_SENTINEL = Object.new.freeze
|
|
32
|
+
private_constant :EMPTY_SENTINEL
|
|
33
|
+
|
|
27
34
|
# Shared empty string constant used as a sentinel default. Intentionally
|
|
28
35
|
# not frozen so callers may treat it as a mutable seed when needed.
|
|
29
36
|
#
|
|
@@ -51,6 +58,11 @@ module CMDx
|
|
|
51
58
|
# before continuing.
|
|
52
59
|
DeprecationError = Class.new(Error)
|
|
53
60
|
|
|
61
|
+
# Raised when a control-flow signal (e.g. `skip!`, `fail!`) is thrown against
|
|
62
|
+
# a task that has already completed and been frozen, making further state
|
|
63
|
+
# transitions impossible.
|
|
64
|
+
FrozenTaskError = Class.new(Error)
|
|
65
|
+
|
|
54
66
|
# Raised when a subclass fails to fulfill an abstract contract — most
|
|
55
67
|
# commonly when {Task} is invoked without overriding `#work`, or when a
|
|
56
68
|
# {Workflow} attempts to define `#work` itself.
|
|
@@ -60,6 +72,18 @@ module CMDx
|
|
|
60
72
|
# yield to `next_link`, which would otherwise silently skip the task body.
|
|
61
73
|
MiddlewareError = Class.new(Error)
|
|
62
74
|
|
|
75
|
+
# Raised by {Context} in strict mode when accessing a key that was never
|
|
76
|
+
# assigned, preventing silent `nil` propagation across task boundaries.
|
|
77
|
+
UnknownAccessorError = Class.new(Error)
|
|
78
|
+
|
|
79
|
+
# Raised when a registry lookup (coercion, validator, middleware, etc.) is
|
|
80
|
+
# performed against a name that has not been registered.
|
|
81
|
+
UnknownEntryError = Class.new(Error)
|
|
82
|
+
|
|
83
|
+
# Raised when the configured locale cannot be resolved to a translation
|
|
84
|
+
# file on the i18n load path.
|
|
85
|
+
UnknownLocaleError = Class.new(Error)
|
|
86
|
+
|
|
63
87
|
end
|
|
64
88
|
|
|
65
89
|
require_relative "cmdx/version"
|