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.
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 +31 -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 +203 -31
  23. data/docs/outcomes/statuses.md +275 -30
  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 +367 -25
  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 +405 -37
  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 -62
  119. data/lib/cmdx/batch.rb +0 -43
  120. data/lib/cmdx/parameters.rb +0 -35
  121. data/lib/cmdx/run.rb +0 -39
  122. data/lib/cmdx/run_inspector.rb +0 -26
  123. data/lib/cmdx/run_serializer.rb +0 -20
  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
@@ -2,10 +2,52 @@
2
2
 
3
3
  module CMDx
4
4
  module CoreExt
5
+ # Extensions to Object that provide CMDx-specific utility methods.
6
+ #
7
+ # ObjectExtensions adds safe method calling, conditional evaluation,
8
+ # and value yielding capabilities to all Ruby objects. These methods
9
+ # are prefixed with `__cmdx_` to avoid conflicts with existing methods.
10
+ #
11
+ # @example Safe method calling
12
+ # object.__cmdx_try(:some_method) # Returns nil if method doesn't exist
13
+ # object.__cmdx_try(proc { expensive_calculation }) # Calls proc safely
14
+ #
15
+ # @example Conditional evaluation
16
+ # object.__cmdx_eval(if: :valid?) # True if object.valid? is true
17
+ # object.__cmdx_eval(unless: :empty?) # True unless object.empty? is true
18
+ # object.__cmdx_eval(if: :valid?, unless: :processed?) # Combined conditions
19
+ #
20
+ # @example Value yielding
21
+ # object.__cmdx_yield(:name) # Returns object.name if method exists, otherwise :name
22
+ # object.__cmdx_yield(-> { compute }) # Executes lambda and returns result
23
+ #
24
+ # @see Task Tasks that use these object extensions
25
+ # @see Parameter Parameters that leverage object extensions
5
26
  module ObjectExtensions
6
27
 
28
+ # Store original respond_to? method before aliasing
7
29
  alias __cmdx_respond_to? respond_to?
8
30
 
31
+ # Safely attempt to call a method or execute a proc on an object.
32
+ #
33
+ # This method provides safe method calling with fallback behavior.
34
+ # It handles method calls, proc execution, and hash key access gracefully.
35
+ #
36
+ # @param key [Symbol, String, Proc] method name or callable to attempt
37
+ # @param args [Array] arguments to pass to the method/proc (passed via splat)
38
+ # @return [Object, nil] result of method call, proc execution, or nil if not possible
39
+ #
40
+ # @example Method calling
41
+ # user.__cmdx_try(:name) # => "John" or nil
42
+ # user.__cmdx_try(:age, 25) # => calls user.age(25) or nil
43
+ #
44
+ # @example Proc execution
45
+ # user.__cmdx_try(-> { expensive_calc }) # => executes lambda
46
+ # user.__cmdx_try(proc { |x| x * 2 }, 5) # => 10
47
+ #
48
+ # @example Hash access
49
+ # hash = {name: "John"}
50
+ # hash.__cmdx_try(:name) # => "John"
9
51
  def __cmdx_try(key, ...)
10
52
  if key.is_a?(Proc)
11
53
  return instance_eval(&key) unless is_a?(Module) || key.inspect.include?("(lambda)")
@@ -18,6 +60,27 @@ module CMDx
18
60
  end
19
61
  end
20
62
 
63
+ # Evaluate conditional options for execution control.
64
+ #
65
+ # This method evaluates :if and :unless conditions to determine
66
+ # whether something should proceed. Used extensively in callbacks
67
+ # and conditional parameter processing.
68
+ #
69
+ # @param options [Hash] conditional options
70
+ # @option options [Symbol, Proc] :if condition that must be true
71
+ # @option options [Symbol, Proc] :unless condition that must be false
72
+ # @option options [Boolean] :default default value if no conditions (default: true)
73
+ # @return [Boolean] true if conditions are met
74
+ #
75
+ # @example Simple conditions
76
+ # user.__cmdx_eval(if: :admin?) # => true if user.admin? is true
77
+ # user.__cmdx_eval(unless: :guest?) # => true unless user.guest? is true
78
+ #
79
+ # @example Combined conditions
80
+ # user.__cmdx_eval(if: :active?, unless: :banned?) # => active AND not banned
81
+ #
82
+ # @example With procs
83
+ # user.__cmdx_eval(if: -> { Time.now.monday? }) # => true if today is Monday
21
84
  def __cmdx_eval(options = {})
