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
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMDx
4
+ # Parameter collection class for managing multiple parameter definitions.
5
+ #
6
+ # The ParameterRegistry class extends Array to provide specialized functionality for
7
+ # managing collections of Parameter instances within CMDx tasks. It handles
8
+ # validation coordination, serialization, and inspection of parameter groups.
9
+ #
10
+ # @example Basic parameter collection usage
11
+ # parameter_registry = ParameterRegistry.new
12
+ # parameter_registry << Parameter.new(:user_id, klass: Task, type: :integer)
13
+ # parameter_registry << Parameter.new(:email, klass: Task, type: :string)
14
+ # parameter_registry.valid? # => true (if all parameters are valid)
15
+ #
16
+ # @example Parameter collection validation
17
+ # parameter_registry.validate!(task_instance) # Validates all parameters
18
+ # parameter_registry.invalid? # => true if any parameter failed validation
19
+ #
20
+ # @example Parameter collection serialization
21
+ # parameter_registry.to_h # => Array of parameter hash representations
22
+ # parameter_registry.to_s # => Human-readable parameter descriptions
23
+ #
24
+ # @see CMDx::Parameter Individual parameter definitions
25
+ # @see CMDx::Task Task parameter integration
26
+ class ParameterRegistry < Array
27
+
28
+ # Checks if any parameters in the collection are invalid.
29
+ #
30
+ # @return [Boolean] true if any parameter has validation errors, false otherwise
31
+ #
32
+ # @example
33
+ # parameter_registry.invalid? # => true if validation errors exist
34
+ def invalid?
35
+ !valid?
36
+ end
37
+
38
+ # Checks if all parameters in the collection are valid.
39
+ #
40
+ # @return [Boolean] true if all parameters are valid, false otherwise
41
+ #
42
+ # @example
43
+ # parameter_registry.valid? # => true if no validation errors exist
44
+ def valid?
45
+ all?(&:valid?)
46
+ end
47
+
48
+ # Validates all parameters in the collection against a task instance.
49
+ #
50
+ # Recursively validates each parameter and its children by calling the
51
+ # parameter accessor methods on the task instance, which triggers
52
+ # value resolution, coercion, and validation.
53
+ #
54
+ # @param task [CMDx::Task] The task instance to validate parameters against
55
+ # @return [void]
56
+ #
57
+ # @example Validating parameters
58
+ # task = ProcessOrderTask.new
59
+ # parameter_registry.validate!(task) # Validates all parameters
60
+ #
61
+ # @example Validation with nested parameters
62
+ # # Validates parent parameters and all nested child parameters
63
+ # parameter_registry.validate!(task_with_nested_params)
64
+ def validate!(task)
65
+ each { |p| recursive_validate!(task, p) }
66
+ end
67
+
68
+ # Converts the parameter collection to a hash representation.
69
+ #
70
+ # Serializes all parameters in the collection to their hash representations
71
+ # using the ParametersSerializer.
72
+ #
73
+ # @return [Array<Hash>] Array of serialized parameter data
74
+ #
75
+ # @example
76
+ # parameter_registry.to_h
77
+ # # => [
78
+ # # {
79
+ # # source: :context,
80
+ # # name: :user_id,
81
+ # # type: :integer,
82
+ # # required: true,
83
+ # # options: {},
84
+ # # children: []
85
+ # # },
86
+ # # { ... }
87
+ # # ]
88
+ def to_h
89
+ ParametersSerializer.call(self)
90
+ end
91
+ alias to_a to_h
92
+
93
+ # Converts the parameter collection to a string representation.
94
+ #
95
+ # Creates a human-readable string representation of all parameters
96
+ # in the collection using the ParametersInspector.
97
+ #
98
+ # @return [String] Multi-line parameter descriptions
99
+ #
100
+ # @example
101
+ # parameter_registry.to_s
102
+ # # => "Parameter: name=user_id type=integer source=context required=true
103
+ # # Parameter: name=email type=string source=context required=false"
104
+ def to_s
105
+ ParametersInspector.call(self)
106
+ end
107
+
108
+ private
109
+
110
+ # Recursively validates a parameter and all its children.
111
+ #
112
+ # Calls the parameter accessor method on the task to trigger validation,
113
+ # then recursively validates all child parameters for nested parameter
114
+ # structures.
115
+ #
116
+ # @param task [CMDx::Task] The task instance to validate against
117
+ # @param parameter [CMDx::Parameter] The parameter to validate
118
+ # @return [void]
119
+ def recursive_validate!(task, parameter)
120
+ task.send(parameter.method_name)
121
+ parameter.children.each { |cp| recursive_validate!(task, cp) }
122
+ end
123
+
124
+ end
125
+ end
@@ -1,10 +1,93 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
+ # Parameter serialization utility for converting Parameter objects to hash representations.
5
+ #
6
+ # The ParameterSerializer module provides functionality to serialize Parameter
7
+ # instances into structured hash representations suitable for inspection,
8
+ # logging, debugging, and data interchange.
9
+ #
10
+ # @example Basic parameter serialization
11
+ # parameter = Parameter.new(:user_id, klass: Task, type: :integer, required: true)
12
+ # ParameterSerializer.call(parameter)
13
+ # # => {
14
+ # # source: :context,
15
+ # # name: :user_id,
16
+ # # type: :integer,
17
+ # # required: true,
18
+ # # options: {},
19
+ # # children: []
20
+ # # }
21
+ #
22
+ # @example Parameter with validation options
23
+ # parameter = Parameter.new(:email, klass: Task, type: :string,
24
+ # format: { with: /@/ }, presence: true)
25
+ # ParameterSerializer.call(parameter)
26
+ # # => {
27
+ # # source: :context,
28
+ # # name: :email,
29
+ # # type: :string,
30
+ # # required: false,
31
+ # # options: { format: { with: /@/ }, presence: true },
32
+ # # children: []
33
+ # # }
34
+ #
35
+ # @example Nested parameter serialization
36
+ # parent = Parameter.new(:address, klass: Task) do
37
+ # required :street, :city
38
+ # end
39
+ # ParameterSerializer.call(parent)
40
+ # # => {
41
+ # # source: :context,
42
+ # # name: :address,
43
+ # # type: :virtual,
44
+ # # required: false,
45
+ # # options: {},
46
+ # # children: [
47
+ # # { source: :address, name: :street, type: :virtual, required: true, options: {}, children: [] },
48
+ # # { source: :address, name: :city, type: :virtual, required: true, options: {}, children: [] }
49
+ # # ]
50
+ # # }
51
+ #
52
+ # @see CMDx::Parameter Parameter object creation and configuration
53
+ # @see CMDx::ParameterInspector Human-readable parameter formatting
4
54
  module ParameterSerializer
