cmdx 0.4.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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/rules/cursor-instructions.mdc +6 -0
  4. data/.rubocop.yml +16 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +42 -1
  7. data/README.md +72 -25
  8. data/docs/ai_prompts.md +309 -0
  9. data/docs/basics/call.md +225 -14
  10. data/docs/basics/chain.md +271 -0
  11. data/docs/basics/context.md +232 -33
  12. data/docs/basics/setup.md +76 -12
  13. data/docs/callbacks.md +273 -0
  14. data/docs/configuration.md +158 -28
  15. data/docs/getting_started.md +134 -22
  16. data/docs/interruptions/exceptions.md +189 -11
  17. data/docs/interruptions/faults.md +187 -44
  18. data/docs/interruptions/halt.md +179 -35
  19. data/docs/logging.md +194 -53
  20. data/docs/middlewares.md +735 -0
  21. data/docs/outcomes/result.md +296 -10
  22. data/docs/outcomes/states.md +212 -19
  23. data/docs/outcomes/statuses.md +284 -18
  24. data/docs/parameters/coercions.md +402 -29
  25. data/docs/parameters/defaults.md +249 -25
  26. data/docs/parameters/definitions.md +238 -72
  27. data/docs/parameters/namespacing.md +250 -27
  28. data/docs/parameters/validations.md +193 -168
  29. data/docs/testing.md +550 -0
  30. data/docs/tips_and_tricks.md +95 -43
  31. data/docs/workflows.md +319 -0
  32. data/lib/cmdx/.DS_Store +0 -0
  33. data/lib/cmdx/callback.rb +69 -0
  34. data/lib/cmdx/callback_registry.rb +106 -0
  35. data/lib/cmdx/chain.rb +190 -0
  36. data/lib/cmdx/chain_inspector.rb +149 -0
  37. data/lib/cmdx/chain_serializer.rb +175 -0
  38. data/lib/cmdx/coercions/array.rb +37 -0
  39. data/lib/cmdx/coercions/big_decimal.rb +33 -0
  40. data/lib/cmdx/coercions/boolean.rb +41 -1
  41. data/lib/cmdx/coercions/complex.rb +31 -0
  42. data/lib/cmdx/coercions/date.rb +39 -0
  43. data/lib/cmdx/coercions/date_time.rb +39 -0
  44. data/lib/cmdx/coercions/float.rb +31 -0
  45. data/lib/cmdx/coercions/hash.rb +42 -0
  46. data/lib/cmdx/coercions/integer.rb +32 -0
  47. data/lib/cmdx/coercions/rational.rb +31 -0
  48. data/lib/cmdx/coercions/string.rb +31 -0
  49. data/lib/cmdx/coercions/time.rb +39 -0
  50. data/lib/cmdx/coercions/virtual.rb +31 -0
  51. data/lib/cmdx/configuration.rb +217 -9
  52. data/lib/cmdx/context.rb +173 -2
  53. data/lib/cmdx/core_ext/hash.rb +72 -0
  54. data/lib/cmdx/core_ext/module.rb +94 -0
  55. data/lib/cmdx/core_ext/object.rb +105 -0
  56. data/lib/cmdx/correlator.rb +217 -0
  57. data/lib/cmdx/error.rb +210 -8
  58. data/lib/cmdx/errors.rb +256 -1
  59. data/lib/cmdx/fault.rb +177 -2
  60. data/lib/cmdx/faults.rb +158 -2
  61. data/lib/cmdx/immutator.rb +121 -2
  62. data/lib/cmdx/lazy_struct.rb +261 -18
  63. data/lib/cmdx/log_formatters/json.rb +46 -0
  64. data/lib/cmdx/log_formatters/key_value.rb +46 -0
  65. data/lib/cmdx/log_formatters/line.rb +54 -0
  66. data/lib/cmdx/log_formatters/logstash.rb +64 -0
  67. data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
  68. data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
  69. data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
  70. data/lib/cmdx/log_formatters/raw.rb +54 -0
  71. data/lib/cmdx/logger.rb +85 -0
  72. data/lib/cmdx/logger_ansi.rb +93 -7
  73. data/lib/cmdx/logger_serializer.rb +116 -0
  74. data/lib/cmdx/middleware.rb +74 -0
  75. data/lib/cmdx/middleware_registry.rb +106 -0
  76. data/lib/cmdx/middlewares/correlate.rb +266 -0
  77. data/lib/cmdx/middlewares/timeout.rb +232 -0
  78. data/lib/cmdx/parameter.rb +228 -1
  79. data/lib/cmdx/parameter_inspector.rb +61 -0
  80. data/lib/cmdx/parameter_registry.rb +125 -0
  81. data/lib/cmdx/parameter_serializer.rb +83 -0
  82. data/lib/cmdx/parameter_validator.rb +62 -0
  83. data/lib/cmdx/parameter_value.rb +109 -1
  84. data/lib/cmdx/parameters_inspector.rb +59 -0
  85. data/lib/cmdx/parameters_serializer.rb +102 -0
  86. data/lib/cmdx/railtie.rb +123 -3
  87. data/lib/cmdx/result.rb +399 -20
  88. data/lib/cmdx/result_ansi.rb +105 -9
  89. data/lib/cmdx/result_inspector.rb +76 -0
  90. data/lib/cmdx/result_logger.rb +90 -3
  91. data/lib/cmdx/result_serializer.rb +137 -0
  92. data/lib/cmdx/rspec/result_matchers.rb +917 -0
  93. data/lib/cmdx/rspec/task_matchers.rb +570 -0
  94. data/lib/cmdx/task.rb +409 -34
  95. data/lib/cmdx/task_serializer.rb +74 -2
  96. data/lib/cmdx/utils/ansi_color.rb +95 -0
  97. data/lib/cmdx/utils/log_timestamp.rb +48 -0
  98. data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
  99. data/lib/cmdx/utils/name_affix.rb +78 -0
  100. data/lib/cmdx/validators/custom.rb +82 -0
  101. data/lib/cmdx/validators/exclusion.rb +94 -0
  102. data/lib/cmdx/validators/format.rb +102 -8
  103. data/lib/cmdx/validators/inclusion.rb +104 -0
  104. data/lib/cmdx/validators/length.rb +128 -0
  105. data/lib/cmdx/validators/numeric.rb +128 -0
  106. data/lib/cmdx/validators/presence.rb +93 -7
  107. data/lib/cmdx/version.rb +7 -1
  108. data/lib/cmdx/workflow.rb +394 -0
  109. data/lib/cmdx.rb +25 -64
  110. data/lib/generators/cmdx/install_generator.rb +37 -1
  111. data/lib/generators/cmdx/task_generator.rb +69 -1
  112. data/lib/generators/cmdx/templates/install.rb +8 -12
  113. data/lib/generators/cmdx/workflow_generator.rb +109 -0
  114. metadata +54 -15
  115. data/docs/basics/run.md +0 -34
  116. data/docs/batch.md +0 -53
  117. data/docs/example.md +0 -82
  118. data/docs/hooks.md +0 -59
  119. data/lib/cmdx/batch.rb +0 -43
  120. data/lib/cmdx/parameters.rb +0 -34
  121. data/lib/cmdx/run.rb +0 -38
  122. data/lib/cmdx/run_inspector.rb +0 -26
  123. data/lib/cmdx/run_serializer.rb +0 -16
  124. data/lib/cmdx/task_hook.rb +0 -18
  125. data/lib/generators/cmdx/batch_generator.rb +0 -30
  126. /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 "Generates a task with the given NAME (if one does not exist)."
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
- # Define which statuses a bang `call!` will halt and raise a fault.
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
- # Enable task timeouts to prevent call execution beyond a defined threshold.
9
- config.task_timeout = nil
7
+ # Global timeout for individual tasks (nil = no timeout)
10
8
 
11
- # Define which statuses a batch task will halt execution from proceeding to the next step.
12
- # By default skipped tasks are treated as a NOOP so processing is continued.
13
- # This option can accept an array of statuses.
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
- # Enable batch timeouts to prevent call execution beyond a defined threshold.
17
- # TIP: remember to account for all defined tasks when setting this value
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
- # A list of available log formatter can be found at:
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.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: 2025-03-17 00:00:00.000000000 Z
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/batch.md
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/batch.rb
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/run.rb
244
- - lib/cmdx/run_inspector.rb
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/generators/cmdx/batch_generator.rb
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.5
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,59 +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
- 1. before_execution
45
- 2. on_executing
46
- 3. before_validation
47
- 4. after_validation
48
- 5. on_[success, skipped, failed]
49
- 6. on_[complete, interrupted]
50
- 7. after_execution
51
- ```
52
-
53
- > [!IMPORTANT]
54
- > Callable hooks are executed in the order they are declared (FIFO: first in, first out).
55
-
56
- ---
57
-
58
- - **Prev:** [Outcomes - States](https://github.com/drexed/cmdx/blob/main/docs/outcomes/states.md)
59
- - **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