22
85
  if options[:if] && options[:unless]
23
86
  __cmdx_try(options[:if]) && !__cmdx_try(options[:unless])
@@ -30,6 +93,27 @@ module CMDx
30
93
  end
31
94
  end
32
95
 
96
+ # Yield a value by attempting to call it as a method or executing it.
97
+ #
98
+ # This method provides intelligent value resolution - if the key is
99
+ # a method name and the object responds to it, call the method.
100
+ # Otherwise, try to execute it as a proc or return the value as-is.
101
+ #
102
+ # @param key [Object] value to yield, method name, or callable
103
+ # @param args [Array] arguments to pass if calling method/proc (passed via splat)
104
+ # @return [Object] yielded value
105
+ #
106
+ # @example Method yielding
107
+ # user.__cmdx_yield(:name) # => calls user.name if method exists, otherwise returns :name
108
+ # user.__cmdx_yield("email") # => calls user.email if method exists, otherwise returns "email"
109
+ #
110
+ # @example Proc yielding
111
+ # user.__cmdx_yield(-> { timestamp }) # => executes lambda
112
+ # hash.__cmdx_yield({key: "value"}) # => tries hash access
113
+ #
114
+ # @example Direct values
115
+ # user.__cmdx_yield(42) # => 42
116
+ # user.__cmdx_yield("literal") # => "literal"
33
117
  def __cmdx_yield(key, ...)
34
118
  if key.is_a?(Symbol) || key.is_a?(String)
35
119
  return key unless respond_to?(key, true)
@@ -42,6 +126,26 @@ module CMDx
42
126
  end
43
127
  end
44
128
 
129
+ # Call an object if it responds to call, otherwise return itself.
130
+ #
131
+ # This method provides safe callable execution - if the object
132
+ # can be called (like a proc or lambda), call it with the given
133
+ # arguments. Otherwise, return the object unchanged.
134
+ #
135
+ # @param args [Array] arguments to pass to call method (passed via splat)
136
+ # @return [Object] result of calling or the object itself
137
+ #
138
+ # @example Callable objects
139
+ # proc = -> { "Hello" }
140
+ # proc.__cmdx_call # => "Hello"
141
+ #
142
+ # @example Non-callable objects
143
+ # string = "Hello"
144
+ # string.__cmdx_call # => "Hello"
145
+ #
146
+ # @example With arguments
147
+ # adder = ->(a, b) { a + b }
148
+ # adder.__cmdx_call(2, 3) # => 5
45
149
  def __cmdx_call(...)
46
150
  return self unless respond_to?(:call)
47
151
 
@@ -52,4 +156,5 @@ module CMDx
52
156
  end
53
157
  end
54
158
 
