cmdx 1.18.0 → 1.20.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/.DS_Store +0 -0
- data/CHANGELOG.md +73 -9
- data/README.md +1 -1
- data/lib/cmdx/attribute.rb +88 -20
- data/lib/cmdx/attribute_registry.rb +79 -8
- data/lib/cmdx/attribute_value.rb +8 -3
- data/lib/cmdx/callback_registry.rb +60 -26
- data/lib/cmdx/chain.rb +47 -4
- data/lib/cmdx/coercion_registry.rb +42 -20
- data/lib/cmdx/coercions/array.rb +8 -3
- data/lib/cmdx/coercions/big_decimal.rb +1 -1
- data/lib/cmdx/coercions/boolean.rb +6 -2
- data/lib/cmdx/coercions/complex.rb +1 -1
- data/lib/cmdx/coercions/date.rb +2 -7
- data/lib/cmdx/coercions/date_time.rb +2 -7
- data/lib/cmdx/coercions/float.rb +1 -1
- data/lib/cmdx/coercions/hash.rb +1 -1
- data/lib/cmdx/coercions/integer.rb +4 -5
- data/lib/cmdx/coercions/rational.rb +1 -1
- data/lib/cmdx/coercions/string.rb +1 -1
- data/lib/cmdx/coercions/symbol.rb +1 -1
- data/lib/cmdx/coercions/time.rb +1 -7
- data/lib/cmdx/configuration.rb +26 -0
- data/lib/cmdx/context.rb +9 -6
- data/lib/cmdx/deprecator.rb +27 -14
- data/lib/cmdx/errors.rb +3 -4
- data/lib/cmdx/exception.rb +7 -0
- data/lib/cmdx/executor.rb +77 -54
- data/lib/cmdx/identifier.rb +4 -6
- data/lib/cmdx/locale.rb +32 -9
- data/lib/cmdx/middleware_registry.rb +43 -23
- data/lib/cmdx/middlewares/correlate.rb +4 -2
- data/lib/cmdx/middlewares/timeout.rb +11 -10
- data/lib/cmdx/parallelizer.rb +100 -0
- data/lib/cmdx/pipeline.rb +42 -23
- data/lib/cmdx/railtie.rb +1 -1
- data/lib/cmdx/result.rb +27 -11
- data/lib/cmdx/retry.rb +166 -0
- data/lib/cmdx/settings.rb +222 -0
- data/lib/cmdx/task.rb +53 -61
- data/lib/cmdx/utils/format.rb +17 -1
- data/lib/cmdx/utils/normalize.rb +52 -0
- data/lib/cmdx/utils/wrap.rb +38 -0
- data/lib/cmdx/validator_registry.rb +45 -20
- data/lib/cmdx/validators/absence.rb +1 -1
- data/lib/cmdx/validators/exclusion.rb +2 -2
- data/lib/cmdx/validators/format.rb +1 -1
- data/lib/cmdx/validators/inclusion.rb +2 -2
- data/lib/cmdx/validators/length.rb +1 -1
- data/lib/cmdx/validators/numeric.rb +1 -1
- data/lib/cmdx/validators/presence.rb +1 -1
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx.rb +12 -0
- data/lib/generators/cmdx/templates/install.rb +11 -0
- data/mkdocs.yml +5 -1
- metadata +6 -15
data/lib/cmdx/task.rb
CHANGED
|
@@ -73,67 +73,60 @@ module CMDx
|
|
|
73
73
|
def_delegators :result, :skip!, :fail!, :throw!
|
|
74
74
|
def_delegators :chain, :dry_run?
|
|
75
75
|
|
|
76
|
-
# @param context [Hash, Context] The initial context for the task
|
|
77
|
-
#
|
|
78
|
-
# @option context [Object] :* Any key-value pairs to initialize the context
|
|
76
|
+
# @param context [Hash, Context, nil] The initial context for the task
|
|
79
77
|
#
|
|
80
78
|
# @return [Task] A new task instance
|
|
81
79
|
#
|
|
82
80
|
# @raise [DeprecationError] If the task class is deprecated
|
|
83
81
|
#
|
|
84
82
|
# @example
|
|
83
|
+
# task = MyTask.new
|
|
85
84
|
# task = MyTask.new(name: "example", priority: :high)
|
|
86
85
|
# task = MyTask.new(Context.build(name: "example"))
|
|
87
86
|
#
|
|
88
87
|
# @rbs (untyped context) -> void
|
|
89
|
-
def initialize(context =
|
|
88
|
+
def initialize(context = nil)
|
|
90
89
|
Deprecator.restrict(self)
|
|
91
90
|
|
|
92
|
-
@attributes = {}
|
|
93
|
-
@errors = Errors.new
|
|
94
|
-
|
|
95
91
|
@id = Identifier.generate
|
|
96
92
|
@context = Context.build(context)
|
|
93
|
+
@errors = Errors.new
|
|
97
94
|
@result = Result.new(self)
|
|
98
95
|
@chain = Chain.build(@result, dry_run: @context.delete(:dry_run))
|
|
96
|
+
|
|
97
|
+
@attributes = {}
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
class << self
|
|
102
101
|
|
|
103
|
-
#
|
|
104
|
-
# @option options [Object] :* Any configuration option key-value pairs
|
|
102
|
+
# Returns the cached task type string for this class.
|
|
105
103
|
#
|
|
106
|
-
# @return [
|
|
104
|
+
# @return [String] "Workflow" or "Task"
|
|
105
|
+
#
|
|
106
|
+
# @rbs () -> String
|
|
107
|
+
def type
|
|
108
|
+
@type ||= include?(Workflow) ? "Workflow" : "Task"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns (and lazily creates) the task-level Settings object.
|
|
112
|
+
# On first access, inherits from the superclass settings or
|
|
113
|
+
# the global Configuration. Optional overrides are applied once.
|
|
114
|
+
#
|
|
115
|
+
# @param overrides [Hash] Configuration overrides applied on first access
|
|
116
|
+
# @option overrides [Object] :* Any configuration override key-value pairs
|
|
117
|
+
#
|
|
118
|
+
# @return [Settings] The settings instance for this task class
|
|
107
119
|
#
|
|
108
120
|
# @example
|
|
109
121
|
# class MyTask < Task
|
|
110
122
|
# settings deprecate: true, tags: [:experimental]
|
|
111
123
|
# end
|
|
112
124
|
#
|
|
113
|
-
# @rbs (**untyped
|
|
114
|
-
def settings(**
|
|
125
|
+
# @rbs (**untyped overrides) -> Settings
|
|
126
|
+
def settings(**overrides)
|
|
115
127
|
@settings ||= begin
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
parent = superclass.settings
|
|
119
|
-
parent
|
|
120
|
-
.except(:backtrace_cleaner, :exception_handler, :logger, :deprecate)
|
|
121
|
-
.transform_values!(&:dup)
|
|
122
|
-
.merge!(
|
|
123
|
-
backtrace_cleaner: parent[:backtrace_cleaner] || CMDx.configuration.backtrace_cleaner,
|
|
124
|
-
exception_handler: parent[:exception_handler] || CMDx.configuration.exception_handler,
|
|
125
|
-
logger: parent[:logger] || CMDx.configuration.logger,
|
|
126
|
-
deprecate: parent[:deprecate]
|
|
127
|
-
)
|
|
128
|
-
else
|
|
129
|
-
CMDx.configuration.to_h
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
hash[:attributes] ||= AttributeRegistry.new
|
|
133
|
-
hash[:returns] ||= []
|
|
134
|
-
hash[:tags] ||= []
|
|
135
|
-
|
|
136
|
-
hash.merge!(options)
|
|
128
|
+
parent = superclass.settings if superclass.respond_to?(:settings)
|
|
129
|
+
Settings.new(parent:, **overrides)
|
|
137
130
|
end
|
|
138
131
|
end
|
|
139
132
|
|
|
@@ -149,11 +142,13 @@ module CMDx
|
|
|
149
142
|
# @rbs (Symbol type, untyped object, *untyped) -> void
|
|
150
143
|
def register(type, object, ...)
|
|
151
144
|
case type
|
|
152
|
-
when :attribute
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
when :
|
|
156
|
-
when :
|
|
145
|
+
when :attribute
|
|
146
|
+
settings.attributes.register(object)
|
|
147
|
+
settings.attributes.define_readers_on!(self, Utils::Wrap.array(object))
|
|
148
|
+
when :callback then settings.callbacks.register(object, ...)
|
|
149
|
+
when :middleware then settings.middlewares.register(object, ...)
|
|
150
|
+
when :validator then settings.validators.register(object, ...)
|
|
151
|
+
when :coercion then settings.coercions.register(object, ...)
|
|
157
152
|
else raise "unknown registry type #{type.inspect}"
|
|
158
153
|
end
|
|
159
154
|
end
|
|
@@ -170,11 +165,13 @@ module CMDx
|
|
|
170
165
|
# @rbs (Symbol type, untyped object, *untyped) -> void
|
|
171
166
|
def deregister(type, object, ...)
|
|
172
167
|
case type
|
|
173
|
-
when :attribute
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
when :
|
|
177
|
-
when :
|
|
168
|
+
when :attribute
|
|
169
|
+
settings.attributes.undefine_readers_on!(self, object)
|
|
170
|
+
settings.attributes.deregister(object)
|
|
171
|
+
when :callback then settings.callbacks.deregister(object, ...)
|
|
172
|
+
when :middleware then settings.middlewares.deregister(object, ...)
|
|
173
|
+
when :validator then settings.validators.deregister(object, ...)
|
|
174
|
+
when :coercion then settings.coercions.deregister(object, ...)
|
|
178
175
|
else raise "unknown registry type #{type.inspect}"
|
|
179
176
|
end
|
|
180
177
|
end
|
|
@@ -229,7 +226,7 @@ module CMDx
|
|
|
229
226
|
#
|
|
230
227
|
# @rbs (*untyped names) -> void
|
|
231
228
|
def returns(*names)
|
|
232
|
-
settings
|
|
229
|
+
settings.returns |= names.map(&:to_sym)
|
|
233
230
|
end
|
|
234
231
|
|
|
235
232
|
# Removes declared returns from the task.
|
|
@@ -241,7 +238,7 @@ module CMDx
|
|
|
241
238
|
#
|
|
242
239
|
# @rbs (*Symbol names) -> void
|
|
243
240
|
def remove_returns(*names)
|
|
244
|
-
settings
|
|
241
|
+
settings.returns -= names.map(&:to_sym)
|
|
245
242
|
end
|
|
246
243
|
alias remove_return remove_returns
|
|
247
244
|
|
|
@@ -259,7 +256,7 @@ module CMDx
|
|
|
259
256
|
#
|
|
260
257
|
# @rbs () -> Hash[Symbol, Hash[Symbol, untyped]]
|
|
261
258
|
def attributes_schema
|
|
262
|
-
|
|
259
|
+
Utils::Wrap.array(settings.attributes).to_h do |attr|
|
|
263
260
|
[attr.method_name, attr.to_h]
|
|
264
261
|
end
|
|
265
262
|
end
|
|
@@ -290,11 +287,8 @@ module CMDx
|
|
|
290
287
|
#
|
|
291
288
|
# @example
|
|
292
289
|
# result = MyTask.execute(name: "example")
|
|
293
|
-
# if result.success?
|
|
294
|
-
# puts "Task completed successfully"
|
|
295
|
-
# end
|
|
296
290
|
#
|
|
297
|
-
# @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
|
|
291
|
+
# @rbs (*untyped args, dry_run: bool, **untyped kwargs) ?{ (Result) -> void } -> Result
|
|
298
292
|
def execute(*args, **kwargs)
|
|
299
293
|
task = new(*args, **kwargs)
|
|
300
294
|
task.execute(raise: false)
|
|
@@ -311,9 +305,8 @@ module CMDx
|
|
|
311
305
|
#
|
|
312
306
|
# @example
|
|
313
307
|
# result = MyTask.execute!(name: "example")
|
|
314
|
-
# # Will raise an exception if execution fails
|
|
315
308
|
#
|
|
316
|
-
# @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
|
|
309
|
+
# @rbs (*untyped args, dry_run: bool, **untyped kwargs) ?{ (Result) -> void } -> Result
|
|
317
310
|
def execute!(*args, **kwargs)
|
|
318
311
|
task = new(*args, **kwargs)
|
|
319
312
|
task.execute(raise: true)
|
|
@@ -364,14 +357,13 @@ module CMDx
|
|
|
364
357
|
# @rbs () -> Logger
|
|
365
358
|
def logger
|
|
366
359
|
@logger ||= begin
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
log_formatter = self.class.settings[:log_formatter]
|
|
360
|
+
settings = self.class.settings
|
|
361
|
+
log_instance = settings.logger || CMDx.configuration.logger
|
|
370
362
|
|
|
371
|
-
if log_level || log_formatter
|
|
363
|
+
if settings.log_level || settings.log_formatter
|
|
372
364
|
log_instance = log_instance.dup
|
|
373
|
-
log_instance.level = log_level if log_level
|
|
374
|
-
log_instance.formatter = log_formatter if log_formatter
|
|
365
|
+
log_instance.level = settings.log_level if settings.log_level
|
|
366
|
+
log_instance.formatter = settings.log_formatter if settings.log_formatter
|
|
375
367
|
end
|
|
376
368
|
|
|
377
369
|
log_instance
|
|
@@ -397,11 +389,11 @@ module CMDx
|
|
|
397
389
|
{
|
|
398
390
|
index: result.index,
|
|
399
391
|
chain_id: chain.id,
|
|
400
|
-
type: self.class.
|
|
401
|
-
tags: self.class.settings[:tags],
|
|
392
|
+
type: self.class.type,
|
|
402
393
|
class: self.class.name,
|
|
394
|
+
id:,
|
|
403
395
|
dry_run: dry_run?,
|
|
404
|
-
|
|
396
|
+
tags: self.class.settings.tags
|
|
405
397
|
}
|
|
406
398
|
end
|
|
407
399
|
|
data/lib/cmdx/utils/format.rb
CHANGED
|
@@ -32,7 +32,7 @@ module CMDx
|
|
|
32
32
|
#
|
|
33
33
|
# @rbs (untyped message) -> untyped
|
|
34
34
|
def to_log(message)
|
|
35
|
-
if message.respond_to?(:to_h) && message.class
|
|
35
|
+
if message.respond_to?(:to_h) && cmdx_based_object?(message.class)
|
|
36
36
|
message.to_h
|
|
37
37
|
else
|
|
38
38
|
message
|
|
@@ -61,6 +61,22 @@ module CMDx
|
|
|
61
61
|
hash.map(&block).join(" ")
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Checks if a class belongs to the CMDx namespace, caching per class.
|
|
67
|
+
#
|
|
68
|
+
# @param klass [Class] The class to check
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true if the class is in the CMDx namespace
|
|
71
|
+
#
|
|
72
|
+
# @rbs (Class klass) -> bool
|
|
73
|
+
def cmdx_based_object?(klass)
|
|
74
|
+
@cmdx_classes ||= {}
|
|
75
|
+
return @cmdx_classes[klass] if @cmdx_classes.key?(klass)
|
|
76
|
+
|
|
77
|
+
@cmdx_classes[klass] = klass.ancestors.any? { |a| a.name&.start_with?("CMDx::") }
|
|
78
|
+
end
|
|
79
|
+
|
|
64
80
|
end
|
|
65
81
|
end
|
|
66
82
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CMDx
|
|
4
|
+
module Utils
|
|
5
|
+
# Provides normalization utilities for a variety of objects
|
|
6
|
+
# into consistent formats.
|
|
7
|
+
module Normalize
|
|
8
|
+
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
# Normalizes an exception into a string representation.
|
|
12
|
+
#
|
|
13
|
+
# @param exception [Exception] The exception to normalize
|
|
14
|
+
#
|
|
15
|
+
# @return [String] The normalized exception string
|
|
16
|
+
#
|
|
17
|
+
# @example From exception
|
|
18
|
+
# Normalize.exception(StandardError.new("test"))
|
|
19
|
+
# # => "[StandardError] test"
|
|
20
|
+
#
|
|
21
|
+
# @rbs (Exception exception) -> String
|
|
22
|
+
def exception(exception)
|
|
23
|
+
"[#{exception.class}] #{exception.message}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Normalizes an object into an array of unique status strings.
|
|
27
|
+
#
|
|
28
|
+
# @param object [Object] The object to normalize into status strings
|
|
29
|
+
#
|
|
30
|
+
# @return [Array<String>] Unique status strings
|
|
31
|
+
#
|
|
32
|
+
# @example From array of symbols
|
|
33
|
+
# Normalize.statuses([:success, :pending, :success])
|
|
34
|
+
# # => ["success", "pending"]
|
|
35
|
+
# @example From single value
|
|
36
|
+
# Normalize.statuses(:success)
|
|
37
|
+
# # => ["success"]
|
|
38
|
+
# @example From nil
|
|
39
|
+
# Normalize.statuses(nil)
|
|
40
|
+
# # => []
|
|
41
|
+
#
|
|
42
|
+
# @rbs (untyped object) -> Array[String]
|
|
43
|
+
def statuses(object)
|
|
44
|
+
ary = Wrap.array(object)
|
|
45
|
+
return EMPTY_ARRAY if ary.empty?
|
|
46
|
+
|
|
47
|
+
ary.map(&:to_s).uniq
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CMDx
|
|
4
|
+
module Utils
|
|
5
|
+
# Provides array wrapping utilities for normalizing input values
|
|
6
|
+
# into consistent array structures.
|
|
7
|
+
module Wrap
|
|
8
|
+
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
# Wraps an object in an array if it is not already an array.
|
|
12
|
+
#
|
|
13
|
+
# @param object [Object] The object to wrap in an array
|
|
14
|
+
#
|
|
15
|
+
# @return [Array] The wrapped array
|
|
16
|
+
#
|
|
17
|
+
# @example Already an array
|
|
18
|
+
# Wrap.array([1, 2, 3])
|
|
19
|
+
# # => [1, 2, 3]
|
|
20
|
+
# @example Single value
|
|
21
|
+
# Wrap.array(1)
|
|
22
|
+
# # => [1]
|
|
23
|
+
# @example Nil value
|
|
24
|
+
# Wrap.array(nil)
|
|
25
|
+
# # => []
|
|
26
|
+
#
|
|
27
|
+
# @rbs (untyped object) -> Array[untyped]
|
|
28
|
+
def array(object)
|
|
29
|
+
case object
|
|
30
|
+
when Array then object
|
|
31
|
+
when NilClass then EMPTY_ARRAY
|
|
32
|
+
else Array(object)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
module CMDx
|
|
4
4
|
# Registry for managing validation rules and their corresponding validator classes.
|
|
5
5
|
# Provides methods to register, deregister, and execute validators against task values.
|
|
6
|
+
#
|
|
7
|
+
# Supports copy-on-write semantics: a duped registry shares the parent's
|
|
8
|
+
# data until a write operation triggers materialization.
|
|
6
9
|
class ValidatorRegistry
|
|
7
10
|
|
|
8
11
|
extend Forwardable
|
|
9
12
|
|
|
10
|
-
# Returns the internal registry mapping validator types to classes.
|
|
11
|
-
#
|
|
12
|
-
# @return [Hash{Symbol => Class}] Hash of validator type names to validator classes
|
|
13
|
-
#
|
|
14
|
-
# @example
|
|
15
|
-
# registry.registry # => { presence: Validators::Presence, format: Validators::Format }
|
|
16
|
-
#
|
|
17
|
-
# @rbs @registry: Hash[Symbol, Class]
|
|
18
|
-
attr_reader :registry
|
|
19
|
-
alias to_h registry
|
|
20
|
-
|
|
21
13
|
def_delegators :registry, :keys
|
|
22
14
|
|
|
23
15
|
# Initialize a new validator registry with default validators.
|
|
@@ -39,14 +31,30 @@ module CMDx
|
|
|
39
31
|
}
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
#
|
|
34
|
+
# Sets up copy-on-write state when duplicated via dup.
|
|
35
|
+
#
|
|
36
|
+
# @param source [ValidatorRegistry] The registry being duplicated
|
|
37
|
+
#
|
|
38
|
+
# @rbs (ValidatorRegistry source) -> void
|
|
39
|
+
def initialize_dup(source)
|
|
40
|
+
@parent = source
|
|
41
|
+
@registry = nil
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the internal registry mapping validator types to classes.
|
|
46
|
+
# Delegates to the parent registry when not yet materialized.
|
|
43
47
|
#
|
|
44
|
-
# @return [
|
|
48
|
+
# @return [Hash{Symbol => Class}] Hash of validator type names to validator classes
|
|
45
49
|
#
|
|
46
|
-
# @
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
# @example
|
|
51
|
+
# registry.registry # => { presence: Validators::Presence, format: Validators::Format }
|
|
52
|
+
#
|
|
53
|
+
# @rbs () -> Hash[Symbol, Class]
|
|
54
|
+
def registry
|
|
55
|
+
@registry || @parent.registry
|
|
49
56
|
end
|
|
57
|
+
alias to_h registry
|
|
50
58
|
|
|
51
59
|
# Register a new validator class with the given name.
|
|
52
60
|
#
|
|
@@ -61,7 +69,9 @@ module CMDx
|
|
|
61
69
|
#
|
|
62
70
|
# @rbs ((String | Symbol) name, Class validator) -> self
|
|
63
71
|
def register(name, validator)
|
|
64
|
-
|
|
72
|
+
materialize!
|
|
73
|
+
|
|
74
|
+
@registry[name.to_sym] = validator
|
|
65
75
|
self
|
|
66
76
|
end
|
|
67
77
|
|
|
@@ -77,7 +87,9 @@ module CMDx
|
|
|
77
87
|
#
|
|
78
88
|
# @rbs ((String | Symbol) name) -> self
|
|
79
89
|
def deregister(name)
|
|
80
|
-
|
|
90
|
+
materialize!
|
|
91
|
+
|
|
92
|
+
@registry.delete(name.to_sym)
|
|
81
93
|
self
|
|
82
94
|
end
|
|
83
95
|
|
|
@@ -96,13 +108,13 @@ module CMDx
|
|
|
96
108
|
# registry.validate(:length, task, password, { min: 8, allow_nil: false })
|
|
97
109
|
#
|
|
98
110
|
# @rbs (Symbol type, Task task, untyped value, untyped options) -> untyped
|
|
99
|
-
def validate(type, task, value, options =
|
|
111
|
+
def validate(type, task, value, options = EMPTY_HASH)
|
|
100
112
|
raise TypeError, "unknown validator type #{type.inspect}" unless registry.key?(type)
|
|
101
113
|
|
|
102
114
|
match =
|
|
103
115
|
if options.is_a?(Hash)
|
|
104
116
|
case options
|
|
105
|
-
in allow_nil: then allow_nil && value.nil?
|
|
117
|
+
in allow_nil: then !(allow_nil && value.nil?)
|
|
106
118
|
else Utils::Condition.evaluate(task, options, value)
|
|
107
119
|
end
|
|
108
120
|
else
|
|
@@ -114,5 +126,18 @@ module CMDx
|
|
|
114
126
|
Utils::Call.invoke(task, registry[type], value, options)
|
|
115
127
|
end
|
|
116
128
|
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# Copies the parent's registry data into this instance,
|
|
132
|
+
# severing the copy-on-write link.
|
|
133
|
+
#
|
|
134
|
+
# @rbs () -> void
|
|
135
|
+
def materialize!
|
|
136
|
+
return if @registry
|
|
137
|
+
|
|
138
|
+
@registry = @parent.registry.dup
|
|
139
|
+
@parent = nil
|
|
140
|
+
end
|
|
141
|
+
|
|
117
142
|
end
|
|
118
143
|
end
|
|
@@ -40,7 +40,7 @@ module CMDx
|
|
|
40
40
|
# # => raises ValidationError with custom message
|
|
41
41
|
#
|
|
42
42
|
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> nil
|
|
43
|
-
def call(value, options =
|
|
43
|
+
def call(value, options = EMPTY_HASH)
|
|
44
44
|
match =
|
|
45
45
|
if value.is_a?(String)
|
|
46
46
|
/\S/.match?(value)
|
|
@@ -34,12 +34,12 @@ module CMDx
|
|
|
34
34
|
# Exclusion.call("test", in: ["test", "demo"], message: "value %{values} is forbidden")
|
|
35
35
|
#
|
|
36
36
|
# @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
|
|
37
|
-
def call(value, options =
|
|
37
|
+
def call(value, options = EMPTY_HASH)
|
|
38
38
|
values = options[:in] || options[:within]
|
|
39
39
|
|
|
40
40
|
if values.is_a?(Range)
|
|
41
41
|
raise_within_validation_error!(values.begin, values.end, options) if values.cover?(value)
|
|
42
|
-
elsif
|
|
42
|
+
elsif Utils::Wrap.array(values).any? { |v| v === value }
|
|
43
43
|
raise_of_validation_error!(values, options)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -40,7 +40,7 @@ module CMDx
|
|
|
40
40
|
# # => raises ValidationError with custom message
|
|
41
41
|
#
|
|
42
42
|
# @rbs (untyped value, (Hash[Symbol, untyped] | Regexp) options) -> nil
|
|
43
|
-
def call(value, options =
|
|
43
|
+
def call(value, options = EMPTY_HASH)
|
|
44
44
|
match =
|
|
45
45
|
if options.is_a?(Regexp)
|
|
46
46
|
value&.match?(options)
|
|
@@ -36,12 +36,12 @@ module CMDx
|
|
|
36
36
|
# Inclusion.call("test", in: ["admin", "user"], message: "must be one of: %{values}")
|
|
37
37
|
#
|
|
38
38
|
# @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
|
|
39
|
-
def call(value, options =
|
|
39
|
+
def call(value, options = EMPTY_HASH)
|
|
40
40
|
values = options[:in] || options[:within]
|
|
41
41
|
|
|
42
42
|
if values.is_a?(Range)
|
|
43
43
|
raise_within_validation_error!(values.begin, values.end, options) unless values.cover?(value)
|
|
44
|
-
elsif
|
|
44
|
+
elsif Utils::Wrap.array(values).none? { |v| v === value }
|
|
45
45
|
raise_of_validation_error!(values, options)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -53,7 +53,7 @@ module CMDx
|
|
|
53
53
|
# # => nil (validation passes - length 5 is not in excluded range)
|
|
54
54
|
#
|
|
55
55
|
# @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
|
|
56
|
-
def call(value, options =
|
|
56
|
+
def call(value, options = EMPTY_HASH)
|
|
57
57
|
length = value&.length
|
|
58
58
|
|
|
59
59
|
case options
|
|
@@ -50,7 +50,7 @@ module CMDx
|
|
|
50
50
|
# # => nil (validation passes - 5 is not in 1..10)
|
|
51
51
|
#
|
|
52
52
|
# @rbs (Numeric value, Hash[Symbol, untyped] options) -> nil
|
|
53
|
-
def call(value, options =
|
|
53
|
+
def call(value, options = EMPTY_HASH)
|
|
54
54
|
case options
|
|
55
55
|
in within:
|
|
56
56
|
raise_within_validation_error!(within.begin, within.end, options) unless within&.cover?(value)
|
|
@@ -40,7 +40,7 @@ module CMDx
|
|
|
40
40
|
# # => raises ValidationError with custom message
|
|
41
41
|
#
|
|
42
42
|
# @rbs (untyped value, ?Hash[Symbol, untyped] options) -> nil
|
|
43
|
-
def call(value, options =
|
|
43
|
+
def call(value, options = EMPTY_HASH)
|
|
44
44
|
match =
|
|
45
45
|
if value.is_a?(String)
|
|
46
46
|
/\S/.match?(value)
|
data/lib/cmdx/version.rb
CHANGED
data/lib/cmdx.rb
CHANGED
|
@@ -15,6 +15,18 @@ require "zeitwerk"
|
|
|
15
15
|
|
|
16
16
|
module CMDx
|
|
17
17
|
|
|
18
|
+
# @rbs EMPTY_ARRAY: Array[untyped]
|
|
19
|
+
EMPTY_ARRAY = [].freeze
|
|
20
|
+
private_constant :EMPTY_ARRAY
|
|
21
|
+
|
|
22
|
+
# @rbs EMPTY_HASH: Hash[untyped, untyped]
|
|
23
|
+
EMPTY_HASH = {}.freeze
|
|
24
|
+
private_constant :EMPTY_HASH
|
|
25
|
+
|
|
26
|
+
# @rbs EMPTY_STRING: String
|
|
27
|
+
EMPTY_STRING = ""
|
|
28
|
+
private_constant :EMPTY_STRING
|
|
29
|
+
|
|
18
30
|
extend self
|
|
19
31
|
|
|
20
32
|
# Returns the path to the CMDx gem.
|
|
@@ -32,6 +32,17 @@ CMDx.configure do |config|
|
|
|
32
32
|
level: Logger::INFO
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
+
# Rollback configuration - controls which statuses trigger task rollback
|
|
36
|
+
# See https://github.com/drexed/cmdx/blob/main/docs/outcomes/statuses.md for more details
|
|
37
|
+
#
|
|
38
|
+
# Available statuses: "success", "skipped", "failed"
|
|
39
|
+
# If set to an empty array, task will never rollback
|
|
40
|
+
config.rollback_on = %w[failed]
|
|
41
|
+
|
|
42
|
+
# Default locale configuration - used for built-in translation lookups
|
|
43
|
+
# Must match the basename of a YAML file in lib/locales/ (e.g. "en", "es", "ja")
|
|
44
|
+
# config.default_locale = "en"
|
|
45
|
+
|
|
35
46
|
# Backtrace configuration - controls whether to log backtraces on faults and exceptions
|
|
36
47
|
# https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#backtraces
|
|
37
48
|
# config.backtrace = false
|
data/mkdocs.yml
CHANGED
|
@@ -144,6 +144,8 @@ plugins:
|
|
|
144
144
|
- returns.md: Declaring expected context outputs that must be set after task execution
|
|
145
145
|
- workflows.md: Composing multiple tasks into sequential pipelines with conditional execution
|
|
146
146
|
More:
|
|
147
|
+
- testing.md: Best practices for testing CMDx tasks and workflows with RSpec
|
|
148
|
+
- exceptions.md: Complete exception hierarchy and error type reference
|
|
147
149
|
- tips_and_tricks.md: Best practices, patterns, and techniques for maintainable CMDx applications
|
|
148
150
|
- comparison.md: Comparison with other command/service object frameworks
|
|
149
151
|
nav:
|
|
@@ -181,13 +183,15 @@ nav:
|
|
|
181
183
|
- Returns: returns.md
|
|
182
184
|
- Workflows: workflows.md
|
|
183
185
|
- More:
|
|
186
|
+
- Testing: testing.md
|
|
187
|
+
- Exceptions: exceptions.md
|
|
184
188
|
- Tips and Tricks: tips_and_tricks.md
|
|
185
189
|
- Comparison: comparison.md
|
|
186
190
|
- References:
|
|
187
191
|
- API Documentation: https://drexed.github.io/cmdx/api/index.html
|
|
188
192
|
- llms.txt: https://drexed.github.io/cmdx/llms.txt
|
|
189
193
|
- llms-full.txt: https://drexed.github.io/cmdx/llms-full.txt
|
|
190
|
-
-
|
|
194
|
+
- AI Skills: https://github.com/drexed/cmdx/blob/main/skills
|
|
191
195
|
- Ecosystem:
|
|
192
196
|
- cmdx-rspec: https://github.com/drexed/cmdx-rspec
|
|
193
197
|
- Blog: blog/index.md
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cmdx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Juan Gomez
|
|
@@ -93,20 +93,6 @@ dependencies:
|
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: parallel
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - ">="
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0'
|
|
103
|
-
type: :development
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - ">="
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0'
|
|
110
96
|
- !ruby/object:Gem::Dependency
|
|
111
97
|
name: rake
|
|
112
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -285,13 +271,18 @@ files:
|
|
|
285
271
|
- lib/cmdx/middlewares/correlate.rb
|
|
286
272
|
- lib/cmdx/middlewares/runtime.rb
|
|
287
273
|
- lib/cmdx/middlewares/timeout.rb
|
|
274
|
+
- lib/cmdx/parallelizer.rb
|
|
288
275
|
- lib/cmdx/pipeline.rb
|
|
289
276
|
- lib/cmdx/railtie.rb
|
|
290
277
|
- lib/cmdx/result.rb
|
|
278
|
+
- lib/cmdx/retry.rb
|
|
279
|
+
- lib/cmdx/settings.rb
|
|
291
280
|
- lib/cmdx/task.rb
|
|
292
281
|
- lib/cmdx/utils/call.rb
|
|
293
282
|
- lib/cmdx/utils/condition.rb
|
|
294
283
|
- lib/cmdx/utils/format.rb
|
|
284
|
+
- lib/cmdx/utils/normalize.rb
|
|
285
|
+
- lib/cmdx/utils/wrap.rb
|
|
295
286
|
- lib/cmdx/validator_registry.rb
|
|
296
287
|
- lib/cmdx/validators/absence.rb
|
|
297
288
|
- lib/cmdx/validators/exclusion.rb
|