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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +13 -12
  4. data/.cursor/prompts/yardoc.md +11 -6
  5. data/CHANGELOG.md +13 -2
  6. data/README.md +1 -0
  7. data/docs/ai_prompts.md +269 -195
  8. data/docs/basics/call.md +124 -58
  9. data/docs/basics/chain.md +190 -160
  10. data/docs/basics/context.md +242 -154
  11. data/docs/basics/setup.md +302 -32
  12. data/docs/callbacks.md +390 -94
  13. data/docs/configuration.md +181 -65
  14. data/docs/deprecation.md +245 -0
  15. data/docs/getting_started.md +161 -39
  16. data/docs/internationalization.md +590 -70
  17. data/docs/interruptions/exceptions.md +135 -118
  18. data/docs/interruptions/faults.md +150 -125
  19. data/docs/interruptions/halt.md +134 -80
  20. data/docs/logging.md +181 -118
  21. data/docs/middlewares.md +150 -377
  22. data/docs/outcomes/result.md +140 -112
  23. data/docs/outcomes/states.md +134 -99
  24. data/docs/outcomes/statuses.md +204 -146
  25. data/docs/parameters/coercions.md +232 -281
  26. data/docs/parameters/defaults.md +224 -169
  27. data/docs/parameters/definitions.md +289 -141
  28. data/docs/parameters/namespacing.md +250 -161
  29. data/docs/parameters/validations.md +260 -133
  30. data/docs/testing.md +191 -197
  31. data/docs/workflows.md +143 -98
  32. data/lib/cmdx/callback.rb +23 -19
  33. data/lib/cmdx/callback_registry.rb +1 -3
  34. data/lib/cmdx/chain_inspector.rb +23 -23
  35. data/lib/cmdx/chain_serializer.rb +38 -19
  36. data/lib/cmdx/coercion.rb +20 -12
  37. data/lib/cmdx/coercion_registry.rb +51 -32
  38. data/lib/cmdx/configuration.rb +84 -31
  39. data/lib/cmdx/context.rb +32 -21
  40. data/lib/cmdx/core_ext/hash.rb +13 -13
  41. data/lib/cmdx/core_ext/module.rb +1 -1
  42. data/lib/cmdx/core_ext/object.rb +12 -12
  43. data/lib/cmdx/correlator.rb +60 -39
  44. data/lib/cmdx/errors.rb +105 -131
  45. data/lib/cmdx/fault.rb +66 -45
  46. data/lib/cmdx/immutator.rb +20 -21
  47. data/lib/cmdx/lazy_struct.rb +78 -70
  48. data/lib/cmdx/log_formatters/json.rb +1 -1
  49. data/lib/cmdx/log_formatters/key_value.rb +1 -1
  50. data/lib/cmdx/log_formatters/line.rb +1 -1
  51. data/lib/cmdx/log_formatters/logstash.rb +1 -1
  52. data/lib/cmdx/log_formatters/pretty_json.rb +1 -1
  53. data/lib/cmdx/log_formatters/pretty_key_value.rb +1 -1
  54. data/lib/cmdx/log_formatters/pretty_line.rb +1 -1
  55. data/lib/cmdx/log_formatters/raw.rb +2 -2
  56. data/lib/cmdx/logger.rb +19 -14
  57. data/lib/cmdx/logger_ansi.rb +33 -17
  58. data/lib/cmdx/logger_serializer.rb +85 -24
  59. data/lib/cmdx/middleware.rb +39 -21
  60. data/lib/cmdx/middleware_registry.rb +4 -3
  61. data/lib/cmdx/parameter.rb +151 -89
  62. data/lib/cmdx/parameter_inspector.rb +34 -21
  63. data/lib/cmdx/parameter_registry.rb +36 -30
  64. data/lib/cmdx/parameter_serializer.rb +21 -14
  65. data/lib/cmdx/result.rb +136 -135
  66. data/lib/cmdx/result_ansi.rb +31 -17
  67. data/lib/cmdx/result_inspector.rb +32 -27
  68. data/lib/cmdx/result_logger.rb +23 -14
  69. data/lib/cmdx/result_serializer.rb +65 -27
  70. data/lib/cmdx/task.rb +234 -113
  71. data/lib/cmdx/task_deprecator.rb +22 -25
  72. data/lib/cmdx/task_processor.rb +89 -88
  73. data/lib/cmdx/task_serializer.rb +27 -14
  74. data/lib/cmdx/utils/monotonic_runtime.rb +2 -4
  75. data/lib/cmdx/validator.rb +25 -16
  76. data/lib/cmdx/validator_registry.rb +53 -31
  77. data/lib/cmdx/validators/exclusion.rb +1 -1
  78. data/lib/cmdx/validators/format.rb +2 -2
  79. data/lib/cmdx/validators/inclusion.rb +2 -2
  80. data/lib/cmdx/validators/length.rb +2 -2
  81. data/lib/cmdx/validators/numeric.rb +3 -3
  82. data/lib/cmdx/validators/presence.rb +2 -2
  83. data/lib/cmdx/version.rb +1 -1
  84. data/lib/cmdx/workflow.rb +54 -33
  85. data/lib/generators/cmdx/task_generator.rb +6 -6
  86. data/lib/generators/cmdx/workflow_generator.rb +6 -6
  87. metadata +3 -1