159
+ # Extend all objects with CMDx utility methods
55
160
  Object.include(CMDx::CoreExt::ObjectExtensions)
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMDx
4
+ ##
5
+ # Thread-safe correlation ID management for tracking related operations across request boundaries.
6
+ #
7
+ # The Correlator provides a simple, thread-local storage mechanism for managing correlation
8
+ # identifiers throughout the execution lifecycle. It enables tracing related operations
9
+ # across different tasks, workflows, and service boundaries within the same execution context.
10
+ #
11
+ # Correlation IDs are automatically used by CMDx runs when available, providing seamless
12
+ # request tracking without requiring explicit parameter passing between tasks.
13
+ #
14
+ # ## Thread Safety
15
+ #
16
+ # All correlation operations are thread-local, ensuring that different threads maintain
17
+ # separate correlation contexts without interference. This is essential for concurrent
18
+ # request processing in multi-threaded applications.
19
+ #
20
+ # ## Integration with CMDx Runs
21
+ #
22
+ # When a new CMDx::Chain is created, it automatically uses the current thread's correlation
23
+ # ID as its chain identifier if available, falling back to UUID generation if none exists.
24
+ #
25
+ # @example Basic correlation usage
26
+ # CMDx::Correlator.id = "req-12345"
27
+ # CMDx::Correlator.id # => "req-12345"
28
+ #
29
+ # # Chain automatically inherits correlation ID
30
+ # result = ProcessOrderTask.call(order_id: 123)
31
+ # result.chain.id # => "req-12345"
32
+ #
33
+ # @example Block-based correlation context
34
+ # CMDx::Correlator.use("workflow-operation-456") do
35
+ # # All tasks within this block share the same correlation
36
+ # ProcessOrderTask.call(order_id: 123)
37
+ # SendEmailTask.call(user_id: 456)
38
+ # end
39
+ # # Correlation context is automatically restored
40
+ #
41
+ # @example Nested correlation contexts
42
+ # CMDx::Correlator.use("parent-operation") do
43
+ # CMDx::Correlator.use("child-operation") do
44
+ # ProcessOrderTask.call(order_id: 123)
45
+ # # Uses "child-operation" as correlation ID
46
+ # end
47
+ # # Restored to "parent-operation"
48
+ # SendEmailTask.call(user_id: 456)
49
+ # end
50
+ #
51
+ # @example Manual correlation management
52
+ # # Set correlation ID for current thread
53
+ # CMDx::Correlator.id = "manual-correlation"
54
+ #
55
+ # # Check current correlation
56
+ # current_id = CMDx::Correlator.id
57
+ #
58
+ # # Clear correlation when done
59
+ # CMDx::Correlator.clear
60
+ #
61
+ # @example Middleware integration pattern
62
+ # class CorrelationMiddleware
63
+ # def call(env)
64
+ # correlation_id = env['HTTP_X_CORRELATION_ID'] || CMDx::Correlator.generate
65
+ #
66
+ # CMDx::Correlator.use(correlation_id) do
67
+ # @app.call(env)
68
+ # end
69
+ # end
70
+ # end
71
+ #
72
+ # @see CMDx::Chain Chain execution context that inherits correlation IDs
73
+ # @since 1.0.0
74
+ module Correlator
75
+
76
+ ##
77
+ # Thread-local storage key for correlation identifiers.
78
+ #
79
+ # Uses a Symbol key to avoid potential conflicts with other thread-local
80
+ # variables and to ensure consistent access across the correlator methods.
81
+ #
82
+ # @return [Symbol] the thread-local storage key
83
+ THREAD_KEY = :cmdx_correlation_id
84
+
85
+ module_function
86
+
87
+ ##
88
+ # Generates a new UUID suitable for use as a correlation identifier.
89
+ #
90
+ # Creates a RFC 4122 compliant UUID using SecureRandom, providing a globally
91
+ # unique identifier suitable for distributed request tracing.
92
+ #
93
+ # @return [String] a new UUID string in standard format (e.g., "123e4567-e89b-12d3-a456-426614174000")
94
+ #
95
+ # @example Generating correlation IDs
96
+ # id1 = CMDx::Correlator.generate # => "018c2b95-b764-7615-a924-cc5b910ed1e5"
97
+ # id2 = CMDx::Correlator.generate # => "018c2b95-b765-7123-b456-dd7c920fe3a8"
98
+ # id1 != id2 # => true
99
+ def generate
100
+ SecureRandom.uuid
101
+ end
102
+
103
+ ##
104
+ # Retrieves the current thread's correlation identifier.
105
+ #
106
+ # Returns the correlation ID that was previously set for the current thread,
107
+ # or nil if no correlation ID has been established. This method is thread-safe
108
+ # and will not interfere with correlation IDs set in other threads.
109
+ #
110
+ # @return [String, nil] the current correlation ID or nil if none is set
111
+ #
112
+ # @example Checking current correlation
113
+ # CMDx::Correlator.id # => nil (when none is set)
114
+ #
115
+ # CMDx::Correlator.id = "test-correlation"
116
+ # CMDx::Correlator.id # => "test-correlation"
117
+ def id
118
+ Thread.current[THREAD_KEY]
119
+ end
120
+
121
+ ##
122
+ # Sets the correlation identifier for the current thread.
123
+ #
124
+ # Assigns a correlation ID to the current thread's local storage, making it
125
+ # available for subsequent operations within the same thread context. The
126
+ # correlation ID will persist until explicitly changed or cleared.
127
+ #
128
+ # @param value [String, #to_s] the correlation identifier to set
129
+ # @return [String] the assigned correlation identifier
130
+ #
131
+ # @example Setting correlation ID
132
+ # CMDx::Correlator.id = "user-request-123"
133
+ # CMDx::Correlator.id # => "user-request-123"
134
+ #
135
+ # @example Type coercion
136
+ # CMDx::Correlator.id = 12345
137
+ # CMDx::Correlator.id # => 12345 (stored as provided)
138
+ def id=(value)
139
+ Thread.current[THREAD_KEY] = value
140
+ end
141
+
142
+ ##
143
+ # Clears the correlation identifier for the current thread.
144
+ #
145
+ # Removes the correlation ID from the current thread's local storage,
146
+ # effectively resetting the correlation context. Subsequent calls to
147
+ # {#id} will return nil until a new correlation ID is set.
148
+ #
149
+ # @return [nil] always returns nil
150
+ #
151
+ # @example Clearing correlation
152
+ # CMDx::Correlator.id = "temporary-correlation"
153
+ # CMDx::Correlator.id # => "temporary-correlation"
154
+ #
155
+ # CMDx::Correlator.clear
156
+ # CMDx::Correlator.id # => nil
157
+ def clear
158
+ Thread.current[THREAD_KEY] = nil
159
+ end
160
+
161
+ ##
162
+ # Temporarily sets a correlation identifier for the duration of a block.
163
+ #
164
+ # Establishes a correlation context for the given block, automatically
165
+ # restoring the previous correlation ID when the block completes. This
166
+ # method is exception-safe and will restore the original context even
167
+ # if the block raises an error.
168
+ #
169
+ # This is the preferred method for managing correlation contexts as it
170
+ # ensures proper cleanup and supports nested correlation scopes.
171
+ #
172
+ # @param value [String, #to_s] the correlation identifier to use during block execution
173
+ # @yieldreturn [Object] the return value of the block
174
+ # @return [Object] the return value of the executed block
175
+ #
176
+ # @example Basic usage
177
+ # result = CMDx::Correlator.use("operation-123") do
178
+ # ProcessOrderTask.call(order_id: 456)
179
+ # end
180
+ # # Correlation context is automatically restored
181
+ #
182
+ # @example Nested contexts
183
+ # CMDx::Correlator.use("parent") do
184
+ # CMDx::Correlator.id # => "parent"
185
+ #
186
+ # CMDx::Correlator.use("child") do
187
+ # CMDx::Correlator.id # => "child"
188
+ # end
189
+ #
190
+ # CMDx::Correlator.id # => "parent" (restored)
191
+ # end
192
+ #
193
+ # @example Exception safety
194
+ # CMDx::Correlator.id = "original"
195
+ #
196
+ # begin
197
+ # CMDx::Correlator.use("temporary") do
198
+ # raise StandardError, "something went wrong"
199
+ # end
200
+ # rescue StandardError
201
+ # CMDx::Correlator.id # => "original" (still restored)
202
+ # end
203
+ def use(value)
204
+ unless value.is_a?(String) || value.is_a?(Symbol)
205
+ raise TypeError,
206
+ "must be a String or Symbol"
207
+ end
208
+
209
+ previous_id = id
210
+ self.id = value
211
+ yield
212
+ ensure
213
+ self.id = previous_id
214
+ end
215
+
216
+ end
217
+ end
data/lib/cmdx/error.rb CHANGED
@@ -2,22 +2,224 @@
2
2
 
