cmdx 1.1.2 → 1.5.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/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
data/lib/cmdx/task.rb
CHANGED
@@ -1,438 +1,266 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# approach to implementing business logic with built-in parameter validation, middleware
|
8
|
-
# support, callback handling, and comprehensive result tracking. Tasks encapsulate
|
9
|
-
# discrete units of work that can be chained together into workflows or executed
|
10
|
-
# independently with rich execution context and error handling.
|
4
|
+
# Represents a task that can be executed within the CMDx framework.
|
5
|
+
# Tasks define attributes, callbacks, and execution logic that can be
|
6
|
+
# chained together to form workflows.
|
11
7
|
class Task
|
12
8
|
|
13
|
-
|
14
|
-
default: -> { CMDx.configuration.to_h.slice(:logger, :task_halt, :workflow_halt).merge(tags: []) }
|
15
|
-
cmdx_attr_setting :cmd_middlewares,
|
16
|
-
default: -> { MiddlewareRegistry.new(CMDx.configuration.middlewares) }
|
17
|
-
cmdx_attr_setting :cmd_callbacks,
|
18
|
-
default: -> { CallbackRegistry.new(CMDx.configuration.callbacks) }
|
19
|
-
cmdx_attr_setting :cmd_parameters,
|
20
|
-
default: -> { ParameterRegistry.new }
|
9
|
+
extend Forwardable
|
21
10
|
|
22
|
-
|
23
|
-
:cmd_settings, :cmd_setting, :cmd_setting?,
|
24
|
-
to: :class
|
25
|
-
cmdx_attr_delegator :skip!, :fail!, :throw!,
|
26
|
-
to: :result
|
27
|
-
|
28
|
-
# @return [Context] parameter context for this task execution
|
29
|
-
attr_reader :context
|
30
|
-
|
31
|
-
# @return [Errors] collection of validation and execution errors
|
32
|
-
attr_reader :errors
|
33
|
-
|
34
|
-
# @return [String] unique identifier for this task instance
|
35
|
-
attr_reader :id
|
36
|
-
|
37
|
-
# @return [Result] execution result tracking state and status
|
38
|
-
attr_reader :result
|
39
|
-
|
40
|
-
# @return [Chain] execution chain containing this task and related executions
|
41
|
-
attr_reader :chain
|
42
|
-
|
43
|
-
# @return [Context] alias for context
|
11
|
+
attr_reader :attributes, :errors, :id, :context, :result, :chain
|
44
12
|
alias ctx context
|
45
|
-
|
46
|
-
# @return [Result] alias for result
|
47
13
|
alias res result
|
48
14
|
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
# result tracking, and execution chain. The context parameter supports various
|
53
|
-
# input formats and will be normalized into a Context instance.
|
54
|
-
#
|
55
|
-
# @param context [Hash, Context, Object] initial execution context and parameters
|
15
|
+
def_delegators :result, :skip!, :fail!, :throw!
|
16
|
+
|
17
|
+
# @param context [Hash, Context] The initial context for the task
|
56
18
|
#
|
57
|
-
# @
|
19
|
+
# @option context [Object] :* Any key-value pairs to initialize the context
|
58
20
|
#
|
59
|
-
# @
|
60
|
-
# task = MyTask.new(user_id: 123, action: "process")
|
61
|
-
# task.context.user_id #=> 123
|
21
|
+
# @return [Task] A new task instance
|
62
22
|
#
|
63
|
-
# @
|
64
|
-
# existing_context = OtherTask.call(status: "active")
|
65
|
-
# task = MyTask.new(existing_context)
|
66
|
-
# task.context.status #=> "active"
|
23
|
+
# @raise [DeprecationError] If the task class is deprecated
|
67
24
|
#
|
68
|
-
# @example
|
69
|
-
# task = MyTask.new
|
70
|
-
# task
|
25
|
+
# @example
|
26
|
+
# task = MyTask.new(name: "example", priority: :high)
|
27
|
+
# task = MyTask.new(Context.build(name: "example"))
|
71
28
|
def initialize(context = {})
|
72
|
-
|
29
|
+
Deprecator.restrict(self)
|
73
30
|
|
74
|
-
@
|
75
|
-
@errors
|
76
|
-
@id = CMDx::Correlator.generate
|
77
|
-
@result = Result.new(self)
|
78
|
-
@chain = Chain.build(@result)
|
31
|
+
@attributes = {}
|
32
|
+
@errors = Errors.new
|
79
33
|
|
80
|
-
|
34
|
+
@id = Identifier.generate
|
35
|
+
@context = Context.build(context)
|
36
|
+
@result = Result.new(self)
|
37
|
+
@chain = Chain.build(@result)
|
81
38
|
end
|
82
39
|
|
83
40
|
class << self
|
84
41
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
# CallbackRegistry, allowing tasks to register callbacks for various
|
90
|
-
# execution lifecycle events.
|
91
|
-
#
|
92
|
-
# @param callables [Array<Object>] callback objects or procs to register
|
93
|
-
# @param options [Hash] options for callback registration
|
94
|
-
# @param block [Proc] optional block to use as callback
|
95
|
-
#
|
96
|
-
# @return [void]
|
97
|
-
#
|
98
|
-
# @example Register before_execution callback with symbol
|
99
|
-
# class MyTask < CMDx::Task
|
100
|
-
# before_execution :setup_database
|
101
|
-
# end
|
102
|
-
#
|
103
|
-
# @example Register before_execution callback with proc
|
104
|
-
# class MyTask < CMDx::Task
|
105
|
-
# before_execution -> { puts "Starting task execution" }
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
# @example Register before_execution callback with class
|
109
|
-
# class MyTask < CMDx::Task
|
110
|
-
# before_execution SetupCallback
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# @example Register before_execution callback with block
|
114
|
-
# class MyTask < CMDx::Task
|
115
|
-
# before_execution { |task| task.context.started_at = Time.now }
|
116
|
-
# end
|
117
|
-
#
|
118
|
-
# @example Register on_success callback with conditional options
|
119
|
-
# class MyTask < CMDx::Task
|
120
|
-
# on_success :send_notification, if: -> { Rails.env.production? }
|
121
|
-
# end
|
122
|
-
#
|
123
|
-
# @example Register on_success callback with multiple callables
|
124
|
-
# class MyTask < CMDx::Task
|
125
|
-
# on_success :log_success, :send_email, :update_metrics
|
126
|
-
# end
|
127
|
-
define_method(callback) do |*callables, **options, &block|
|
128
|
-
cmd_callbacks.register(callback, *callables, **options, &block)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# Retrieves a configuration setting value by key.
|
133
|
-
#
|
134
|
-
# Provides access to task-specific configuration settings that control
|
135
|
-
# various aspects of task execution including logging, halt conditions,
|
136
|
-
# and custom settings.
|
137
|
-
#
|
138
|
-
# @param key [Symbol, String] the configuration setting key to retrieve
|
42
|
+
# @param options [Hash] Configuration options to merge with existing settings
|
43
|
+
# @option options [AttributeRegistry] :attributes Registry for task attributes
|
44
|
+
# @option options [Boolean] :deprecate Whether the task is deprecated
|
45
|
+
# @option options [Array<Symbol>] :tags Tags associated with the task
|
139
46
|
#
|
140
|
-
# @return [
|
47
|
+
# @return [Hash] The merged settings hash
|
141
48
|
#
|
142
|
-
# @example
|
143
|
-
# MyTask
|
144
|
-
#
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
49
|
+
# @example
|
50
|
+
# class MyTask < Task
|
51
|
+
# settings deprecate: true, tags: [:experimental]
|
52
|
+
# end
|
53
|
+
def settings(**options)
|
54
|
+
@settings ||= begin
|
55
|
+
hash =
|
56
|
+
if superclass.respond_to?(:settings)
|
57
|
+
superclass.settings
|
58
|
+
else
|
59
|
+
CMDx.configuration.to_h.except(:logger)
|
60
|
+
end.transform_values(&:dup)
|
151
61
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
# @example Check for existing setting
|
159
|
-
# MyTask.cmd_setting?(:logger) #=> true
|
160
|
-
#
|
161
|
-
# @example Check for non-existing setting
|
162
|
-
# MyTask.cmd_setting?(:nonexistent) #=> false
|
163
|
-
def cmd_setting?(key)
|
164
|
-
cmd_settings.key?(key)
|
62
|
+
hash[:attributes] ||= AttributeRegistry.new
|
63
|
+
hash[:deprecate] ||= false
|
64
|
+
hash[:tags] ||= []
|
65
|
+
|
66
|
+
hash.merge!(options)
|
67
|
+
end
|
165
68
|
end
|
166
69
|
|
167
|
-
#
|
70
|
+
# @param type [Symbol] The type of registry to register with
|
71
|
+
# @param object [Object] The object to register
|
72
|
+
# @param args [Array] Additional arguments for registration
|
168
73
|
#
|
169
|
-
#
|
170
|
-
# allowing tasks to customize their execution behavior.
|
74
|
+
# @raise [RuntimeError] If the registry type is unknown
|
171
75
|
#
|
172
|
-
# @
|
173
|
-
#
|
174
|
-
#
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
76
|
+
# @example
|
77
|
+
# register(:attribute, MyAttribute.new)
|
78
|
+
# register(:callback, :before, -> { puts "before" })
|
79
|
+
def register(type, object, ...)
|
80
|
+
case type
|
81
|
+
when :attribute then settings[:attributes].register(object, ...)
|
82
|
+
when :callback then settings[:callbacks].register(object, ...)
|
83
|
+
when :coercion then settings[:coercions].register(object, ...)
|
84
|
+
when :middleware then settings[:middlewares].register(object, ...)
|
85
|
+
when :validator then settings[:validators].register(object, ...)
|
86
|
+
else raise "unknown registry type #{type.inspect}"
|
87
|
+
end
|
183
88
|
end
|
184
89
|
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
# extensions that modify or enhance task execution behavior.
|
189
|
-
#
|
190
|
-
# @param type [Symbol] the type of extension to register (:middleware, :callback, :validator, :coercion)
|
191
|
-
# @param object [Object] the extension object to register
|
192
|
-
# @param args [Array] additional arguments for registration
|
193
|
-
#
|
194
|
-
# @return [void]
|
195
|
-
#
|
196
|
-
# @raise [ArgumentError] if an unsupported type is provided
|
197
|
-
#
|
198
|
-
# @example Register coercion
|
199
|
-
# class MyTask < CMDx::Task
|
200
|
-
# use :coercion, TemperatureCoercion
|
201
|
-
# end
|
202
|
-
#
|
203
|
-
# @example Register validator
|
204
|
-
# class MyTask < CMDx::Task
|
205
|
-
# use :validator, ZipcodeValidator, country: "US"
|
206
|
-
# end
|
90
|
+
# @param type [Symbol] The type of registry to deregister from
|
91
|
+
# @param object [Object] The object to deregister
|
92
|
+
# @param args [Array] Additional arguments for deregistration
|
207
93
|
#
|
208
|
-
# @
|
209
|
-
# class MyTask < CMDx::Task
|
210
|
-
# use :middleware, CMDx::Middlewares::Timeout.new(seconds: 30)
|
211
|
-
# end
|
94
|
+
# @raise [RuntimeError] If the registry type is unknown
|
212
95
|
#
|
213
|
-
# @example
|
214
|
-
#
|
215
|
-
#
|
216
|
-
|
217
|
-
def use(type, object, ...)
|
96
|
+
# @example
|
97
|
+
# deregister(:attribute, :name)
|
98
|
+
# deregister(:callback, :before, MyCallback)
|
99
|
+
def deregister(type, object, ...)
|
218
100
|
case type
|
219
|
-
when :
|
220
|
-
|
221
|
-
when :
|
222
|
-
|
223
|
-
when :validator
|
224
|
-
|
225
|
-
when :coercion
|
226
|
-
cmd_coercions.register(type, object, ...)
|
101
|
+
when :attribute then settings[:attributes].deregister(object, ...)
|
102
|
+
when :callback then settings[:callbacks].deregister(object, ...)
|
103
|
+
when :coercion then settings[:coercions].deregister(object, ...)
|
104
|
+
when :middleware then settings[:middlewares].deregister(object, ...)
|
105
|
+
when :validator then settings[:validators].deregister(object, ...)
|
106
|
+
else raise "unknown registry type #{type.inspect}"
|
227
107
|
end
|
228
108
|
end
|
229
109
|
|
230
|
-
#
|
231
|
-
#
|
232
|
-
# Creates parameter definitions that are not required for task execution
|
233
|
-
# but will be validated and coerced if provided. Supports nested parameter
|
234
|
-
# structures through block syntax.
|
110
|
+
# @param args [Array] Arguments to build the attribute with
|
235
111
|
#
|
236
|
-
# @
|
237
|
-
#
|
238
|
-
#
|
239
|
-
|
240
|
-
|
241
|
-
# @param block [Proc] optional block for defining nested parameters
|
242
|
-
#
|
243
|
-
# @return [Array<Parameter>] the created parameter definitions
|
244
|
-
#
|
245
|
-
# @example Define simple optional parameters
|
246
|
-
# class MyTask < CMDx::Task
|
247
|
-
# optional :name, :email, type: :string
|
248
|
-
# optional :age, type: :integer, default: 0
|
249
|
-
# end
|
250
|
-
#
|
251
|
-
# @example Define optional parameter with validation
|
252
|
-
# class MyTask < CMDx::Task
|
253
|
-
# optional :score, type: :integer, validates: { numeric: { greater_than: 0 } }
|
254
|
-
# end
|
255
|
-
#
|
256
|
-
# @example Define nested optional parameters
|
257
|
-
# class MyTask < CMDx::Task
|
258
|
-
# optional :user, type: :hash do
|
259
|
-
# required :name, type: :string
|
260
|
-
# optional :age, type: :integer
|
261
|
-
# end
|
262
|
-
# end
|
263
|
-
def optional(*attributes, **options, &)
|
264
|
-
parameters = Parameter.optional(*attributes, **options.merge(klass: self), &)
|
265
|
-
cmd_parameters.registry.concat(parameters)
|
112
|
+
# @example
|
113
|
+
# attributes :name, :email
|
114
|
+
# attributes :age, type: Integer, default: 18
|
115
|
+
def attributes(...)
|
116
|
+
register(:attribute, Attribute.build(...))
|
266
117
|
end
|
118
|
+
alias attribute attributes
|
267
119
|
|
268
|
-
#
|
269
|
-
#
|
270
|
-
# Creates parameter definitions that must be provided for successful task
|
271
|
-
# execution. Missing required parameters will cause task validation to fail.
|
272
|
-
# Supports nested parameter structures through block syntax.
|
273
|
-
#
|
274
|
-
# @param attributes [Array<Symbol>] parameter names to define as required
|
275
|
-
# @param options [Hash] parameter configuration options
|
276
|
-
# @option options [Symbol, Array<Symbol>] :type parameter type(s) for coercion
|
277
|
-
# @option options [Object] :default default value if parameter not provided
|
278
|
-
# @option options [Hash] :validates validation rules to apply
|
279
|
-
# @param block [Proc] optional block for defining nested parameters
|
120
|
+
# @param args [Array] Arguments to build the optional attribute with
|
280
121
|
#
|
281
|
-
# @
|
282
|
-
#
|
283
|
-
#
|
284
|
-
|
285
|
-
|
286
|
-
# required :action, type: :string
|
287
|
-
# end
|
288
|
-
#
|
289
|
-
# @example Define required parameter with validation
|
290
|
-
# class MyTask < CMDx::Task
|
291
|
-
# required :email, type: :string, validates: { format: /@/ }
|
292
|
-
# end
|
293
|
-
#
|
294
|
-
# @example Define nested required parameters
|
295
|
-
# class MyTask < CMDx::Task
|
296
|
-
# required :payment, type: :hash do
|
297
|
-
# required :amount, type: :big_decimal
|
298
|
-
# required :currency, type: :string
|
299
|
-
# optional :description, type: :string
|
300
|
-
# end
|
301
|
-
# end
|
302
|
-
def required(*attributes, **options, &)
|
303
|
-
parameters = Parameter.required(*attributes, **options.merge(klass: self), &)
|
304
|
-
cmd_parameters.registry.concat(parameters)
|
122
|
+
# @example
|
123
|
+
# optional :description, :notes
|
124
|
+
# optional :priority, type: Symbol, default: :normal
|
125
|
+
def optional(...)
|
126
|
+
register(:attribute, Attribute.optional(...))
|
305
127
|
end
|
306
128
|
|
307
|
-
#
|
129
|
+
# @param args [Array] Arguments to build the required attribute with
|
308
130
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
131
|
+
# @example
|
132
|
+
# required :name, :email
|
133
|
+
# required :age, type: Integer, min: 0
|
134
|
+
def required(...)
|
135
|
+
register(:attribute, Attribute.required(...))
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param names [Array<Symbol>] Names of attributes to remove
|
316
139
|
#
|
317
|
-
# @example
|
318
|
-
#
|
319
|
-
|
320
|
-
|
321
|
-
instance = new(...)
|
322
|
-
instance.process
|
323
|
-
instance.result
|
140
|
+
# @example
|
141
|
+
# remove_attributes :old_field, :deprecated_field
|
142
|
+
def remove_attributes(*names)
|
143
|
+
deregister(:attribute, names)
|
324
144
|
end
|
145
|
+
alias remove_attribute remove_attributes
|
325
146
|
|
326
|
-
|
147
|
+
CallbackRegistry::TYPES.each do |callback|
|
148
|
+
# @param callables [Array] Callable objects to register as callbacks
|
149
|
+
# @param options [Hash] Options for the callback registration
|
150
|
+
# @option options [Symbol] :priority Priority of the callback
|
151
|
+
# @option options [Boolean] :async Whether the callback should run asynchronously
|
152
|
+
# @param block [Proc] Block to register as a callback
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# before { puts "before execution" }
|
156
|
+
# after :cleanup, priority: :high
|
157
|
+
# around ->(task) { task.logger.info("starting") }
|
158
|
+
define_method(callback) do |*callables, **options, &block|
|
159
|
+
register(:callback, callback, *callables, **options, &block)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# @param args [Array] Arguments to pass to the task constructor
|
327
164
|
#
|
328
|
-
#
|
329
|
-
# the complete execution pipeline, and returns the result. This method will
|
330
|
-
# raise appropriate fault exceptions if the task fails or is skipped.
|
165
|
+
# @return [Result] The execution result
|
331
166
|
#
|
332
|
-
# @
|
167
|
+
# @example
|
168
|
+
# result = MyTask.execute(name: "example")
|
169
|
+
# if result.success?
|
170
|
+
# puts "Task completed successfully"
|
171
|
+
# end
|
172
|
+
def execute(...)
|
173
|
+
task = new(...)
|
174
|
+
task.execute(raise: false)
|
175
|
+
task.result
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param args [Array] Arguments to pass to the task constructor
|
333
179
|
#
|
334
|
-
# @return [Result]
|
180
|
+
# @return [Result] The execution result
|
335
181
|
#
|
336
|
-
# @raise [
|
337
|
-
# @raise [Skipped] when task execution is skipped
|
182
|
+
# @raise [ExecutionError] If the task execution fails
|
338
183
|
#
|
339
|
-
# @example
|
340
|
-
#
|
341
|
-
#
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
def call!(...)
|
347
|
-
instance = new(...)
|
348
|
-
instance.process!
|
349
|
-
instance.result
|
184
|
+
# @example
|
185
|
+
# result = MyTask.execute!(name: "example")
|
186
|
+
# # Will raise an exception if execution fails
|
187
|
+
def execute!(...)
|
188
|
+
task = new(...)
|
189
|
+
task.execute(raise: true)
|
190
|
+
task.result
|
350
191
|
end
|
351
192
|
|
352
193
|
end
|
353
194
|
|
354
|
-
#
|
355
|
-
#
|
356
|
-
# This method contains the actual business logic for the task. Subclasses
|
357
|
-
# must override this method to provide their specific implementation.
|
358
|
-
# The method has access to the task's context, can modify it, and can
|
359
|
-
# use skip!, fail!, or throw! to control execution flow.
|
195
|
+
# @param raise [Boolean] Whether to raise exceptions on failure
|
360
196
|
#
|
361
|
-
# @return [
|
197
|
+
# @return [Result] The execution result
|
362
198
|
#
|
363
|
-
# @
|
364
|
-
#
|
365
|
-
#
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
#
|
371
|
-
# skip!(reason: "User already processed") if user.processed?
|
199
|
+
# @example
|
200
|
+
# result = task.execute
|
201
|
+
# result = task.execute(raise: true)
|
202
|
+
def execute(raise: false)
|
203
|
+
Worker.execute(self, raise:)
|
204
|
+
end
|
205
|
+
|
206
|
+
# @raise [UndefinedMethodError] Always raised as this method must be overridden
|
372
207
|
#
|
373
|
-
#
|
374
|
-
#
|
208
|
+
# @example
|
209
|
+
# class MyTask < Task
|
210
|
+
# def work
|
211
|
+
# # Custom work logic here
|
212
|
+
# puts "Performing work..."
|
375
213
|
# end
|
376
214
|
# end
|
377
|
-
def
|
378
|
-
raise
|
215
|
+
def work
|
216
|
+
raise UndefinedMethodError, "undefined method #{self.class.name}#work"
|
379
217
|
end
|
380
218
|
|
381
|
-
#
|
382
|
-
#
|
383
|
-
# Processes the task by running it through all registered middleware and
|
384
|
-
# the TaskProcessor. This method captures exceptions and converts them
|
385
|
-
# into result states rather than propagating them.
|
386
|
-
#
|
387
|
-
# @return [void]
|
219
|
+
# @return [Logger] The logger instance for this task
|
388
220
|
#
|
389
|
-
# @example
|
390
|
-
#
|
391
|
-
#
|
392
|
-
|
393
|
-
|
394
|
-
|
221
|
+
# @example
|
222
|
+
# logger.info "Starting task execution"
|
223
|
+
# logger.error "Task failed", error: exception
|
224
|
+
def logger
|
225
|
+
@logger ||= begin
|
226
|
+
logger = self.class.settings[:logger] || CMDx.configuration.logger
|
227
|
+
logger.level = self.class.settings[:log_level] || logger.level
|
228
|
+
logger.formatter = self.class.settings[:log_formatter] || logger.formatter
|
229
|
+
logger
|
230
|
+
end
|
395
231
|
end
|
396
232
|
|
397
|
-
#
|
398
|
-
#
|
399
|
-
# Processes the task by running it through all registered middleware and
|
400
|
-
# the TaskProcessor. This method will raise appropriate fault exceptions
|
401
|
-
# if the task fails or is skipped.
|
233
|
+
# @return [Hash] A hash representation of the task
|
402
234
|
#
|
403
|
-
# @return [
|
235
|
+
# @option return [Integer] :index The result index
|
236
|
+
# @option return [String] :chain_id The chain identifier
|
237
|
+
# @option return [String] :type The task type ("Task" or "Workflow")
|
238
|
+
# @option return [Array<Symbol>] :tags The task tags
|
239
|
+
# @option return [String] :class The task class name
|
240
|
+
# @option return [String] :id The task identifier
|
404
241
|
#
|
405
|
-
# @
|
406
|
-
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
242
|
+
# @example
|
243
|
+
# task_hash = task.to_h
|
244
|
+
# puts "Task type: #{task_hash[:type]}"
|
245
|
+
# puts "Task tags: #{task_hash[:tags].join(', ')}"
|
246
|
+
def to_h
|
247
|
+
{
|
248
|
+
index: result.index,
|
249
|
+
chain_id: chain.id,
|
250
|
+
type: self.class.include?(Workflow) ? "Workflow" : "Task",
|
251
|
+
tags: self.class.settings[:tags],
|
252
|
+
class: self.class.name,
|
253
|
+
id:
|
254
|
+
}
|
418
255
|
end
|
419
256
|
|
420
|
-
#
|
421
|
-
#
|
422
|
-
# Returns a logger instance that is pre-configured with the task's
|
423
|
-
# settings and context information for consistent logging throughout
|
424
|
-
# task execution.
|
257
|
+
# @return [String] A string representation of the task
|
425
258
|
#
|
426
|
-
# @
|
427
|
-
#
|
428
|
-
#
|
429
|
-
|
430
|
-
|
431
|
-
# # ... task logic ...
|
432
|
-
# logger.info "User processing completed"
|
433
|
-
# end
|
434
|
-
def logger
|
435
|
-
Logger.call(self)
|
259
|
+
# @example
|
260
|
+
# puts task.to_s
|
261
|
+
# # Output: "Task[MyTask] tags: [:important] id: abc123"
|
262
|
+
def to_s
|
263
|
+
Utils::Format.to_str(to_h)
|
436
264
|
end
|
437
265
|
|
438
266
|
end
|