@@ -1,82 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Handles the execution orchestration of Task instances with middleware and callback support.
4
+ # Core task execution processor handling the complete task lifecycle.
5
5
  #
6
- # TaskProcessor provides the core execution logic for Task instances, managing
7
- # the complete lifecycle including parameter validation, callback execution,
8
- # middleware processing, error handling, and result finalization. It supports
9
- # both regular and bang execution modes with different error handling behaviors.
6
+ # TaskProcessor manages the execution pipeline for individual tasks, coordinating
7
+ # parameter validation, callback invocation, error handling, and result state
8
+ # management. It provides both safe execution (capturing exceptions) and unsafe
9
+ # execution (re-raising exceptions) modes through call and call! methods respectively.
10
+ # The processor ensures proper state transitions, handles fault propagation, and
11
+ # maintains execution context throughout the task lifecycle.
10
12
  class TaskProcessor
11
13
 
12
14
  # @return [CMDx::Task] The task instance being executed
13
15
  attr_reader :task
14
16
 
15
- # Creates a new TaskProcessor instance for the specified task.
17
+ # Creates a new task processor for the specified task instance.
16
18
  #
17
- # @param task [CMDx::Task] the task instance to execute
19
+ # @param task [CMDx::Task] the task instance to process
18
20
  #
19
- # @return [TaskProcessor] a new TaskProcessor instance
21
+ # @return [TaskProcessor] a new processor instance for the task
20
22
  #
21
- # @example Create processor for a task
23
+ # @example Create a processor for a task
22
24
  # task = MyTask.new(user_id: 123)
23
25
  # processor = TaskProcessor.new(task)
24
- # processor.task # => #<MyTask:...>
25
26
  def initialize(task)
26
27
  @task = task
27
28
  end
28
29
 
29
30
  class << self
30
31
 
31
- # Executes a task with full error handling and result management.
32
+ # Executes the specified task and returns the result without raising exceptions.
32
33
  #
33
- # This is a convenience method that creates a new TaskProcessor instance
34
- # and immediately calls the safe execution method. It provides the same
35
- # comprehensive error handling as the instance method, catching faults
36
- # and StandardErrors and converting them to failed results.
34
+ # Creates a new processor instance and executes the task through the complete
35
+ # lifecycle including validation, callbacks, and error handling. Exceptions
36
+ # are captured in the result rather than being raised to the caller.
37
37
  #
38
38
  # @param task [CMDx::Task] the task instance to execute
39
39
  #
40
- # @return [Result] the task's result object after execution
40
+ # @return [CMDx::Result] the execution result containing state and status information
41
41
  #