3
3
  module CMDx
4
4
 
5
- # Base of all CMDx errors
5
+ ##
6
+ # Base exception class for all CMDx-specific errors.
7
+ # All other CMDx exceptions inherit from this class, providing a common
8
+ # hierarchy for error handling and rescue operations.
9
+ #
10
+ # This allows for catching all CMDx-related exceptions with a single rescue clause
11
+ # while still maintaining specific error types for detailed error handling.
12
+ #
13
+ # @example Catching all CMDx errors
14
+ # begin
15
+ # ProcessOrderTask.call(invalid_params)
16
+ # rescue CMDx::Error => e
17
+ # logger.error "CMDx error occurred: #{e.message}"
18
+ # end
19
+ #
20
+ # @example Specific error handling
21
+ # begin
22
+ # ProcessOrderTask.call(order_id: "invalid")
23
+ # rescue CMDx::CoercionError => e
24
+ # # Handle type coercion failures
25
+ # rescue CMDx::ValidationError => e
26
+ # # Handle validation failures
27
+ # rescue CMDx::Error => e
28
+ # # Handle any other CMDx errors
29
+ # end
30
+ #
31
+ # @see StandardError Ruby's standard error base class
32
+ # @since 1.0.0
6
33
  Error = Class.new(StandardError)
7
34
 