5
55
 
6
56
  module_function
7
57
 
58
+ # Converts a Parameter object to a hash representation.
59
+ #
60
+ # Serializes a Parameter instance into a structured hash containing
61
+ # all relevant parameter information including source, name, type,
62
+ # requirement status, options, and recursively serialized children.
63
+ #
64
+ # @param parameter [CMDx::Parameter] The parameter object to serialize
65
+ # @return [Hash] Structured hash representation of the parameter
66
+ #
67
+ # @example Simple parameter serialization
68
+ # param = Parameter.new(:age, klass: Task, type: :integer, required: true)
69
+ # ParameterSerializer.call(param)
70
+ # # => {
71
+ # # source: :context,
72
+ # # name: :age,
73
+ # # type: :integer,
74
+ # # required: true,
75
+ # # options: {},
76
+ # # children: []
77
+ # # }
78
+ #
79
+ # @example Parameter with custom source and options
80
+ # param = Parameter.new(:name, klass: Task, source: :user,
81
+ # type: :string, length: { min: 2 })
82
+ # ParameterSerializer.call(param)
83
+ # # => {
84
+ # # source: :user,
85
+ # # name: :name,
86
+ # # type: :string,
87
+ # # required: false,
88
+ # # options: { length: { min: 2 } },
89
+ # # children: []
90
+ # # }
8
91
  def call(parameter)