42
- # @raise [UndefinedCallError] if the task doesn't implement a call method
43
- #
44
- # @example Execute a task safely using class method
45
- # task = MyTask.new(name: "test")
46
- # result = TaskProcessor.call(task)
47
- # result.success? # => true or false
48
- #
49
- # @example Handle task with validation errors
50
- # task = MyTask.new # missing required parameters
42
+ # @example Execute a task safely
43
+ # task = ProcessDataTask.new(data: raw_data)
51
44
  # result = TaskProcessor.call(task)
52
- # result.failed? # => true
45
+ # puts result.status #=> "success", "failed", or "skipped"
53
46
  def call(task)
54
47
  new(task).call
55
48
  end
56
49
 
57
- # Executes a task with bang semantics, re-raising exceptions.
50
+ # Executes the specified task and raises exceptions on failure.
58
51
  #
59
- # This is a convenience method that creates a new TaskProcessor instance
60
- # and immediately calls the strict execution method. It provides the same
61
- # strict error handling as the instance method, re-raising exceptions
62
- # after proper cleanup and chain clearing.
52
+ # Creates a new processor instance and executes the task through the complete
53
+ # lifecycle. Unlike call, this method will re-raise exceptions including
54
+ # Fault exceptions when their status matches the task's halt configuration.
63
55
  #
64
56
  # @param task [CMDx::Task] the task instance to execute
65
57
  #
66
- # @return [Result] the task's result object after execution
58
+ # @return [CMDx::Result] the execution result on success
67
59
  #
68
- # @raise [UndefinedCallError] if the task doesn't implement a call method
69
- # @raise [Fault] if a fault occurs during execution
60
+ # @raise [CMDx::Fault] when a fault occurs with status matching task halt configuration
61
+ # @raise [StandardError] when unexpected errors occur during execution
70
62
  #
71
- # @example Execute task with strict error handling
72
- # task = MyTask.new(name: "test")
73
- # result = TaskProcessor.call!(task) # raises on failure
74
- #
75
- # @example Handle exceptions in bang mode
63
+ # @example Execute a task with exception raising
64
+ # task = CriticalTask.new(operation: "delete")
76
65
  # begin
77
- # TaskProcessor.call!(task)
66
+ # result = TaskProcessor.call!(task)
67
+ # puts "Success: #{result.status}"
78
68
  # rescue CMDx::Fault => e
79
- # puts "Task failed: #{e.result.status}"
69
+ # puts "Task failed: #{e.message}"
80
70
  # end
81
71
  def call!(task)
82
72
  new(task).call!
@@ -84,28 +74,24 @@ module CMDx
84
74
 
85
75
  end
86
76
 
87
- # Executes the task with full error handling and result management.
88
- #
89
- # This method provides safe task execution with comprehensive error handling,
90
- # automatic result state management, and callback execution. Faults are caught
91
- # and processed according to task halt settings, while StandardErrors are
92
- # converted to failed results.
77
+ # Executes the task with safe error handling and returns the result.
93
78
  #
94
- # @return [Result] the task's result object after execution
79
+ # Runs the complete task execution pipeline including parameter validation,
80
+ # callback invocation, and the task's call method. Captures all exceptions
81
+ # as result status rather than raising them, ensuring the chain continues
82
+ # execution. Handles both standard errors and Fault exceptions according
83
+ # to the task's halt configuration.
95
84
  #
96
- # @raise [UndefinedCallError] if the task doesn't implement a call method
85
+ # @return [CMDx::Result] the execution result with captured state and status
97
86
  #
98
- # @example Execute a task safely
99
- # task = MyTask.new(name: "test")
87
+ # @example Safe task execution
100
88
  # processor = TaskProcessor.new(task)
101
89
  # result = processor.call
102
- # result.success? # => true or false
103
- #
104
- # @example Handle task with validation errors
105
- # task = MyTask.new # missing required parameters
106
- # processor = TaskProcessor.new(task)
107
- # result = processor.call
108
- # result.failed? # => true
90
+ # if result.success?
91
+ # puts "Task completed successfully"
92
+ # else
93
+ # puts "Task failed: #{result.metadata[:reason]}"
94
+ # end
109
95
  def call
110
96
  task.result.runtime do
111
97
  before_call
@@ -128,27 +114,27 @@ module CMDx
128
114
  terminate_call
129
115
  end