8
- # Raised when value could not be coerced to defined type
35
+ ##
36
+ # Raised when a value cannot be coerced to the specified type.
37
+ # This exception occurs during parameter processing when type coercion fails,
38
+ # typically due to incompatible data formats or invalid input values.
39
+ #
40
+ # CoercionError is raised by the various coercion modules when they encounter
41
+ # values that cannot be converted to the target type. Each coercion module
42
+ # provides specific error messages indicating the expected type and the
43
+ # problematic value.
44
+ #
45
+ # @example Integer coercion failure
46
+ # class MyTask < CMDx::Task
47
+ # required :count, type: :integer
48
+ # end
49
+ #
50
+ # # This will raise CoercionError during parameter processing
51
+ # MyTask.call(count: "not_a_number")
52
+ # # => CMDx::CoercionError: could not coerce into an integer
53
+ #
54
+ # @example Date coercion failure
55
+ # class ScheduleTask < CMDx::Task
56
+ # required :due_date, type: :date
57
+ # end
58
+ #
59
+ # ScheduleTask.call(due_date: "invalid_date")
60
+ # # => CMDx::CoercionError: could not coerce into a date
61
+ #
62
+ # @example Handling coercion errors
63
+ # begin
64
+ # MyTask.call(count: "invalid")
65
+ # rescue CMDx::CoercionError => e
66
+ # # Log the coercion failure and provide user-friendly message
67
+ # logger.warn "Invalid input format: #{e.message}"
68
+ # render json: { error: "Please provide a valid number" }
69
+ # end
70
+ #
71
+ # @see Parameter Parameter type definitions and coercion
72
+ # @see ParameterValue Parameter value processing and coercion
73
+ # @since 1.0.0
9
74
  CoercionError = Class.new(Error)
10
75
 
11
- # Raised when call execution time exceeds max allowed
12
- TimeoutError = Class.new(Interrupt)
13
-
14
- # Raised when call method not defined in implementing class
76
+ ##
77
+ # Raised when a task class doesn't implement the required `call` method.
78
+ # This exception enforces the CMDx contract that all task classes must
79
+ # provide a `call` method containing their business logic.
80
+ #
81
+ # This error typically occurs during development when creating new task
82
+ # classes that inherit from CMDx::Task but forget to implement the
83
+ # abstract `call` method.
84
+ #
85
+ # @example Missing call method
86
+ # class IncompleteTask < CMDx::Task
87
+ # required :data, type: :string
88
+ # # Missing call method implementation
89
+ # end
90
+ #
91
+ # IncompleteTask.call(data: "test")
92
+ # # => CMDx::UndefinedCallError: call method not defined in IncompleteTask
93
+ #
94
+ # @example Proper task implementation
95
+ # class CompleteTask < CMDx::Task
96
+ # required :data, type: :string
97
+ #
98
+ # def call
99
+ # # Business logic implementation
100
+ # context.result = process(data)
101
+ # end
102
+ # end
103
+ #
104
+ # @example Handling undefined call errors
105
+ # begin
106
+ # SomeTask.call(params)
107
+ # rescue CMDx::UndefinedCallError => e
108
+ # # This should typically only happen during development
109
+ # logger.error "Task implementation incomplete: #{e.message}"
110
+ # raise # Re-raise as this is a programming error
111
+ # end
112
+ #
113
+ # @see Task Task base class and call method requirement
114
+ # @see Workflow Workflow base class and call method requirement
115
+ # @since 1.0.0
15
116
  UndefinedCallError = Class.new(Error)
16
117
 
