cmdx 1.1.0 → 1.1.1
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/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +13 -12
- data/.cursor/prompts/yardoc.md +11 -6
- data/CHANGELOG.md +13 -2
- data/README.md +1 -0
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +124 -58
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +390 -94
- data/docs/configuration.md +181 -65
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +150 -125
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +181 -118
- data/docs/middlewares.md +150 -377
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +232 -281
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +260 -133
- data/docs/testing.md +191 -197
- data/docs/workflows.md +143 -98
- data/lib/cmdx/callback.rb +23 -19
- data/lib/cmdx/callback_registry.rb +1 -3
- data/lib/cmdx/chain_inspector.rb +23 -23
- data/lib/cmdx/chain_serializer.rb +38 -19
- data/lib/cmdx/coercion.rb +20 -12
- data/lib/cmdx/coercion_registry.rb +51 -32
- data/lib/cmdx/configuration.rb +84 -31
- data/lib/cmdx/context.rb +32 -21
- data/lib/cmdx/core_ext/hash.rb +13 -13
- data/lib/cmdx/core_ext/module.rb +1 -1
- data/lib/cmdx/core_ext/object.rb +12 -12
- data/lib/cmdx/correlator.rb +60 -39
- data/lib/cmdx/errors.rb +105 -131
- data/lib/cmdx/fault.rb +66 -45
- data/lib/cmdx/immutator.rb +20 -21
- data/lib/cmdx/lazy_struct.rb +78 -70
- data/lib/cmdx/log_formatters/json.rb +1 -1
- data/lib/cmdx/log_formatters/key_value.rb +1 -1
- data/lib/cmdx/log_formatters/line.rb +1 -1
- data/lib/cmdx/log_formatters/logstash.rb +1 -1
- data/lib/cmdx/log_formatters/pretty_json.rb +1 -1
- data/lib/cmdx/log_formatters/pretty_key_value.rb +1 -1
- data/lib/cmdx/log_formatters/pretty_line.rb +1 -1
- data/lib/cmdx/log_formatters/raw.rb +2 -2
- data/lib/cmdx/logger.rb +19 -14
- data/lib/cmdx/logger_ansi.rb +33 -17
- data/lib/cmdx/logger_serializer.rb +85 -24
- data/lib/cmdx/middleware.rb +39 -21
- data/lib/cmdx/middleware_registry.rb +4 -3
- data/lib/cmdx/parameter.rb +151 -89
- data/lib/cmdx/parameter_inspector.rb +34 -21
- data/lib/cmdx/parameter_registry.rb +36 -30
- data/lib/cmdx/parameter_serializer.rb +21 -14
- data/lib/cmdx/result.rb +136 -135
- data/lib/cmdx/result_ansi.rb +31 -17
- data/lib/cmdx/result_inspector.rb +32 -27
- data/lib/cmdx/result_logger.rb +23 -14
- data/lib/cmdx/result_serializer.rb +65 -27
- data/lib/cmdx/task.rb +234 -113
- data/lib/cmdx/task_deprecator.rb +22 -25
- data/lib/cmdx/task_processor.rb +89 -88
- data/lib/cmdx/task_serializer.rb +27 -14
- data/lib/cmdx/utils/monotonic_runtime.rb +2 -4
- data/lib/cmdx/validator.rb +25 -16
- data/lib/cmdx/validator_registry.rb +53 -31
- data/lib/cmdx/validators/exclusion.rb +1 -1
- data/lib/cmdx/validators/format.rb +2 -2
- data/lib/cmdx/validators/inclusion.rb +2 -2
- data/lib/cmdx/validators/length.rb +2 -2
- data/lib/cmdx/validators/numeric.rb +3 -3
- data/lib/cmdx/validators/presence.rb +2 -2
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/workflow.rb +54 -33
- data/lib/generators/cmdx/task_generator.rb +6 -6
- data/lib/generators/cmdx/workflow_generator.rb +6 -6
- metadata +3 -1
@@ -40,7 +40,7 @@ module CMDx
|
|
40
40
|
#
|
41
41
|
# @example Range validation
|
42
42
|
# Validators::Numeric.call(5, numeric: { within: 1..10 })
|
43
|
-
#
|
43
|
+
# #=> nil (no error raised)
|
44
44
|
#
|
45
45
|
# @example Range exclusion
|
46
46
|
# Validators::Numeric.call(5, numeric: { not_within: 1..10 })
|
@@ -48,7 +48,7 @@ module CMDx
|
|
48
48
|
#
|
49
49
|
# @example Min/max validation
|
50
50
|
# Validators::Numeric.call(15, numeric: { min: 10, max: 20 })
|
51
|
-
#
|
51
|
+
# #=> nil (no error raised)
|
52
52
|
#
|
53
53
|
# @example Minimum value validation
|
54
54
|
# Validators::Numeric.call(5, numeric: { min: 10 })
|
@@ -56,7 +56,7 @@ module CMDx
|
|
56
56
|
#
|
57
57
|
# @example Exact value validation
|
58
58
|
# Validators::Numeric.call(42, numeric: { is: 42 })
|
59
|
-
#
|
59
|
+
# #=> nil (no error raised)
|
60
60
|
#
|
61
61
|
# @example Custom error message
|
62
62
|
# Validators::Numeric.call(5, numeric: { min: 10, message: "Age must be at least %{min}" })
|
@@ -23,7 +23,7 @@ module CMDx
|
|
23
23
|
#
|
24
24
|
# @example Validating a non-empty string
|
25
25
|
# Validators::Presence.call("hello", presence: {})
|
26
|
-
#
|
26
|
+
# #=> nil (no error raised)
|
27
27
|
#
|
28
28
|
# @example Validating an empty string
|
29
29
|
# Validators::Presence.call("", presence: {})
|
@@ -35,7 +35,7 @@ module CMDx
|
|
35
35
|
#
|
36
36
|
# @example Validating a non-empty array
|
37
37
|
# Validators::Presence.call([1, 2, 3], presence: {})
|
38
|
-
#
|
38
|
+
# #=> nil (no error raised)
|
39
39
|
#
|
40
40
|
# @example Validating an empty array
|
41
41
|
# Validators::Presence.call([], presence: {})
|
data/lib/cmdx/version.rb
CHANGED
data/lib/cmdx/workflow.rb
CHANGED
@@ -1,63 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Sequential task execution orchestration system for CMDx framework.
|
5
5
|
#
|
6
|
-
# Workflow provides
|
7
|
-
# execution
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# Workflow provides declarative composition of multiple tasks into linear pipelines
|
7
|
+
# with conditional execution, context propagation, and configurable halt behavior.
|
8
|
+
# Workflows inherit from Task, gaining all task capabilities including callbacks,
|
9
|
+
# parameter validation, result tracking, and configuration while coordinating
|
10
|
+
# other tasks rather than implementing business logic directly.
|
10
11
|
class Workflow < Task
|
11
12
|
|
12
|
-
#
|
13
|
+
# Data structure containing a group of tasks and their execution options.
|
13
14
|
#
|
14
15
|
# @!attribute [r] tasks
|
15
|
-
# @return [Array<
|
16
|
+
# @return [Array<Class>] array of Task or Workflow classes to execute
|
16
17
|
# @!attribute [r] options
|
17
|
-
# @return [Hash]
|
18
|
+
# @return [Hash] execution options including conditional and halt configuration
|
18
19
|
Group = Struct.new(:tasks, :options)
|
19
20
|
|
20
21
|
class << self
|
21
22
|
|
22
|
-
# Returns the
|
23
|
+
# Returns the array of workflow groups defined for this workflow class.
|
23
24
|
#
|
24
|
-
#
|
25
|
+
# Each group contains tasks and their execution options. Groups are processed
|
26
|
+
# sequentially during workflow execution, with each group's tasks executing
|
27
|
+
# in order unless halted by a result status.
|
28
|
+
#
|
29
|
+
# @return [Array<Group>] array of workflow groups containing tasks and options
|
25
30
|
#
|
26
31
|
# @example Access workflow groups
|
27
|
-
# MyWorkflow
|
32
|
+
# class MyWorkflow < CMDx::Workflow
|
33
|
+
# process TaskA, TaskB
|
34
|
+
# process TaskC, if: :condition_met?
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# MyWorkflow.workflow_groups.size #=> 2
|
38
|
+
# MyWorkflow.workflow_groups.first.tasks #=> [TaskA, TaskB]
|
28
39
|
def workflow_groups
|
29
40
|
@workflow_groups ||= []
|
30
41
|
end
|
31
42
|
|
32
|
-
#
|
43
|
+
# Declares a group of tasks to execute sequentially with optional conditions.
|
44
|
+
#
|
45
|
+
# Tasks are executed in the order specified, with shared context propagated
|
46
|
+
# between executions. Groups support conditional execution and configurable
|
47
|
+
# halt behavior to control workflow flow based on task results.
|
33
48
|
#
|
34
|
-
# @param tasks [Array<
|
35
|
-
# @param options [Hash] execution options
|
36
|
-
#
|
37
|
-
# @option options [Proc] :if
|
38
|
-
# @option options [Proc] :unless
|
49
|
+
# @param tasks [Array<Class>] Task or Workflow classes to execute in sequence
|
50
|
+
# @param options [Hash] execution configuration options
|
51
|
+
#
|
52
|
+
# @option options [Proc, Symbol, String] :if condition that must be truthy for group execution
|
53
|
+
# @option options [Proc, Symbol, String] :unless condition that must be falsy for group execution
|
54
|
+
# @option options [String, Array<String>] :workflow_halt result statuses that halt workflow execution
|
39
55
|
#
|
40
56
|
# @return [void]
|
41
57
|
#
|
42
|
-
# @raise [TypeError]
|
58
|
+
# @raise [TypeError] when tasks contain objects that are not Task or Workflow classes
|
43
59
|
#
|
44
|
-
# @example
|
45
|
-
#
|
60
|
+
# @example Declare sequential tasks
|
61
|
+
# class UserRegistrationWorkflow < CMDx::Workflow
|
62
|
+
# process CreateUserTask, SendWelcomeEmailTask
|
63
|
+
# end
|
46
64
|
#
|
47
|
-
# @example
|
48
|
-
#
|
65
|
+
# @example Declare conditional task group
|
66
|
+
# class OrderProcessingWorkflow < CMDx::Workflow
|
67
|
+
# process ValidateOrderTask
|
68
|
+
# process ChargePaymentTask, if: ->(workflow) { workflow.context.payment_required? }
|
69
|
+
# process ShipOrderTask, unless: :digital_product?
|
70
|
+
# process NotifyAdminTask, if: proc { context.admin.active? }
|
71
|
+
# end
|
49
72
|
#
|
50
|
-
# @example
|
51
|
-
#
|
73
|
+
# @example Configure halt behavior per group
|
74
|
+
# class DataProcessingWorkflow < CMDx::Workflow
|
75
|
+
# process LoadDataTask, ValidateDataTask, workflow_halt: %w[failed skipped]
|
76
|
+
# process OptionalCleanupTask, workflow_halt: []
|
77
|
+
# end
|
52
78
|
def process(*tasks, **options)
|
53
79
|
workflow_groups << Group.new(
|
54
80
|
tasks.flatten.map do |task|
|
55
|
-
|
56
|
-
raise TypeError,
|
57
|
-
"must be a Task or Workflow"
|
58
|
-
end
|
81
|
+
next task if task.is_a?(Class) && (task <= Task)
|
59
82
|
|
60
|
-
|
83
|
+
raise TypeError, "must be a Task or Workflow"
|
61
84
|
end,
|
62
85
|
options
|
63
86
|
)
|
@@ -65,8 +88,6 @@ module CMDx
|
|
65
88
|
|
66
89
|
end
|
67
90
|
|
68
|
-
# Executes all workflow groups in sequence.
|
69
|
-
#
|
70
91
|
# Each group is evaluated for conditional execution, and if the group should
|
71
92
|
# execute, all tasks in the group are called in sequence. If any task returns
|
72
93
|
# a status that matches the workflow halt criteria, execution is halted and
|
@@ -76,9 +97,9 @@ module CMDx
|
|
76
97
|
#
|
77
98
|
# @raise [Fault] if a task fails and its status matches the workflow halt criteria
|
78
99
|
#
|
79
|
-
# @example Execute workflow
|
100
|
+
# @example Execute workflow
|
80
101
|
# workflow = MyWorkflow.new(user_id: 123)
|
81
|
-
# workflow.call
|
102
|
+
# workflow.call
|
82
103
|
def call
|
83
104
|
self.class.workflow_groups.each do |group|
|
84
105
|
next unless cmdx_eval(group.options)
|
@@ -24,11 +24,11 @@ module Cmdx
|
|
24
24
|
#
|
25
25
|
# @example Generate a user task
|
26
26
|
# rails generate cmdx:task user
|
27
|
-
#
|
27
|
+
# #=> Creates app/cmds/user_task.rb
|
28
28
|
#
|
29
29
|
# @example Generate a nested task
|
30
30
|
# rails generate cmdx:task admin/users
|
31
|
-
#
|
31
|
+
# #=> Creates app/cmds/admin/users_task.rb
|
32
32
|
def copy_files
|
33
33
|
name = file_name.sub(/_?task$/i, "")
|
34
34
|
path = File.join("app/cmds", class_path, "#{name}_task.rb")
|
@@ -47,11 +47,11 @@ module Cmdx
|
|
47
47
|
#
|
48
48
|
# @example Class name without suffix
|
49
49
|
# # Given name: "User"
|
50
|
-
# class_name
|
50
|
+
# class_name #=> "UserTask"
|
51
51
|
#
|
52
52
|
# @example Class name with suffix
|
53
53
|
# # Given name: "UserTask"
|
54
|
-
# class_name
|
54
|
+
# class_name #=> "UserTask"
|
55
55
|
def class_name
|
56
56
|
@class_name ||= super.end_with?("Task") ? super : "#{super}Task"
|
57
57
|
end
|
@@ -68,10 +68,10 @@ module Cmdx
|
|
68
68
|
# @raise [StandardError] if neither ApplicationTask nor CMDx::Task are available
|
69
69
|
#
|
70
70
|
# @example With ApplicationTask defined
|
71
|
-
# parent_class_name
|
71
|
+
# parent_class_name #=> ApplicationTask
|
72
72
|
#
|
73
73
|
# @example Without ApplicationTask
|
74
|
-
# parent_class_name
|
74
|
+
# parent_class_name #=> CMDx::Task
|
75
75
|
def parent_class_name
|
76
76
|
ApplicationTask
|
77
77
|
rescue StandardError
|
@@ -26,11 +26,11 @@ module Cmdx
|
|
26
26
|
#
|
27
27
|
# @example Generate a user workflow
|
28
28
|
# rails generate cmdx:workflow user
|
29
|
-
#
|
29
|
+
# #=> Creates app/cmds/user_workflow.rb
|
30
30
|
#
|
31
31
|
# @example Generate a nested workflow
|
32
32
|
# rails generate cmdx:workflow admin/users
|
33
|
-
#
|
33
|
+
# #=> Creates app/cmds/admin/users_workflow.rb
|
34
34
|
def copy_files
|
35
35
|
name = file_name.sub(/_?workflow$/i, "")
|
36
36
|
path = File.join("app/cmds", class_path, "#{name}_workflow.rb")
|
@@ -49,11 +49,11 @@ module Cmdx
|
|
49
49
|
#
|
50
50
|
# @example Class name without suffix
|
51
51
|
# # Given name: "User"
|
52
|
-
# class_name
|
52
|
+
# class_name #=> "UserWorkflow"
|
53
53
|
#
|
54
54
|
# @example Class name with suffix
|
55
55
|
# # Given name: "UserWorkflow"
|
56
|
-
# class_name
|
56
|
+
# class_name #=> "UserWorkflow"
|
57
57
|
def class_name
|
58
58
|
@class_name ||= super.end_with?("Workflow") ? super : "#{super}Workflow"
|
59
59
|
end
|
@@ -70,10 +70,10 @@ module Cmdx
|
|
70
70
|
# @raise [StandardError] if neither ApplicationWorkflow nor CMDx::Workflow are available
|
71
71
|
#
|
72
72
|
# @example With ApplicationWorkflow defined
|
73
|
-
# parent_class_name
|
73
|
+
# parent_class_name #=> ApplicationWorkflow
|
74
74
|
#
|
75
75
|
# @example Without ApplicationWorkflow
|
76
|
-
# parent_class_name
|
76
|
+
# parent_class_name #=> CMDx::Workflow
|
77
77
|
def parent_class_name
|
78
78
|
ApplicationWorkflow
|
79
79
|
rescue StandardError
|
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.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
@@ -186,6 +186,7 @@ extensions: []
|
|
186
186
|
extra_rdoc_files: []
|
187
187
|
files:
|
188
188
|
- ".DS_Store"
|
189
|
+
- ".cursor/prompts/docs.md"
|
189
190
|
- ".cursor/prompts/rspec.md"
|
190
191
|
- ".cursor/prompts/yardoc.md"
|
191
192
|
- ".cursor/rules/cursor-instructions.mdc"
|
@@ -204,6 +205,7 @@ files:
|
|
204
205
|
- docs/basics/setup.md
|
205
206
|
- docs/callbacks.md
|
206
207
|
- docs/configuration.md
|
208
|
+
- docs/deprecation.md
|
207
209
|
- docs/getting_started.md
|
208
210
|
- docs/internationalization.md
|
209
211
|
- docs/interruptions/exceptions.md
|