130
116
 
131
- # Executes the task with bang semantics, re-raising exceptions.
117
+ # Executes the task with exception raising on halt conditions.
132
118
  #
133
- # This method provides strict task execution where exceptions are re-raised
134
- # after proper cleanup. It clears the execution chain on failures and
135
- # provides different error handling behavior compared to the regular call method.
119
+ # Runs the complete task execution pipeline including parameter validation,
120
+ # callback invocation, and the task's call method. Unlike call, this method
121
+ # will re-raise Fault exceptions when their status matches the task's halt
122
+ # configuration, and clears the execution chain before raising.
136
123
  #
137
- # @return [Result] the task's result object after execution
124
+ # @return [CMDx::Result] the execution result on successful completion
138
125
  #
139
- # @raise [UndefinedCallError] if the task doesn't implement a call method
140
- # @raise [Fault] if a fault occurs during execution
126
+ # @raise [CMDx::Fault] when a fault occurs with status matching task halt configuration
127
+ # @raise [CMDx::UndefinedCallError] when the task's call method is not implemented
128
+ # @raise [StandardError] when unexpected errors occur during execution
141
129
  #
142
- # @example Execute task with strict error handling
143
- # task = MyTask.new(name: "test")
144
- # processor = TaskProcessor.new(task)
145
- # result = processor.call! # raises on failure
146
- #
147
- # @example Handle exceptions in bang mode
130
+ # @example Task execution with exception raising
131
+ # processor = TaskProcessor.new(critical_task)
148
132
  # begin
149
- # processor.call!
133
+ # result = processor.call!
134
+ # puts "Task succeeded"
150
135
  # rescue CMDx::Fault => e
151
- # puts "Task failed: #{e.result.status}"
136
+ # puts "Critical failure: #{e.message}"
137
+ # # Chain is cleared, execution stops
152
138
  # end
153
139
  def call!
154
140
  task.result.runtime do
@@ -173,7 +159,11 @@ module CMDx
173
159
 
174
160
  private
175
161
 
176
- # Executes pre-execution callbacks and parameter validation.
162
+ # Executes pre-execution callbacks and sets the task to executing state.
163
+ #
164
+ # Invokes before_execution callbacks, transitions the result to executing
165
+ # state, and triggers on_executing callbacks. This method prepares the
166
+ # task for execution and notifies registered callbacks about the state change.
177
167
  #
178
168
  # @return [void]
179
169
  def before_call
@@ -183,16 +173,14 @@ module CMDx
183
173
  task.cmd_callbacks.call(task, :on_executing)
184
174
  end
185
175
 
186
- # Validates task parameters and handles validation errors.
176
+ # Validates task parameters and handles validation failures.
187
177
  #
188
- # This method orchestrates the parameter validation process by executing
189
- # validation callbacks, performing parameter validation, and handling
190
- # any validation errors that occur. If validation fails, the task result
191
- # is marked as failed with detailed error messages.
178
+ # Executes parameter validation callbacks, validates all task parameters
179
+ # against their defined rules, and sets the task result to failed if
180
+ # validation errors are found. Collects all validation messages into
181
+ # the result metadata.
192
182
  #
193
183
  # @return [void]
194
- #
195
- # @raise [Exception] Validations, coercions, or exceptions
196
184
  def validate_parameters
197
185
  task.cmd_callbacks.call(task, :before_validation)
198
186
 
@@ -207,19 +195,28 @@ module CMDx
207
195
  task.cmd_callbacks.call(task, :after_validation)
208
196
  end
209
197
 
210
- # Clears the execution chain and re-raises the given exception.
198
+ # Clears the execution chain and raises the specified exception.
211
199
  #
212
- # @param exception [Exception] the exception to re-raise after chain cleanup
200
+ # This method is used to clean up the execution context before
201
+ # re-raising exceptions, ensuring that the chain state is properly
202
+ # reset when execution cannot continue.
213
203
  #
214
- # @return [void] this method never returns as it always raises
204
+ # @param exception [Exception] the exception to raise after clearing the chain
215
205
  #
