cmdx 0.5.0 → 1.0.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/rules/cursor-instructions.mdc +6 -0
- data/.rubocop.yml +16 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +31 -1
- data/README.md +72 -25
- data/docs/ai_prompts.md +309 -0
- data/docs/basics/call.md +225 -14
- data/docs/basics/chain.md +271 -0
- data/docs/basics/context.md +232 -33
- data/docs/basics/setup.md +76 -12
- data/docs/callbacks.md +273 -0
- data/docs/configuration.md +158 -28
- data/docs/getting_started.md +134 -22
- data/docs/interruptions/exceptions.md +189 -11
- data/docs/interruptions/faults.md +187 -44
- data/docs/interruptions/halt.md +179 -35
- data/docs/logging.md +194 -53
- data/docs/middlewares.md +735 -0
- data/docs/outcomes/result.md +296 -10
- data/docs/outcomes/states.md +203 -31
- data/docs/outcomes/statuses.md +275 -30
- data/docs/parameters/coercions.md +402 -29
- data/docs/parameters/defaults.md +249 -25
- data/docs/parameters/definitions.md +238 -72
- data/docs/parameters/namespacing.md +250 -27
- data/docs/parameters/validations.md +193 -168
- data/docs/testing.md +550 -0
- data/docs/tips_and_tricks.md +95 -43
- data/docs/workflows.md +319 -0
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +69 -0
- data/lib/cmdx/callback_registry.rb +106 -0
- data/lib/cmdx/chain.rb +190 -0
- data/lib/cmdx/chain_inspector.rb +149 -0
- data/lib/cmdx/chain_serializer.rb +175 -0
- data/lib/cmdx/coercions/array.rb +37 -0
- data/lib/cmdx/coercions/big_decimal.rb +33 -0
- data/lib/cmdx/coercions/boolean.rb +41 -1
- data/lib/cmdx/coercions/complex.rb +31 -0
- data/lib/cmdx/coercions/date.rb +39 -0
- data/lib/cmdx/coercions/date_time.rb +39 -0
- data/lib/cmdx/coercions/float.rb +31 -0
- data/lib/cmdx/coercions/hash.rb +42 -0
- data/lib/cmdx/coercions/integer.rb +32 -0
- data/lib/cmdx/coercions/rational.rb +31 -0
- data/lib/cmdx/coercions/string.rb +31 -0
- data/lib/cmdx/coercions/time.rb +39 -0
- data/lib/cmdx/coercions/virtual.rb +31 -0
- data/lib/cmdx/configuration.rb +217 -9
- data/lib/cmdx/context.rb +173 -2
- data/lib/cmdx/core_ext/hash.rb +72 -0
- data/lib/cmdx/core_ext/module.rb +94 -0
- data/lib/cmdx/core_ext/object.rb +105 -0
- data/lib/cmdx/correlator.rb +217 -0
- data/lib/cmdx/error.rb +210 -8
- data/lib/cmdx/errors.rb +256 -1
- data/lib/cmdx/fault.rb +177 -2
- data/lib/cmdx/faults.rb +158 -2
- data/lib/cmdx/immutator.rb +121 -2
- data/lib/cmdx/lazy_struct.rb +261 -18
- data/lib/cmdx/log_formatters/json.rb +46 -0
- data/lib/cmdx/log_formatters/key_value.rb +46 -0
- data/lib/cmdx/log_formatters/line.rb +54 -0
- data/lib/cmdx/log_formatters/logstash.rb +64 -0
- data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
- data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
- data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
- data/lib/cmdx/log_formatters/raw.rb +54 -0
- data/lib/cmdx/logger.rb +85 -0
- data/lib/cmdx/logger_ansi.rb +93 -7
- data/lib/cmdx/logger_serializer.rb +116 -0
- data/lib/cmdx/middleware.rb +74 -0
- data/lib/cmdx/middleware_registry.rb +106 -0
- data/lib/cmdx/middlewares/correlate.rb +266 -0
- data/lib/cmdx/middlewares/timeout.rb +232 -0
- data/lib/cmdx/parameter.rb +228 -1
- data/lib/cmdx/parameter_inspector.rb +61 -0
- data/lib/cmdx/parameter_registry.rb +125 -0
- data/lib/cmdx/parameter_serializer.rb +83 -0
- data/lib/cmdx/parameter_validator.rb +62 -0
- data/lib/cmdx/parameter_value.rb +109 -1
- data/lib/cmdx/parameters_inspector.rb +59 -0
- data/lib/cmdx/parameters_serializer.rb +102 -0
- data/lib/cmdx/railtie.rb +123 -3
- data/lib/cmdx/result.rb +367 -25
- data/lib/cmdx/result_ansi.rb +105 -9
- data/lib/cmdx/result_inspector.rb +76 -0
- data/lib/cmdx/result_logger.rb +90 -3
- data/lib/cmdx/result_serializer.rb +137 -0
- data/lib/cmdx/rspec/result_matchers.rb +917 -0
- data/lib/cmdx/rspec/task_matchers.rb +570 -0
- data/lib/cmdx/task.rb +405 -37
- data/lib/cmdx/task_serializer.rb +74 -2
- data/lib/cmdx/utils/ansi_color.rb +95 -0
- data/lib/cmdx/utils/log_timestamp.rb +48 -0
- data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
- data/lib/cmdx/utils/name_affix.rb +78 -0
- data/lib/cmdx/validators/custom.rb +82 -0
- data/lib/cmdx/validators/exclusion.rb +94 -0
- data/lib/cmdx/validators/format.rb +102 -8
- data/lib/cmdx/validators/inclusion.rb +104 -0
- data/lib/cmdx/validators/length.rb +128 -0
- data/lib/cmdx/validators/numeric.rb +128 -0
- data/lib/cmdx/validators/presence.rb +93 -7
- data/lib/cmdx/version.rb +7 -1
- data/lib/cmdx/workflow.rb +394 -0
- data/lib/cmdx.rb +25 -64
- data/lib/generators/cmdx/install_generator.rb +37 -1
- data/lib/generators/cmdx/task_generator.rb +69 -1
- data/lib/generators/cmdx/templates/install.rb +8 -12
- data/lib/generators/cmdx/workflow_generator.rb +109 -0
- metadata +54 -15
- data/docs/basics/run.md +0 -34
- data/docs/batch.md +0 -53
- data/docs/example.md +0 -82
- data/docs/hooks.md +0 -62
- data/lib/cmdx/batch.rb +0 -43
- data/lib/cmdx/parameters.rb +0 -35
- data/lib/cmdx/run.rb +0 -39
- data/lib/cmdx/run_inspector.rb +0 -26
- data/lib/cmdx/run_serializer.rb +0 -20
- data/lib/cmdx/task_hook.rb +0 -18
- data/lib/generators/cmdx/batch_generator.rb +0 -30
- /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
@@ -1,13 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Cmdx
|
4
|
+
##
|
5
|
+
# Rails generator for creating CMDx task classes.
|
6
|
+
#
|
7
|
+
# This generator creates individual task files that encapsulate specific
|
8
|
+
# business logic operations. Tasks inherit from CMDx::Task and provide
|
9
|
+
# parameter validation, callbacks, result tracking, and error handling
|
10
|
+
# capabilities for focused business operations.
|
11
|
+
#
|
12
|
+
# The generator handles name normalization, ensuring "Task" suffix
|
13
|
+
# and proper file naming conventions. Generated tasks inherit from
|
14
|
+
# ApplicationTask when available, falling back to CMDx::Task.
|
15
|
+
#
|
16
|
+
# @example Generate a task
|
17
|
+
# rails generate cmdx:task SendEmail
|
18
|
+
# rails generate cmdx:task ProcessPayment
|
19
|
+
# rails generate cmdx:task ProcessPaymentTask # "Task" suffix preserved
|
20
|
+
#
|
21
|
+
# @example Generated file location
|
22
|
+
# app/cmds/send_email_task.rb
|
23
|
+
# app/cmds/process_payment_task.rb
|
24
|
+
#
|
25
|
+
# @since 1.0.0
|
4
26
|
class TaskGenerator < Rails::Generators::NamedBase
|
5
27
|
|
6
28
|
source_root File.expand_path("templates", __dir__)
|
7
29
|
check_class_collision suffix: "Task"
|
8
30
|
|
9
|
-
desc "
|
31
|
+
desc "Creates a task with the given NAME"
|
10
32
|
|
33
|
+
##
|
34
|
+
# Copies the task template to the application commands directory.
|
35
|
+
#
|
36
|
+
# Creates a new task file in `app/cmds/` with the normalized name.
|
37
|
+
# The generator automatically handles:
|
38
|
+
# - Removing "Task" suffix from file naming
|
39
|
+
# - Converting to snake_case for file naming
|
40
|
+
# - Adding "_task" suffix to the filename
|
41
|
+
# - Setting up proper class inheritance
|
42
|
+
#
|
43
|
+
# @return [void]
|
44
|
+
# @raise [Thor::Error] if the destination file cannot be created
|
45
|
+
#
|
46
|
+
# @example Generated task structure
|
47
|
+
# class SendEmailTask < ApplicationTask
|
48
|
+
# def call
|
49
|
+
# # Task business logic
|
50
|
+
# end
|
51
|
+
# end
|
11
52
|
def copy_files
|
12
53
|
name = file_name.sub(/_?task$/i, "")
|
13
54
|
path = File.join("app/cmds", class_path, "#{name}_task.rb")
|
@@ -16,10 +57,37 @@ module Cmdx
|
|
16
57
|
|
17
58
|
private
|
18
59
|
|
60
|
+
##
|
61
|
+
# Normalizes the class name by ensuring "Task" suffix.
|
62
|
+
#
|
63
|
+
# Ensures consistent class naming by adding "Task" suffix if not
|
64
|
+
# already present, allowing users to specify either "SendEmail"
|
65
|
+
# or "SendEmailTask".
|
66
|
+
#
|
67
|
+
# @return [String] the normalized class name with "Task" suffix
|
68
|
+
#
|
69
|
+
# @example Class name normalization
|
70
|
+
# # Input: "SendEmail"
|
71
|
+
# # Output: "SendEmailTask"
|
72
|
+
#
|
73
|
+
# # Input: "SendEmailTask"
|
74
|
+
# # Output: "SendEmailTask"
|
19
75
|
def class_name
|
20
76
|
@class_name ||= super.end_with?("Task") ? super : "#{super}Task"
|
21
77
|
end
|
22
78
|
|
79
|
+
##
|
80
|
+
# Determines the parent class for the generated task.
|
81
|
+
#
|
82
|
+
# Attempts to use ApplicationTask as the parent class if available,
|
83
|
+
# falling back to CMDx::Task if ApplicationTask is not defined.
|
84
|
+
# This allows applications to define custom base task behavior.
|
85
|
+
#
|
86
|
+
# @return [String] the parent class name to inherit from
|
87
|
+
#
|
88
|
+
# @example Parent class resolution
|
89
|
+
# # If ApplicationTask exists: "ApplicationTask"
|
90
|
+
# # If ApplicationTask missing: "CMDx::Task"
|
23
91
|
def parent_class_name
|
24
92
|
ApplicationTask
|
25
93
|
rescue StandardError
|
@@ -1,23 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
CMDx.configure do |config|
|
4
|
-
#
|
5
|
-
# This option can accept an array of statuses.
|
4
|
+
# Halt execution and raise fault on these result statuses when using `call!`
|
6
5
|
config.task_halt = CMDx::Result::FAILED
|
7
6
|
|
8
|
-
#
|
9
|
-
config.task_timeout = nil
|
7
|
+
# Global timeout for individual tasks (nil = no timeout)
|
10
8
|
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
config.batch_halt = CMDx::Result::FAILED
|
9
|
+
# Stop workflow execution when tasks return these statuses
|
10
|
+
# Note: Skipped tasks continue processing by default
|
11
|
+
config.workflow_halt = CMDx::Result::FAILED
|
15
12
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
config.batch_timeout = nil
|
13
|
+
# Global timeout for entire workflow execution (nil = no timeout)
|
14
|
+
# Tip: Account for all tasks when setting this value
|
19
15
|
|
20
|
-
#
|
16
|
+
# Logger with formatter - see available formatters at:
|
21
17
|
# https://github.com/drexed/cmdx/tree/main/lib/cmdx/log_formatters
|
22
18
|
config.logger = Logger.new($stdout, formatter: CMDx::LogFormatters::Line.new)
|
23
19
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cmdx
|
4
|
+
##
|
5
|
+
# Rails generator for creating CMDx workflow task classes.
|
6
|
+
#
|
7
|
+
# This generator creates workflow task files that coordinate multiple
|
8
|
+
# individual tasks in a structured workflow. Workflow tasks inherit
|
9
|
+
# from CMDx::Workflow and provide orchestration capabilities for
|
10
|
+
# complex business processes.
|
11
|
+
#
|
12
|
+
# The generator handles name normalization, ensuring proper file naming
|
13
|
+
# conventions and class names. Generated workflow tasks inherit from
|
14
|
+
# ApplicationWorkflow when available, falling back to CMDx::Workflow.
|
15
|
+
#
|
16
|
+
# @example Generate a workflow task
|
17
|
+
# rails generate cmdx:workflow OrderProcessing
|
18
|
+
# rails generate cmdx:workflow PaymentWorkflow # "Workflow" suffix preserved
|
19
|
+
#
|
20
|
+
# @example Generated file location
|
21
|
+
# app/cmds/order_processing_workflow.rb
|
22
|
+
# app/cmds/payment_workflow.rb
|
23
|
+
#
|
24
|
+
# @example Generated class structure
|
25
|
+
# class OrderProcessingWorkflow < ApplicationWorkflow
|
26
|
+
# def call
|
27
|
+
# # Workflow orchestration logic
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @see CMDx::Workflow Base workflow class
|
32
|
+
# @see Rails::Generators::NamedBase Rails generator base class
|
33
|
+
# @since 1.0.0
|
34
|
+
class WorkflowGenerator < Rails::Generators::NamedBase
|
35
|
+
|
36
|
+
source_root File.expand_path("templates", __dir__)
|
37
|
+
check_class_collision suffix: "Workflow"
|
38
|
+
|
39
|
+
desc "Creates a workflow with the given NAME"
|
40
|
+
|
41
|
+
##
|
42
|
+
# Copies the workflow task template to the application commands directory.
|
43
|
+
#
|
44
|
+
# Creates a new workflow task file in `app/cmds/` with the normalized
|
45
|
+
# name. The generator automatically handles:
|
46
|
+
# - Removing "workflow" suffix from the provided name for filename
|
47
|
+
# - Converting to snake_case for file naming
|
48
|
+
# - Adding "_workflow" suffix to the filename
|
49
|
+
# - Setting up proper class inheritance
|
50
|
+
# - Ensuring class names end with "Workflow"
|
51
|
+
#
|
52
|
+
# @return [void]
|
53
|
+
# @raise [Thor::Error] if the destination file cannot be created
|
54
|
+
#
|
55
|
+
# @example File generation
|
56
|
+
# # Input: rails generate cmdx:workflow OrderProcessing
|
57
|
+
# # Creates: app/cmds/order_processing_workflow.rb
|
58
|
+
# # Class: OrderProcessingWorkflow
|
59
|
+
#
|
60
|
+
# # Input: rails generate cmdx:workflow PaymentWorkflow
|
61
|
+
# # Creates: app/cmds/payment_workflow.rb
|
62
|
+
# # Class: PaymentWorkflow
|
63
|
+
def copy_files
|
64
|
+
name = file_name.sub(/_?workflow$/i, "")
|
65
|
+
path = File.join("app/cmds", class_path, "#{name}_workflow.rb")
|
66
|
+
template("workflow.rb.tt", path)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
##
|
72
|
+
# Normalizes the class name by ensuring it ends with "Workflow".
|
73
|
+
#
|
74
|
+
# Ensures consistent class naming by appending "Workflow" suffix
|
75
|
+
# to the provided generator name if it doesn't already end with it,
|
76
|
+
# allowing users to specify either "OrderProcessing" or "OrderProcessingWorkflow".
|
77
|
+
#
|
78
|
+
# @return [String] the normalized class name with "Workflow" suffix
|
79
|
+
#
|
80
|
+
# @example Class name normalization
|
81
|
+
# # Input: "OrderProcessing"
|
82
|
+
# # Output: "OrderProcessingWorkflow"
|
83
|
+
#
|
84
|
+
# # Input: "PaymentWorkflow"
|
85
|
+
# # Output: "PaymentWorkflow"
|
86
|
+
def class_name
|
87
|
+
@class_name ||= super.end_with?("Workflow") ? super : "#{super}Workflow"
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Determines the parent class for the generated workflow task.
|
92
|
+
#
|
93
|
+
# Attempts to use ApplicationWorkflow as the parent class if available,
|
94
|
+
# falling back to CMDx::Workflow if ApplicationWorkflow is not defined.
|
95
|
+
# This allows applications to define custom base workflow behavior.
|
96
|
+
#
|
97
|
+
# @return [String] the parent class name to inherit from
|
98
|
+
#
|
99
|
+
# @example Parent class resolution
|
100
|
+
# # If ApplicationWorkflow exists: "ApplicationWorkflow"
|
101
|
+
# # If ApplicationWorkflow missing: "CMDx::Workflow"
|
102
|
+
def parent_class_name
|
103
|
+
ApplicationWorkflow
|
104
|
+
rescue StandardError
|
105
|
+
CMDx::Workflow
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmdx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bigdecimal
|
@@ -51,6 +51,20 @@ dependencies:
|
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: zeitwerk
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
54
68
|
- !ruby/object:Gem::Dependency
|
55
69
|
name: bundler
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,6 +79,20 @@ dependencies:
|
|
65
79
|
- - ">="
|
66
80
|
- !ruby/object:Gem::Version
|
67
81
|
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: ostruct
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
68
96
|
- !ruby/object:Gem::Dependency
|
69
97
|
name: rake
|
70
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,6 +186,7 @@ extensions: []
|
|
158
186
|
extra_rdoc_files: []
|
159
187
|
files:
|
160
188
|
- ".DS_Store"
|
189
|
+
- ".cursor/rules/cursor-instructions.mdc"
|
161
190
|
- ".rspec"
|
162
191
|
- ".rubocop.yml"
|
163
192
|
- ".ruby-version"
|
@@ -166,19 +195,19 @@ files:
|
|
166
195
|
- LICENSE.txt
|
167
196
|
- README.md
|
168
197
|
- Rakefile
|
198
|
+
- docs/ai_prompts.md
|
169
199
|
- docs/basics/call.md
|
200
|
+
- docs/basics/chain.md
|
170
201
|
- docs/basics/context.md
|
171
|
-
- docs/basics/run.md
|
172
202
|
- docs/basics/setup.md
|
173
|
-
- docs/
|
203
|
+
- docs/callbacks.md
|
174
204
|
- docs/configuration.md
|
175
|
-
- docs/example.md
|
176
205
|
- docs/getting_started.md
|
177
|
-
- docs/hooks.md
|
178
206
|
- docs/interruptions/exceptions.md
|
179
207
|
- docs/interruptions/faults.md
|
180
208
|
- docs/interruptions/halt.md
|
181
209
|
- docs/logging.md
|
210
|
+
- docs/middlewares.md
|
182
211
|
- docs/outcomes/result.md
|
183
212
|
- docs/outcomes/states.md
|
184
213
|
- docs/outcomes/statuses.md
|
@@ -187,10 +216,16 @@ files:
|
|
187
216
|
- docs/parameters/definitions.md
|
188
217
|
- docs/parameters/namespacing.md
|
189
218
|
- docs/parameters/validations.md
|
219
|
+
- docs/testing.md
|
190
220
|
- docs/tips_and_tricks.md
|
221
|
+
- docs/workflows.md
|
191
222
|
- lib/cmdx.rb
|
192
223
|
- lib/cmdx/.DS_Store
|
193
|
-
- lib/cmdx/
|
224
|
+
- lib/cmdx/callback.rb
|
225
|
+
- lib/cmdx/callback_registry.rb
|
226
|
+
- lib/cmdx/chain.rb
|
227
|
+
- lib/cmdx/chain_inspector.rb
|
228
|
+
- lib/cmdx/chain_serializer.rb
|
194
229
|
- lib/cmdx/coercions/array.rb
|
195
230
|
- lib/cmdx/coercions/big_decimal.rb
|
196
231
|
- lib/cmdx/coercions/boolean.rb
|
@@ -209,6 +244,7 @@ files:
|
|
209
244
|
- lib/cmdx/core_ext/hash.rb
|
210
245
|
- lib/cmdx/core_ext/module.rb
|
211
246
|
- lib/cmdx/core_ext/object.rb
|
247
|
+
- lib/cmdx/correlator.rb
|
212
248
|
- lib/cmdx/error.rb
|
213
249
|
- lib/cmdx/errors.rb
|
214
250
|
- lib/cmdx/fault.rb
|
@@ -226,12 +262,16 @@ files:
|
|
226
262
|
- lib/cmdx/logger.rb
|
227
263
|
- lib/cmdx/logger_ansi.rb
|
228
264
|
- lib/cmdx/logger_serializer.rb
|
265
|
+
- lib/cmdx/middleware.rb
|
266
|
+
- lib/cmdx/middleware_registry.rb
|
267
|
+
- lib/cmdx/middlewares/correlate.rb
|
268
|
+
- lib/cmdx/middlewares/timeout.rb
|
229
269
|
- lib/cmdx/parameter.rb
|
230
270
|
- lib/cmdx/parameter_inspector.rb
|
271
|
+
- lib/cmdx/parameter_registry.rb
|
231
272
|
- lib/cmdx/parameter_serializer.rb
|
232
273
|
- lib/cmdx/parameter_validator.rb
|
233
274
|
- lib/cmdx/parameter_value.rb
|
234
|
-
- lib/cmdx/parameters.rb
|
235
275
|
- lib/cmdx/parameters_inspector.rb
|
236
276
|
- lib/cmdx/parameters_serializer.rb
|
237
277
|
- lib/cmdx/railtie.rb
|
@@ -240,11 +280,9 @@ files:
|
|
240
280
|
- lib/cmdx/result_inspector.rb
|
241
281
|
- lib/cmdx/result_logger.rb
|
242
282
|
- lib/cmdx/result_serializer.rb
|
243
|
-
- lib/cmdx/
|
244
|
-
- lib/cmdx/
|
245
|
-
- lib/cmdx/run_serializer.rb
|
283
|
+
- lib/cmdx/rspec/result_matchers.rb
|
284
|
+
- lib/cmdx/rspec/task_matchers.rb
|
246
285
|
- lib/cmdx/task.rb
|
247
|
-
- lib/cmdx/task_hook.rb
|
248
286
|
- lib/cmdx/task_serializer.rb
|
249
287
|
- lib/cmdx/utils/ansi_color.rb
|
250
288
|
- lib/cmdx/utils/log_timestamp.rb
|
@@ -258,12 +296,13 @@ files:
|
|
258
296
|
- lib/cmdx/validators/numeric.rb
|
259
297
|
- lib/cmdx/validators/presence.rb
|
260
298
|
- lib/cmdx/version.rb
|
261
|
-
- lib/
|
299
|
+
- lib/cmdx/workflow.rb
|
262
300
|
- lib/generators/cmdx/install_generator.rb
|
263
301
|
- lib/generators/cmdx/task_generator.rb
|
264
|
-
- lib/generators/cmdx/templates/batch.rb.tt
|
265
302
|
- lib/generators/cmdx/templates/install.rb
|
266
303
|
- lib/generators/cmdx/templates/task.rb.tt
|
304
|
+
- lib/generators/cmdx/templates/workflow.rb.tt
|
305
|
+
- lib/generators/cmdx/workflow_generator.rb
|
267
306
|
- lib/locales/en.yml
|
268
307
|
- lib/locales/es.yml
|
269
308
|
homepage: https://github.com/drexed/cmdx
|
@@ -290,7 +329,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
290
329
|
- !ruby/object:Gem::Version
|
291
330
|
version: '0'
|
292
331
|
requirements: []
|
293
|
-
rubygems_version: 3.6.
|
332
|
+
rubygems_version: 3.6.9
|
294
333
|
specification_version: 4
|
295
334
|
summary: Command (aka service) objects with intent
|
296
335
|
test_files: []
|
data/docs/basics/run.md
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# Basics - Run
|
2
|
-
|
3
|
-
A run represents a group of tasks executed as part of collection.
|
4
|
-
|
5
|
-
When building complex tasks, it's best to pass the parents context to subtasks
|
6
|
-
(unless necessary or preventative) so that it gains automated indexing and the
|
7
|
-
parents `run_id`. This makes it easy to identify all tasks involved in one
|
8
|
-
execution from logging and stdout console calls.
|
9
|
-
|
10
|
-
```ruby
|
11
|
-
class ProcessOrderTask < CMDx::Task
|
12
|
-
|
13
|
-
def call
|
14
|
-
# Subtasks inherit the ProcessOrderTask run_id:
|
15
|
-
SendEmailConfirmationTask.call(context)
|
16
|
-
NotifyPartnerWarehousesTask.call(context)
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
result = ProcessOrderTask.call
|
22
|
-
puts result.run.to_s
|
23
|
-
|
24
|
-
# Task name Index Run ID Task ID etc
|
25
|
-
# -----------------------------------------------------------------
|
26
|
-
#=> ProcessOrderTask 0 foobar123 abc123 ...
|
27
|
-
#=> SendEmailConfirmationTask 1 foobar123 def456 ...
|
28
|
-
#=> NotifyPartnerWarehousesTask 2 foobar123 ghi789 ...
|
29
|
-
```
|
30
|
-
|
31
|
-
---
|
32
|
-
|
33
|
-
- **Prev:** [Basics - Context](https://github.com/drexed/cmdx/blob/main/docs/basics/context.md)
|
34
|
-
- **Next:** [Interruptions - Halt](https://github.com/drexed/cmdx/blob/main/docs/interruptions/halt.md)
|
data/docs/batch.md
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# Batch
|
2
|
-
|
3
|
-
A CMDx::Batch is a task that calls other tasks in a linear fashion. The
|
4
|
-
context is passed down to each task, building on it knowledge with
|
5
|
-
each step. This is useful for composing multiple steps into one call.
|
6
|
-
|
7
|
-
> [!WARNING]
|
8
|
-
> Do **NOT** define a call method in this class. The batch class automatically
|
9
|
-
> defines the call logic.
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
class BatchProcessCheckout < CMDx::Batch
|
13
|
-
|
14
|
-
# Task level settings:
|
15
|
-
task_settings!(batch_halt: CMDx::Result::FAILED)
|
16
|
-
|
17
|
-
# Single declaration:
|
18
|
-
process FinalizeInvoiceTask
|
19
|
-
|
20
|
-
# Multiple declarations:
|
21
|
-
process SendConfirmationEmailTask, SendConfirmationTextTask
|
22
|
-
|
23
|
-
# With options (applies to all declared in that group):
|
24
|
-
process BatchNotifyPartnerWarehouses, batch_halt: [CMDx::Result::SKIPPED, CMDx::Result::FAILED]
|
25
|
-
process BatchNotifyUsaWarehouses, unless: proc { context.invoice.fulfilled_in_house? }
|
26
|
-
|
27
|
-
end
|
28
|
-
```
|
29
|
-
|
30
|
-
> [!IMPORTANT]
|
31
|
-
> Process steps are executed in the order they are declared (FIFO: first in, first out).
|
32
|
-
|
33
|
-
The `process` method support the following options:
|
34
|
-
|
35
|
-
| Option | Description |
|
36
|
-
| ------------- | ----------- |
|
37
|
-
| `:if` | Specifies a callable method, proc or string to determine if processing steps should occur. |
|
38
|
-
| `:unless` | Specifies a callable method, proc, or string to determine if processing steps should not occur. |
|
39
|
-
| `:batch_halt` | Sets which result statuses processing of further steps should be prevented. (default: `CMDx::Result::FAILED`) |
|
40
|
-
|
41
|
-
> [!NOTE]
|
42
|
-
> Batches stop execution on `failed` by default. This is due to the concept
|
43
|
-
> of `skipped` being a bypass mechanism, rather than a choke point in execution.
|
44
|
-
|
45
|
-
## Generator
|
46
|
-
|
47
|
-
Run `rails g cmdx:batch [NAME]` to create a batch task template file under `app/cmds`.
|
48
|
-
Tasks will inherit from `ApplicationBatch` if available or fall back to `CMDx::Batch`.
|
49
|
-
|
50
|
-
---
|
51
|
-
|
52
|
-
- **Prev:** [Hooks](https://github.com/drexed/cmdx/blob/main/docs/hooks.md)
|
53
|
-
- **Next:** [Logging](https://github.com/drexed/cmdx/blob/main/docs/logging.md)
|
data/docs/example.md
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
# Example
|
2
|
-
|
3
|
-
The following is a full example of task that is commonly implemented.
|
4
|
-
|
5
|
-
## Setup
|
6
|
-
|
7
|
-
```ruby
|
8
|
-
class ProcessOrderTask < CMDx::Task
|
9
|
-
|
10
|
-
required :order
|
11
|
-
required :billing_details, :shipping_details, from: :order
|
12
|
-
|
13
|
-
optional :package do
|
14
|
-
required :delivery_company, default: -> { Current.account.premium? ? "UPS" : "DHL" }
|
15
|
-
optional :weight, :volume, type: :float
|
16
|
-
end
|
17
|
-
|
18
|
-
after_execution :track_metrics
|
19
|
-
|
20
|
-
def call
|
21
|
-
if cart_abandoned?
|
22
|
-
skip!(reason: "Cart was abandoned due to 30 days of inactivity")
|
23
|
-
elsif cart_items_out_of_stock?
|
24
|
-
fail!(reason: "Items in the cart are out of stock", item: [123, 987])
|
25
|
-
else
|
26
|
-
charge_payment_method
|
27
|
-
ship_order_packages
|
28
|
-
send_confirmation_email
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def charge_payment_method
|
35
|
-
@charge_payment_method ||= ChargePaymentMethodTask.call(details: billing_details)
|
36
|
-
end
|
37
|
-
|
38
|
-
def ship_order_packages
|
39
|
-
@ship_order_packages ||= ShipOrderPackagesTask.call(details: shipping_details)
|
40
|
-
end
|
41
|
-
|
42
|
-
def send_confirmation_email
|
43
|
-
return if charge_payment_method.failed? || ship_order_packages.bad?
|
44
|
-
|
45
|
-
BatchSendConfirmationNotifications.call(context)
|
46
|
-
end
|
47
|
-
|
48
|
-
def track_metrics
|
49
|
-
if Rails.env.development?
|
50
|
-
logger.debug { "Sending metrics to collector" }
|
51
|
-
else
|
52
|
-
TrackMetricsTask.call(metric: :process_order, status: order.status)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
```
|
58
|
-
|
59
|
-
## Execution
|
60
|
-
|
61
|
-
```ruby
|
62
|
-
class OrdersController < ApplicationController
|
63
|
-
|
64
|
-
def create
|
65
|
-
task = ProcessOrderTask.call(order_params)
|
66
|
-
|
67
|
-
if task.success?
|
68
|
-
flash[:success] = "Order is on its way!"
|
69
|
-
redirect_to(my_orders_path)
|
70
|
-
else
|
71
|
-
flash[:error] = "Whoops something is wrong: #{task.metadata[:reason]}"
|
72
|
-
render(:new)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
```
|
78
|
-
|
79
|
-
---
|
80
|
-
|
81
|
-
- **Prev:** [Tips & Tricks](https://github.com/drexed/cmdx/blob/main/docs/tips_and_tricks.md)
|
82
|
-
- **Next:** [Getting Started](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md)
|
data/docs/hooks.md
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# Hooks
|
2
|
-
|
3
|
-
Hooks (callbacks) run logic at task transition points. Callable hooks have access
|
4
|
-
to all the same information as the `call` method.
|
5
|
-
|
6
|
-
> [!TIP]
|
7
|
-
> Hooks are inheritable which is handy for setting up global logic execution,
|
8
|
-
> eg: setting tracking markers, account plan checks, etc.
|
9
|
-
|
10
|
-
```ruby
|
11
|
-
class ProcessOrderTask < CMDx::Task
|
12
|
-
|
13
|
-
# Symbol or string declaration:
|
14
|
-
after_validation :verify_message_starting_chars
|
15
|
-
|
16
|
-
# Proc or lambda declaration:
|
17
|
-
on_complete -> { send_telemetry_data }
|
18
|
-
|
19
|
-
# Multiple declarations:
|
20
|
-
on_success :increment_success_task_counter, :scrub_secret_message_data
|
21
|
-
|
22
|
-
# With options (applies to all declared in that group):
|
23
|
-
on_failure :increment_failure_task_counter, if: :worth_keep_track?
|
24
|
-
|
25
|
-
def call
|
26
|
-
# Do work...
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
```
|
31
|
-
|
32
|
-
The hook methods support the following options:
|
33
|
-
|
34
|
-
| Option | Description |
|
35
|
-
| ------------- | ----------- |
|
36
|
-
| `:if` | Specifies a callable method, proc or string to determine if hook processing should occur. |
|
37
|
-
| `:unless` | Specifies a callable method, proc, or string to determine if hook processing should not occur. |
|
38
|
-
|
39
|
-
## Order
|
40
|
-
|
41
|
-
Hook types are executed in the following order:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
0. before_execution
|
45
|
-
1. on_executing
|
46
|
-
2. before_validation
|
47
|
-
3. after_validation
|
48
|
-
4. on_[complete, interrupted]
|
49
|
-
5. on_executed
|
50
|
-
6. on_[success, skipped, failed]
|
51
|
-
7. on_good
|
52
|
-
8. on_bad
|
53
|
-
9. after_execution
|
54
|
-
```
|
55
|
-
|
56
|
-
> [!IMPORTANT]
|
57
|
-
> Callable hooks are executed in the order they are declared (FIFO: first in, first out).
|
58
|
-
|
59
|
-
---
|
60
|
-
|
61
|
-
- **Prev:** [Outcomes - States](https://github.com/drexed/cmdx/blob/main/docs/outcomes/states.md)
|
62
|
-
- **Next:** [Batch](https://github.com/drexed/cmdx/blob/main/docs/batch.md)
|
data/lib/cmdx/batch.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CMDx
|
4
|
-
class Batch < Task
|
5
|
-
|
6
|
-
Group = Struct.new(:tasks, :options)
|
7
|
-
|
8
|
-
class << self
|
9
|
-
|
10
|
-
def batch_groups
|
11
|
-
@batch_groups ||= []
|
12
|
-
end
|
13
|
-
|
14
|
-
def process(*tasks, **options)
|
15
|
-
batch_groups << Group.new(
|
16
|
-
tasks.flatten.map do |task|
|
17
|
-
next task if task <= Task
|
18
|
-
|
19
|
-
raise ArgumentError, "must be a Batch or Task"
|
20
|
-
end,
|
21
|
-
options
|
22
|
-
)
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def call
|
28
|
-
self.class.batch_groups.each do |group|
|
29
|
-
next unless __cmdx_eval(group.options)
|
30
|
-
|
31
|
-
batch_halt = group.options[:batch_halt] || task_setting(:batch_halt)
|
32
|
-
|
33
|
-
group.tasks.each do |task|
|
34
|
-
task_result = task.call(context)
|
35
|
-
next unless Array(batch_halt).include?(task_result.status)
|
36
|
-
|
37
|
-
throw!(task_result)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|