9
92
  {
10
93
  source: parameter.method_source,
@@ -1,10 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
+ # Parameter validation orchestration module for CMDx tasks.
5
+ #
6
+ # The ParameterValidator module provides high-level parameter validation
7
+ # coordination for task instances. It triggers validation of all task
8
+ # parameters and handles validation failure by setting task failure state
9
+ # with appropriate error messages.
10
+ #
11
+ # @example Basic parameter validation
12
+ # class ProcessOrderTask < CMDx::Task
13
+ # required :order_id, type: :integer
14
+ # required :email, type: :string, format: { with: /@/ }
15
+ # end
16
+ #
17
+ # task = ProcessOrderTask.new
18
+ # ParameterValidator.call(task) # Validates all parameters
19
+ # # If validation fails, task.failed? => true
20
+ #
21
+ # @example Validation with error handling
22
+ # task = ProcessOrderTask.new
23
+ # ParameterValidator.call(task)
24
+ #
25
+ # if task.failed?
26
+ # puts task.result.metadata[:reason] # => "order_id is a required parameter. email is invalid"
27
+ # puts task.errors.messages # => { order_id: ["is a required parameter"], email: ["is invalid"] }
28
+ # end
29
+ #
30
+ # @example Successful validation
31
+ # task = ProcessOrderTask.call(order_id: 123, email: "user@example.com")
32
+ # # ParameterValidator runs automatically and validation passes
33
+ # task.success? # => true
34
+ #
35
+ # @see CMDx::Parameters Parameter collection validation
36
+ # @see CMDx::Parameter Individual parameter definitions
37
+ # @see CMDx::Task Task execution and parameter integration
4
38
  module ParameterValidator
5
39
 
6
40
  module_function
7
41
 
42
+ # Validates all parameters for a task instance.
43
+ #
44
+ # Triggers validation of all task parameters through the Parameters collection.
45
+ # If any validation errors occur, sets the task to failed state with a
46
+ # comprehensive error message and detailed error information.
47
+ #
48
+ # @param task [CMDx::Task] The task instance to validate parameters for
49
+ # @return [void]
50
+ #
51
+ # @example Validating task parameters
52
+ # task = MyTask.new
53
+ # ParameterValidator.call(task)
54
+ #
55
+ # # If validation fails:
56
+ # task.failed? # => true
57
+ # task.result.metadata[:reason] # => "Combined error messages from all failed parameters"
58
+ # task.errors.empty? # => false
59
+ #
60
+ # @example Validation success
61
+ # task = MyTask.new # with valid parameters
62
+ # ParameterValidator.call(task)
63
+ #
64
+ # task.errors.empty? # => true
65
+ # # Task continues normal execution
66
+ #
67
+ # @note This method is typically called automatically during task execution
68
+ # before the main task logic runs, ensuring parameter validation occurs
69
+ # early in the task lifecycle.
8
70
  def call(task)
9
71
  task.class.cmd_parameters.validate!(task)
10
72
  return if task.errors.empty?
@@ -1,27 +1,95 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
+ # Parameter value resolution and processing class for CMDx tasks.
5
+ #
6
+ # The ParameterValue class handles the complete lifecycle of parameter value
7
+ # processing including source resolution, type coercion, validation, and error
8
+ # handling. It serves as the bridge between parameter definitions and their
9
+ # actual values during task execution.
10
+ #
11
+ # @example Basic parameter value processing
12
+ # task = ProcessOrderTask.new
13
+ # parameter = Parameter.new(:order_id, klass: ProcessOrderTask, type: :integer)
14
+ # value_processor = ParameterValue.new(task, parameter)
15
+ # processed_value = value_processor.call # Resolves, coerces, and validates
16
+ #
17
+ # @example Parameter value with validation
18
+ # parameter = Parameter.new(:email, klass: Task, type: :string,
19
+ # format: { with: /@/ }, presence: true)
20
+ # value_processor = ParameterValue.new(task, parameter)
21
+ # value_processor.call # Validates email format and presence
22
+ #
23
+ # @example Parameter value with default
24
+ # parameter = Parameter.new(:priority, klass: Task, default: "normal")
25
+ # value_processor = ParameterValue.new(task, parameter)
26
+ # value_processor.call # Returns "normal" if not provided
27
+ #
28
+ # @see CMDx::Parameter Parameter definition and configuration
29
+ # @see CMDx::Coercions Type coercion modules
30
+ # @see CMDx::Validators Parameter validation modules
4
31
  class ParameterValue
5
32
 
6
- __cmdx_attr_delegator :parent, :method_source, :name, :options, :required?, :optional?, :type, to: :parameter, private: true
33
+ __cmdx_attr_delegator :parent, :method_source, :name, :options, :required?, :optional?, :type,
34
+ to: :parameter,
35
+ private: true
7
36
 
37
+ # @return [CMDx::Task] The task instance being processed
38
+ # @return [CMDx::Parameter] The parameter definition being processed
8
39
  attr_reader :task, :parameter
9
40
 
41
+ # Initializes a new ParameterValue processor.
42
+ #
43
+ # Creates a parameter value processor for resolving, coercing, and validating
44
+ # a specific parameter value within the context of a task instance.
45
+ #
46
+ # @param task [CMDx::Task] The task instance containing the parameter source
47
+ # @param parameter [CMDx::Parameter] The parameter definition to process
48
+ #
49
+ # @example Creating a parameter value processor
50
+ # processor = ParameterValue.new(task_instance, parameter_definition)
10
51
  def initialize(task, parameter)
11
52
  @task = task
12
53
  @parameter = parameter
13
54
  end
14
55
 
56
+ # Processes the parameter value through coercion and validation.
57
+ #
58
+ # Executes the complete parameter value processing pipeline:
59
+ # 1. Resolves the raw value from the source
60
+ # 2. Applies type coercion based on parameter type
61
+ # 3. Runs all configured validations
62
+ # 4. Returns the final processed value
63
+ #
64
+ # @return [Object] The processed and validated parameter value
65
+ # @raise [CoercionError] If type coercion fails
66
+ # @raise [ValidationError] If validation fails
67
+ #
68
+ # @example Processing a simple parameter
69
+ # processor.call # => 42 (after coercion and validation)
70
+ #
71
+ # @example Processing with validation failure
72
+ # processor.call # => raises ValidationError: "is not valid"
15
73
  def call
16
74
  coerce!.tap { validate! }
17
75
  end
18
76
 
19
77
  private
20
78
 
79
+ # Checks if the parameter source method is defined on the task.
80
+ #
81
+ # @return [Boolean] true if source method exists, false otherwise
21
82
  def source_defined?
22
83
  task.respond_to?(method_source, true) || task.__cmdx_try(method_source)
23
84
  end
24
85
 
86
+ # Resolves the source object that contains the parameter value.
87
+ #
88
+ # Gets the source object by calling the method_source on the task instance.
89
+ # Raises ValidationError if the source method is not defined.
90
+ #
91
+ # @return [Object] The source object containing parameter values
92
+ # @raise [ValidationError] If source method is undefined
25
93
  def source
26
94
  return @source if defined?(@source)
27
95
 
@@ -36,18 +104,31 @@ module CMDx
36
104
  @source = task.__cmdx_try(method_source)
37
105
  end
38
106
 
107
+ # Checks if the source object has the parameter value.
108
+ #
109
+ # @return [Boolean] true if source responds to parameter name, false otherwise
39
110
  def source_value?
40
111
  return false if source.nil?
41
112
 
42
113
  source.__cmdx_respond_to?(name, true)
43
114
  end
44
115
 
116
+ # Checks if a required parameter value is missing from the source.
117
+ #
118
+ # @return [Boolean] true if required parameter is missing, false otherwise
45
119
  def source_value_required?
46
120
  return false if parent&.optional? && source.nil?
47
121
 
48
122
  required? && !source_value?
49
123
  end
50
124
 
125
+ # Resolves the raw parameter value from the source.
126
+ #
127
+ # Gets the parameter value from the source object, handling required
128
+ # parameter validation and default value resolution.
129
+ #
130
+ # @return [Object] The raw parameter value
131
+ # @raise [ValidationError] If required parameter is missing
51
132
  def value
52
133
  return @value if defined?(@value)
53
134
 
@@ -64,6 +145,14 @@ module CMDx
64
145
  @value = task.__cmdx_yield(options[:default])
65
146
  end
66
147
 
148
+ # Applies type coercion to the parameter value.
149
+ #
150
+ # Attempts to coerce the value to each specified type in order,
151
+ # supporting multiple type fallbacks for flexible coercion.
152
+ #
153
+ # @return [Object] The coerced parameter value
154
+ # @raise [CoercionError] If all coercion attempts fail
155
+ # @raise [UnknownCoercionError] If an unknown type is specified
67
156
  def coerce!
68
157
  types = Array(type)
69
158
  tsize = types.size - 1
@@ -99,20 +188,39 @@ module CMDx
99
188
  end
100
189
  end
101
190
 
191
+ # Checks if validations should be skipped for optional missing arguments.
192
+ #
193
+ # @return [Boolean] true if validations should be skipped, false otherwise
102
194
  def skip_validations_due_to_optional_missing_argument?
103
195
  optional? && value.nil? && !source.nil? && !source.__cmdx_respond_to?(name, true)
104
196
  end
105
197
 
198
+ # Checks if a specific validator should be skipped due to conditional logic.
199
+ #
200
+ # @param key [Symbol] The validator key to check
201
+ # @return [Boolean] true if validator should be skipped, false otherwise
106
202
  def skip_validator_due_to_conditional?(key)
107
203
  opts = options[key]
108
204
  opts.is_a?(Hash) && !task.__cmdx_eval(opts)
109
205
  end
110
206
 
207
+ # Checks if a specific validator should be skipped due to allow_nil option.
208
+ #
209
+ # @param key [Symbol] The validator key to check
210
+ # @return [Boolean] true if validator should be skipped, false otherwise
111
211
  def skip_validator_due_to_allow_nil?(key)
112
212
  opts = options[key]
113
213
  opts.is_a?(Hash) && opts[:allow_nil] && value.nil?
114
214
  end
115
215
 
216
+ # Runs all configured validations on the parameter value.
217
+ #
218
+ # Iterates through all validation options and applies the appropriate
219
+ # validators, respecting skip conditions for optional parameters,
220
+ # conditional validations, and allow_nil settings.
221
+ #
222
+ # @return [void]
223
+ # @raise [ValidationError] If any validation fails
116
224
  def validate!
117
225
  return if skip_validations_due_to_optional_missing_argument?
118
226
 
@@ -1,10 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
+ # Parameter collection inspection utility for generating human-readable descriptions.
5
+ #
6
+ # The ParametersInspector module provides functionality to convert collections
7
+ # of parameters into formatted, human-readable string representations. It
8
+ # coordinates with ParameterInspector to format individual parameters and
9
+ # combines them into a cohesive multi-parameter description.
10
+ #
11
+ # @example Basic parameters collection inspection
12
+ # parameter_registry = ParameterRegistry.new
13
+ # parameter_registry << Parameter.new(:user_id, klass: Task, type: :integer, required: true)
14
+ # parameter_registry << Parameter.new(:email, klass: Task, type: :string, required: false)
15
+ #
16
+ # ParametersInspector.call(parameter_registry)
17
+ # # => "Parameter: name=user_id type=integer source=context required=true options={}
18
+ # # Parameter: name=email type=string source=context required=false options={}"
19
+ #
20
+ # @example Empty parameters collection
21
+ # empty_parameter_registry = ParameterRegistry.new
22
+ # ParametersInspector.call(empty_parameter_registry)
23
+ # # => ""
24
+ #
25
+ # @example Parameters with validation options
26
+ # parameter_registry = ParameterRegistry.new
27
+ # parameter_registry << Parameter.new(:age, klass: Task, type: :integer,
28
+ # numeric: { within: 18..120 }, required: true)
29
+ # parameter_registry << Parameter.new(:website, klass: Task, type: :string,
30
+ # format: { with: /^https?:\/\// }, required: false)
31
+ #
32
+ # ParametersInspector.call(parameter_registry)
33
+ # # => "Parameter: name=age type=integer source=context required=true options={numeric: {within: 18..120}}
34
+ # # Parameter: name=website type=string source=context required=false options={format: {with: /^https?:\/\//}}"
35
+ #
36
+ # @see CMDx::ParameterRegistry Parameter collection management
37
+ # @see CMDx::ParameterInspector Individual parameter inspection
38
+ # @see CMDx::Parameter Parameter definition and configuration
4
39
  module ParametersInspector
5
40
 
6
41
  module_function
7
42
 
43
+ # Converts a Parameters collection to a human-readable string representation.
44
+ #
45
+ # Iterates through all parameters in the collection and formats each one
46
+ # using ParameterInspector, then joins them with newlines to create a
47
+ # comprehensive multi-parameter description.
48
+ #
49
+ # @param parameters [CMDx::ParameterRegistry] The parameters collection to inspect
50
+ # @return [String] Multi-line formatted parameter descriptions
51
+ #
52
+ # @example Inspecting multiple parameters
53
+ # ParametersInspector.call(parameters_collection)
54
+ # # => "Parameter: name=user_id type=integer source=context required=true
55
+ # # Parameter: name=email type=string source=context required=false
56
+ # # Parameter: name=age type=integer source=context required=true"
57
+ #
58
+ # @example Inspecting empty collection
59
+ # ParametersInspector.call(ParameterRegistry.new)
60
+ # # => ""
61
+ #
62
+ # @example Inspecting single parameter collection
63
+ # single_param_collection = ParameterRegistry.new
64
+ # single_param_collection << Parameter.new(:name, klass: Task)
65
+ # ParametersInspector.call(single_param_collection)
66
+ # # => "Parameter: name=name type=virtual source=context required=false options={}"
8
67
  def call(parameters)
9
68
  parameters.map(&:to_s).join("\n")
10
69
  end
@@ -1,10 +1,112 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
+ # Parameter collection serialization utility for converting Parameters to hash arrays.
5
+ #
6
+ # The ParametersSerializer module provides functionality to serialize collections
7
+ # of Parameter instances into structured array representations. Each parameter
8
+ # in the collection is converted to its hash representation, creating a
9
+ # comprehensive data structure suitable for inspection, logging, and data interchange.
10
+ #
11
+ # @example Basic parameters collection serialization
12
+ # parameter_registry = ParameterRegistry.new
13
+ # parameter_registry << Parameter.new(:user_id, klass: Task, type: :integer, required: true)
14
+ # parameters << Parameter.new(:email, klass: Task, type: :string, required: false)
15
+ #
16
+ # ParametersSerializer.call(parameters)
17
+ # # => [
18
+ # # {
19
+ # # source: :context,
20
+ # # name: :user_id,
21
+ # # type: :integer,
22
+ # # required: true,
23
+ # # options: {},
24
+ # # children: []
25
+ # # },
26
+ # # {
27
+ # # source: :context,
28
+ # # name: :email,
29
+ # # type: :string,
30
+ # # required: false,
31
+ # # options: {},
32
+ # # children: []
33
+ # # }
34
+ # # ]
35
+ #
36
+ # @example Empty parameters collection
37
+ # empty_parameter_registry = ParameterRegistry.new
38
+ # ParametersSerializer.call(empty_parameter_registry)
39
+ # # => []
40
+ #
41
+ # @example Parameters with validation and nested structures
42
+ # parameter_registry = ParameterRegistry.new
43
+ # parameter_registry << Parameter.new(:age, klass: Task, type: :integer,
44
+ # numeric: { within: 18..120 }, required: true)
45
+ #
46
+ # address_param = Parameter.new(:address, klass: Task) do
47
+ # required :street, :city
48
+ # optional :apartment
49
+ # end
50
+ # parameter_registry << address_param
51
+ #
52
+ # ParametersSerializer.call(parameter_registry)
53
+ # # => [
54
+ # # {
55
+ # # source: :context,
56
+ # # name: :age,
57
+ # # type: :integer,
58
+ # # required: true,
59
+ # # options: { numeric: { within: 18..120 } },
60
+ # # children: []
61
+ # # },
62
+ # # {
63
+ # # source: :context,
64
+ # # name: :address,
65
+ # # type: :virtual,
66
+ # # required: false,
67
+ # # options: {},
68
+ # # children: [
69
+ # # { source: :address, name: :street, type: :virtual, required: true, options: {}, children: [] },
70
+ # # { source: :address, name: :city, type: :virtual, required: true, options: {}, children: [] },
71
+ # # { source: :address, name: :apartment, type: :virtual, required: false, options: {}, children: [] }
72
+ # # ]
73
+ # # }
74
+ # # ]
75
+ #
76
+ # @see CMDx::ParameterRegistry Parameter collection management
77
+ # @see CMDx::ParameterSerializer Individual parameter serialization
78
+ # @see CMDx::Parameter Parameter definition and configuration
4
79
  module ParametersSerializer
5
80
 
6
81
  module_function
7
82
 
83
+ # Converts a Parameters collection to an array of hash representations.
84
+ #
85
+ # Iterates through all parameters in the collection and converts each one
86
+ # to its hash representation using the Parameter#to_h method, which delegates
87
+ # to ParameterSerializer.
88
+ #
89
+ # @param parameters [CMDx::ParameterRegistry] The parameters collection to serialize
90
+ # @return [Array<Hash>] Array of serialized parameter data structures
91
+ #
92
+ # @example Serializing multiple parameters
93
+ # ParametersSerializer.call(parameters_collection)
94
+ # # => [
95
+ # # { source: :context, name: :user_id, type: :integer, required: true, options: {}, children: [] },
96
+ # # { source: :context, name: :email, type: :string, required: false, options: {}, children: [] }
97
+ # # ]
98
+ #
99
+ # @example Serializing empty collection
100
+ # ParametersSerializer.call(ParameterRegistry.new)
101
+ # # => []
102
+ #
103
+ # @example Serializing single parameter collection
104
+ # single_param_collection = ParameterRegistry.new
105
+ # single_param_collection << Parameter.new(:name, klass: Task, type: :string)
106
+ # ParametersSerializer.call(single_param_collection)
107
+ # # => [
108
+ # # { source: :context, name: :name, type: :string, required: false, options: {}, children: [] }
109
+ # # ]
8
110
  def call(parameters)
9
111
  parameters.map(&:to_h)
10
112
  end