216
- # @raise [Exception] always re-raises the provided exception
206
+ # @return [void]
207
+ #
208
+ # @raise [Exception] the provided exception after chain cleanup
217
209
  def raise!(exception)
218
210
  Chain.clear
219
211
  raise(exception)
220
212
  end
221
213
 
222
- # Executes post-execution callbacks based on result state and status.
214
+ # Executes post-execution callbacks based on task result state and status.
215
+ #
216
+ # Invokes appropriate callbacks based on the task's final execution state
217
+ # (success, failure, etc.) and status. Handles both state-specific and
218
+ # status-specific callback invocation, as well as general execution
219
+ # completion callbacks.
223
220
  #
224
221
  # @return [void]
225
222
  def after_call
@@ -233,9 +230,13 @@ module CMDx
233
230
  task.cmd_callbacks.call(task, :after_execution)
234
231
  end
235
232
 
236
- # Finalizes task execution with immutability and logging.
233
+ # Finalizes task execution by freezing state and logging results.
237
234
  #
238
- # @return [Result] the task's result object
235
+ # Applies immutability to the task instance and logs the execution
236
+ # result. This method ensures that the task state cannot be modified
237
+ # after execution and provides visibility into the execution outcome.
238
+ #
239
+ # @return [void]
239
240
  def terminate_call
240
241
  Immutator.call(task)
241
242
  ResultLogger.call(task.result)
@@ -1,33 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Serializes Task objects into hash representations for external consumption.
5
- # Provides a consistent interface for converting task execution data into
6
- # structured format suitable for logging, API responses, or persistence.
4
+ # Task serialization module for converting task objects to hash format.
5
+ #
6
+ # TaskSerializer provides functionality to serialize task objects into a
7
+ # standardized hash representation that includes essential metadata about
8
+ # the task such as its index, chain ID, type, class, ID, and tags. The
9
+ # serialized format is commonly used for debugging, logging, and introspection
10
+ # purposes throughout the task execution pipeline.
7
11
  module TaskSerializer
8
12
 
9
13
  module_function
10
14
 
11
- # Converts a task object into a hash representation containing task metadata.
12
- # Extracts key task attributes including execution index, chain association,
13
- # type classification, and associated tags for complete task identification.
15
+ # Serializes a task object into a hash representation.
14
16
  #
15
- # @param task [Task] the task instance to serialize
17
+ # Converts a task instance into a standardized hash format containing
18
+ # key metadata about the task's execution context and classification.
19
+ # The serialization includes information from the task's result, chain,
20
+ # and command settings to provide comprehensive task identification.
16
21
  #
17
- # @return [Hash] hash containing task metadata and execution details
22
+ # @param task [CMDx::Task, CMDx::Workflow] the task or workflow object to serialize
18
23
  #
19
- # @raise [NoMethodError] if task doesn't respond to required methods
24
+ # @return [Hash] a hash containing the task's metadata
25
+ # @option return [Integer] :index the task's position index in the execution chain
26
+ # @option return [String] :chain_id the unique identifier of the task's execution chain
27
+ # @option return [String] :type the task type, either "Task" or "Workflow"
28
+ # @option return [String] :class the full class name of the task
29
+ # @option return [String] :id the unique identifier of the task instance
30
+ # @option return [Array] :tags the tags associated with the task from cmd settings
20
31
  #
21
- # @example Serializing a task
22
- # task = UserRegistrationTask.call(email: "user@example.com")
32
+ # @raise [NoMethodError] if the task doesn't respond to required methods
33
+ #
34
+ # @example Serialize a basic task
35
+ # task = ProcessDataTask.new
23
36
  # TaskSerializer.call(task)
24
- # # => {
37
+ # #=> {
25
38
  # # index: 0,
26
39
  # # chain_id: "abc123",
27
40
  # # type: "Task",
28
- # # class: "UserRegistrationTask",
41
+ # # class: "ProcessDataTask",
29
42
  # # id: "def456",
30
- # # tags: [:authentication, :user_management]
43
+ # # tags: []
31
44
  # # }
32
45
  def call(task)