17
- # Raised when unknown coercion type
118
+ ##
119
+ # Raised when an unknown or unsupported coercion type is specified.
120
+ # This exception occurs when parameter definitions reference type coercions
121
+ # that don't exist or aren't registered in the CMDx coercion system.
122
+ #
123
+ # This error helps catch typos in type specifications and ensures that
124
+ # only supported data types are used in parameter definitions.
125
+ #
126
+ # @example Unknown type specification
127
+ # class MyTask < CMDx::Task
128
+ # required :value, type: :unknown_type # Typo or unsupported type
129
+ # end
130
+ #
131
+ # MyTask.call(value: "test")
132
+ # # => CMDx::UnknownCoercionError: unknown coercion unknown_type
133
+ #
134
+ # @example Common typos
135
+ # class TaskWithTypo < CMDx::Task
136
+ # required :count, type: :integr # Should be :integer
137
+ # required :flag, type: :bool # Should be :boolean
138
+ # required :data, type: :json # Should be :hash
139
+ # end
140
+ #
141
+ # @example Supported types
142
+ # class ProperTask < CMDx::Task
143
+ # required :id, type: :integer
144
+ # required :active, type: :boolean
145
+ # required :metadata, type: :hash
146
+ # required :tags, type: :array
147
+ # required :name, type: :string
148
+ # required :score, type: :float
149
+ # required :created_at, type: :date_time
150
+ # end
151
+ #
152
+ # @example Handling unknown coercion errors
153
+ # begin
154
+ # MyTask.call(params)
155
+ # rescue CMDx::UnknownCoercionError => e
156
+ # # This indicates a programming error in parameter definition
157
+ # logger.error "Invalid type specification: #{e.message}"
158
+ # raise # Re-raise as this should be fixed in code
159
+ # end
160
+ #
161
+ # @see Parameter Parameter type definitions
162
+ # @see ParameterValue Type coercion processing
163
+ # @since 1.0.0
18
164
  UnknownCoercionError = Class.new(Error)
19
165
 
20
- # Raised when value failed a validation
166
+ ##
167
+ # Raised when a parameter value fails validation rules.
168
+ # This exception occurs during parameter processing when values don't meet
169
+ # the specified validation criteria, such as format requirements, length
170
+ # constraints, or custom validation logic.
171
+ #
172
+ # ValidationError provides detailed feedback about why validation failed,
173
+ # helping developers and users understand what corrections are needed.
174
+ #
175
+ # @example Presence validation failure
176
+ # class CreateUserTask < CMDx::Task
177
+ # required :email, type: :string, presence: true
178
+ # end
179
+ #
180
+ # CreateUserTask.call(email: "")
181
+ # # => CMDx::ValidationError: cannot be empty
182
+ #
183
+ # @example Format validation failure
184
+ # class ValidateEmailTask < CMDx::Task
185
+ # required :email, type: :string, format: { with: /@/ }
186
+ # end
187
+ #
188
+ # ValidateEmailTask.call(email: "invalid-email")
189
+ # # => CMDx::ValidationError: is an invalid format
190
+ #
191
+ # @example Length validation failure
192
+ # class SetPasswordTask < CMDx::Task
193
+ # required :password, type: :string, length: { min: 8 }
194
+ # end
195
+ #
196
+ # SetPasswordTask.call(password: "short")
197
+ # # => CMDx::ValidationError: length must be at least 8
198
+ #
199
+ # @example Custom validation failure
200
+ # class ProcessOrderTask < CMDx::Task
201
+ # required :quantity, type: :integer, custom: -> (val) { val > 0 }
202
+ # end
203
+ #
204
+ # ProcessOrderTask.call(quantity: -1)
205
+ # # => CMDx::ValidationError: is not valid
206
+ #
207
+ # @example Handling validation errors
208
+ # begin
209
+ # CreateUserTask.call(email: "", password: "short")
210
+ # rescue CMDx::ValidationError => e
211
+ # # Provide user-friendly feedback
212
+ # render json: {
213
+ # error: "Validation failed",
214
+ # message: e.message,
215
+ # field: extract_field_from_context(e)
216
+ # }
217
+ # end
218
+ #
219
+ # @see Parameter Parameter validation options
220
+ # @see ParameterValue Validation processing
221
+ # @see Validators Validation modules (Presence, Format, Length, etc.)
222
+ # @since 1.0.0
21
223
  ValidationError = Class.new(Error)
22
224
 
23
225
  end