33
46
  {
@@ -19,12 +19,10 @@ module CMDx
19
19
  # @return [Integer] the execution time in milliseconds
20
20
  #
21
21
  # @example Basic usage
22
- # runtime = MonotonicRuntime.call { sleep(0.1) }
23
- # # => 100 (approximately)
22
+ # MonotonicRuntime.call { sleep(0.1) } #=> 100 (approximately)
24
23
  #
25
24
  # @example Measuring database query time
26
- # query_time = MonotonicRuntime.call { User.find(1) }
27
- # # => 15 (milliseconds)
25
+ # MonotonicRuntime.call { User.find(1) } #=> 15 (milliseconds)
28
26
  def call(&)
29
27
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
30
28
  yield
@@ -1,46 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Base class for implementing validation functionality in parameter processing.
4
+ # Base class for implementing parameter validation functionality in task processing.
5
5
  #
6
- # Validators are used to validate parameter values during task execution to
7
- # ensure data integrity and business rule compliance. All validator implementations
8
- # must inherit from this class and implement the abstract call method.
6
+ # Validators are used to validate parameter values against specific rules and constraints,
7
+ # supporting both built-in validation types and custom validation logic. All validator
8
+ # implementations must inherit from this class and implement the abstract call method.
9
9
  class Validator
10
10
 
11
11
  # Executes a validator by creating a new instance and calling it.
12
12
  #
13
13
  # @param value [Object] the value to be validated
14
- # @param options [Hash] optional validation configuration
14
+ # @param options [Hash] additional options for the validation
15
15
  #
16
- # @return [Object] the result of the validation execution
16
+ # @return [Object] the validated value if validation passes
17
17
  #
18
18
  # @raise [UndefinedCallError] when the validator subclass doesn't implement call
19
+ # @raise [ValidationError] when validation fails
19
20
  #
20
21
  # @example Execute a validator on a value
21
- # MyValidator.call("example", { min_length: 5 })
22
+ # PresenceValidator.call("some_value") #=> "some_value"
23
+ #
24
+ # @example Execute with options
25
+ # NumericValidator.call(42, greater_than: 10) #=> 42
22
26
  def self.call(value, options = {})
23
27
  new.call(value, options)
24
28
  end
25
29
 
26
30
  # Abstract method that must be implemented by validator subclasses.
27
31
  #
28
- # This method contains the actual validation logic to be executed.
29
- # Subclasses must override this method to provide their specific
30
- # validation implementation.
32
+ # This method contains the actual validation logic to verify the input
33
+ # value meets the specified criteria. Subclasses must override this method
34
+ # to provide their specific validation implementation.
31
35
  #
32
- # @param _value [Object] the value to be validated
33
- # @param _options [Hash] optional validation configuration
36
+ # @param value [Object] the value to be validated
37
+ # @param options [Hash] additional options for the validation
34
38
  #
35
- # @return [Object] the result of the validation execution
39
+ # @return [Object] the validated value if validation passes
36
40
  #
37
41
  # @raise [UndefinedCallError] always raised in the base class
42
+ # @raise [ValidationError] when validation fails in subclass implementations
38
43
  #
39
44
  # @example Implement in a subclass
40
- # def call(value, options)
41
- # raise ValidationError, "Value too short" if value.length < options[:min_length]
45
+ # class BlankValidator < CMDx::Validator
46
+ # def call(value, options = {})
47
+ # if value.nil? || value.empty?
48
+ # raise ValidationError, options[:message] || "Value cannot be blank"
49
+ # end
50
+ # end
42
51
  # end
43
- def call(_value, _options = {})
52
+ def call(value, options = {}) # rubocop:disable Lint/UnusedMethodArgument
44
53
  raise UndefinedCallError, "call method not defined in #{self.class.name}"
45
54
  end
46
55
 
@@ -1,24 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Registry for managing validator definitions and execution within tasks.
4
+ # Registry for parameter validation handlers in the CMDx framework.
5
5
  #
6
- # This registry handles the registration and execution of validators for
7
- # parameter validation, including built-in validators and custom validators
8
- # that can be registered at runtime.
6
+ # ValidatorRegistry manages the collection of validator implementations
7
+ # that can be used for parameter validation in tasks. It provides a
8
+ # centralized registry where validators can be registered by type and
9
+ # invoked during parameter processing. The registry comes pre-loaded
10
+ # with built-in validators for common validation scenarios.
9
11
  class ValidatorRegistry
10
12
 
11
- # The internal hash storing validator definitions.
12
- #
13
- # @return [Hash] hash containing validator type keys and validator class values
13
+ # @return [Hash] internal hash storing validator implementations by type
14
14
  attr_reader :registry
15
15
 
16
- # Initializes a new validator registry with built-in validators.
16
+ # Creates a new validator registry with built-in validators.
17
+ #
18
+ # The registry is initialized with standard validators including
19
+ # exclusion, format, inclusion, length, numeric, and presence validation.
20
+ # These built-in validators provide common validation functionality
21
+ # that can be immediately used without additional registration.
17
22
  #
18
- # @return [ValidatorRegistry] a new validator registry instance
23
+ # @return [ValidatorRegistry] a new registry instance with built-in validators
19
24
  #
20
- # @example Creating a validator registry
21
- # ValidatorRegistry.new
25
+ # @example Create a new validator registry
26
+ # registry = ValidatorRegistry.new
27
+ # registry.registry.keys #=> [:exclusion, :format, :inclusion, :length, :numeric, :presence]
22
28
  def initialize
23
29
  @registry = {
24
30
  exclusion: Validators::Exclusion,
@@ -30,46 +36,62 @@ module CMDx
30
36
  }
31
37
  end
32
38
 
33
- # Registers a custom validator for a specific type.
39
+ # Registers a new validator implementation for the specified type.
40
+ #
41
+ # This method allows custom validators to be added to the registry,
42
+ # enabling extended validation functionality beyond the built-in
43
+ # validators. The validator can be a class, symbol, string, or proc
44
+ # that implements the validation logic.
34
45
  #
35
- # @param type [Symbol] the validator type to register
36
- # @param validator [Class, Module, Symbol, Proc] the validator to register
46
+ # @param type [Symbol] the validator type identifier
47
+ # @param validator [Class, Symbol, String, Proc] the validator implementation
37
48
  #
38
49
  # @return [ValidatorRegistry] returns self for method chaining
39
50
  #
40
- # @example Registering a custom validator class
51
+ # @example Register a custom validator class
41
52
  # registry.register(:email, EmailValidator)
42
53
  #
43
- # @example Registering a Proc validator
44
- # registry.register(:custom, ->(value, opts) { value.length > 3 })
54
+ # @example Register a symbol validator
55
+ # registry.register(:zipcode, :validate_zipcode)
45
56
  #
46
- # @example Registering a Symbol validator
47
- # registry.register(:password, :validate_password_strength)
57
+ # @example Register a proc validator
58
+ # registry.register(:positive, ->(value, options) { value > 0 })
48
59
  #
49
- # @example Chaining validator registrations
50
- # registry.register(:phone, PhoneValidator)
51
- # .register(:zipcode, ZipcodeValidator)
60
+ # @example Method chaining
61
+ # registry.register(:email, EmailValidator)
62
+ # .register(:phone, PhoneValidator)
52
63
  def register(type, validator)
53
64
  registry[type] = validator
54
65
  self
55
66
  end
56
67
 
57
- # Executes validation for a specific type on a given value.
68
+ # Executes validation for a parameter value using the specified validator type.
69
+ #
70
+ # This method performs validation by looking up the registered validator
71
+ # for the given type and executing it with the provided value and options.
72
+ # The validation is only performed if the task's evaluation of the options
73
+ # returns a truthy value, allowing for conditional validation.
58
74
  #
59
- # @param task [Task] the task instance to execute validation on
60
- # @param type [Symbol] the validator type to execute
75
+ # @param task [Task] the task instance performing validation
76
+ # @param type [Symbol] the validator type to use
61
77
  # @param value [Object] the value to validate
62
- # @param options [Hash] options for conditional validation execution
78
+ # @param options [Hash] validation options and configuration
63
79
  #
64
- # @return [Object, nil] returns the validation result or nil if skipped
80
+ # @return [Object, nil] the validation result or nil if validation was skipped
65
81
  #
66
- # @raise [UnknownValidatorError] when the validator type is not registered
82
+ # @raise [UnknownValidatorError] if the specified validator type is not registered
67
83
  #
68
- # @example Validating with a built-in validator
84
+ # @example Validate with a built-in validator
69
85
  # registry.call(task, :presence, "", {})
86
+ # #=> may raise ValidationError if value is blank
87
+ #
88
+ # @example Validate with options
89
+ # registry.call(task, :length, "hello", minimum: 3, maximum: 10)
90
+ # #=> validates string length is between 3 and 10 characters
70
91
  #
71
- # @example Validating with options
72
- # registry.call(task, :length, "test", { minimum: 5 })
92
+ # @example Conditional validation that gets skipped
93
+ # registry.call(task, :presence, "", if: -> { false })
94
+ # #=> returns nil without performing validation
73
95
  def call(task, type, value, options = {})
74
96
  raise UnknownValidatorError, "unknown validator #{type}" unless registry.key?(type)
75
97
  return unless task.cmdx_eval(options)
@@ -35,7 +35,7 @@ module CMDx
35
35
  #
36
36
  # @example Valid exclusion
37
37
  # Validators::Exclusion.call("user", exclusion: { in: ["admin", "root"] })
38
- # # => nil (no error raised)
38
+ # #=> nil (no error raised)
39
39
  #
40
40
  # @example Using a custom message
41
41
  # Validators::Exclusion.call("admin", exclusion: { in: ["admin", "root"], message: "Reserved username not allowed" })
@@ -25,7 +25,7 @@ module CMDx
25
25
  #
26
26
  # @example Validating with a positive pattern
27
27
  # Validators::Format.call("user123", format: { with: /\A[a-z]+\d+\z/ })
28
- # # => nil (no error raised)
28
+ # #=> nil (no error raised)
29
29
  #
30
30
  # @example Validating with a negative pattern
31
31
  # Validators::Format.call("admin", format: { without: /admin|root/ })
@@ -33,7 +33,7 @@ module CMDx
33
33
  #
34
34
  # @example Validating with both patterns
35
35
  # Validators::Format.call("user123", format: { with: /\A[a-z]+\d+\z/, without: /admin|root/ })
36
- # # => nil (no error raised)
36
+ # #=> nil (no error raised)
37
37
  #
38
38
  # @example Invalid format with positive pattern
39
39
  # Validators::Format.call("123abc", format: { with: /\A[a-z]+\d+\z/ })
@@ -27,11 +27,11 @@ module CMDx
27
27
  #
28
28
  # @example Including from an array
29
29
  # Validators::Inclusion.call("user", inclusion: { in: ["user", "admin"] })
30
- # # => nil (no error raised)
30
+ # #=> nil (no error raised)
31
31
  #
32
32
  # @example Including from a range
33
33
  # Validators::Inclusion.call(5, inclusion: { in: 1..10 })
34
- # # => nil (no error raised)
34
+ # #=> nil (no error raised)
35
35
  #
36
36
  # @example Invalid inclusion from array
37
37
  # Validators::Inclusion.call("guest", inclusion: { in: ["user", "admin"] })
@@ -39,7 +39,7 @@ module CMDx
39
39
  #
40
40
  # @example Validating within a range
41
41
  # Validators::Length.call("hello", length: { within: 1..10 })
42
- # # => nil (no error raised)
42
+ # #=> nil (no error raised)
43
43
  #
44
44
  # @example Validating minimum length
45
45
  # Validators::Length.call("hi", length: { min: 5 })
@@ -47,7 +47,7 @@ module CMDx
47
47
  #
48
48
  # @example Validating exact length
49
49
  # Validators::Length.call("test", length: { is: 4 })
50
- # # => nil (no error raised)
50
+ # #=> nil (no error raised)
51
51
  #
52
52
  # @example Validating with custom message
53
53
  # Validators::Length.call("", length: { min: 1, message: "cannot be empty" })