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,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Parameter definition system for CMDx tasks.
4
+ # Parameter definition and management for task attribute configuration.
5
5
  #
6
- # This class manages parameter definitions including type coercion, validation,
7
- # and nested parameter structures. It handles the creation of accessor methods
8
- # on task classes and provides a flexible system for defining required and
9
- # optional parameters with various data types and validation rules.
6
+ # Parameter provides a flexible system for defining, validating, and managing
7
+ # task parameters with support for type coercion, nested parameter structures,
8
+ # validation rules, and dynamic attribute generation. Parameters can be defined
9
+ # as required or optional with various configuration options including custom
10
+ # naming, source specification, and child parameter definitions.
10
11
  class Parameter
11
12
 
12
13
  cmdx_attr_delegator :invalid?, :valid?,
@@ -36,22 +37,31 @@ module CMDx
36
37
  # @return [CMDx::Errors] Validation errors for this parameter
37
38
  attr_reader :errors
38
39
 
39
- # Creates a new parameter definition with the given name and options.
40
+ # Creates a new parameter definition with the specified configuration.
40
41
  #
41
- # @param name [Symbol] The parameter name
42
- # @param options [Hash] Configuration options for the parameter
43
- # @option options [Class] :klass The task class this parameter belongs to (required)
44
- # @option options [Parameter] :parent The parent parameter for nested parameters
45
- # @option options [Symbol, Array<Symbol>] :type (:virtual) The parameter type(s) for coercion
46
- # @option options [Boolean] :required (false) Whether the parameter is required
47
- # @param block [Proc] Optional block for defining nested parameters
48
- # @return [Parameter] The newly created parameter
49
- # @raise [KeyError] If the :klass option is not provided
42
+ # @param name [Symbol, String] the parameter name
43
+ # @param options [Hash] parameter configuration options
44
+ # @option options [Class] :klass the task class this parameter belongs to (required)
45
+ # @option options [Parameter] :parent the parent parameter for nested definitions
46
+ # @option options [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
47
+ # @option options [Boolean] :required whether the parameter is required for task execution
48
+ # @option options [Symbol] :source the source context for parameter resolution
49
+ # @option options [Symbol, String] :as custom method name for the parameter
50
+ # @option options [Hash] :validates validation rules to apply to the parameter
51
+ # @option options [Object] :default default value when parameter is not provided
52
+ # @param block [Proc] optional block for defining nested parameters
50
53
  #
51
- # @example Create a simple parameter
52
- # Parameter.new(:name, klass: MyTask, type: :string, required: true)
54
+ # @return [Parameter] a new parameter instance
53
55
  #
54
- # @example Create a parameter with nested children
56
+ # @raise [KeyError] if the :klass option is not provided
57
+ #
58
+ # @example Create a simple required parameter
59
+ # Parameter.new(:user_id, klass: MyTask, type: :integer, required: true)
60
+ #
61
+ # @example Create parameter with validation
62
+ # Parameter.new(:email, klass: MyTask, type: :string, validates: { format: /@/ })
63
+ #
64
+ # @example Create nested parameter with children
55
65
  # Parameter.new(:user, klass: MyTask, type: :hash) do
56
66
  # required :name, type: :string
57
67
  # optional :age, type: :integer
@@ -73,21 +83,32 @@ module CMDx
73
83
 
74
84
  class << self
75
85
 
76
- # Creates one or more optional parameters with the given names and options.
86
+ # Creates one or more optional parameter definitions.
87
+ #
88
+ # @param names [Array<Symbol>] parameter names to define as optional
89
+ # @param options [Hash] parameter configuration options
90
+ # @option options [Class] :klass the task class this parameter belongs to
91
+ # @option options [Parameter] :parent the parent parameter for nested definitions
92
+ # @option options [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
93
+ # @option options [Symbol] :source the source context for parameter resolution
94
+ # @option options [Symbol, String] :as custom method name (only allowed for single parameter)
95
+ # @option options [Hash] :validates validation rules to apply to the parameter
96
+ # @option options [Object] :default default value when parameter is not provided
97
+ # @param block [Proc] optional block for defining nested parameters
77
98
  #
78
- # @param names [Array<Symbol>] Parameter names to create
79
- # @param options [Hash] Configuration options for all parameters
80
- # @param block [Proc] Optional block for defining nested parameters
81
- # @return [Array<Parameter>] The created optional parameters
82
- # @raise [ArgumentError] If no parameters are given or :as option is used with multiple names
99
+ # @return [Array<Parameter>] array of created optional parameter instances
83
100
  #
84
- # @example Create multiple optional parameters
85
- # Parameter.optional(:name, :email, type: :string, klass: MyTask)
101
+ # @raise [ArgumentError] if no parameter names are provided
102
+ # @raise [ArgumentError] if :as option is used with multiple parameter names
86
103
  #
87
- # @example Create optional parameter with nested structure
88
- # Parameter.optional(:user, klass: MyTask, type: :hash) do
89
- # required :name, type: :string
90
- # end
104
+ # @example Define single optional parameter
105
+ # Parameter.optional(:description, klass: MyTask, type: :string)
106
+ #
107
+ # @example Define multiple optional parameters
108
+ # Parameter.optional(:name, :email, klass: MyTask, type: :string)
109
+ #
110
+ # @example Define optional parameter with custom name
111
+ # Parameter.optional(:user_id, klass: MyTask, type: :integer, as: :current_user_id)
91
112
  def optional(*names, **options, &)
92
113
  if names.none?
93
114
  raise ArgumentError, "no parameters given"
@@ -98,133 +119,174 @@ module CMDx
98
119
  names.filter_map { |n| new(n, **options, &) }
99
120
  end
100
121
 
101
- # Creates one or more required parameters with the given names and options.
122
+ # Creates one or more required parameter definitions.
123
+ #
124
+ # @param names [Array<Symbol>] parameter names to define as required
125
+ # @param options [Hash] parameter configuration options
126
+ # @option options [Class] :klass the task class this parameter belongs to
127
+ # @option options [Parameter] :parent the parent parameter for nested definitions
128
+ # @option options [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
129
+ # @option options [Symbol] :source the source context for parameter resolution
130
+ # @option options [Symbol, String] :as custom method name (only allowed for single parameter)
131
+ # @option options [Hash] :validates validation rules to apply to the parameter
132
+ # @option options [Object] :default default value when parameter is not provided
133
+ # @param block [Proc] optional block for defining nested parameters
134
+ #
135
+ # @return [Array<Parameter>] array of created required parameter instances
102
136
  #
103
- # @param names [Array<Symbol>] Parameter names to create
104
- # @param options [Hash] Configuration options for all parameters
105
- # @param block [Proc] Optional block for defining nested parameters
106
- # @return [Array<Parameter>] The created required parameters
107
- # @raise [ArgumentError] If no parameters are given or :as option is used with multiple names
137
+ # @raise [ArgumentError] if no parameter names are provided
138
+ # @raise [ArgumentError] if :as option is used with multiple parameter names
108
139
  #
109
- # @example Create multiple required parameters
110
- # Parameter.required(:name, :email, type: :string, klass: MyTask)
140
+ # @example Define single required parameter
141
+ # Parameter.required(:user_id, klass: MyTask, type: :integer)
111
142
  #
112
- # @example Create required parameter with validation
113
- # Parameter.required(:age, type: :integer, validate: { numeric: { greater_than: 0 } }, klass: MyTask)
143
+ # @example Define multiple required parameters
144
+ # Parameter.required(:name, :email, klass: MyTask, type: :string)
145
+ #
146
+ # @example Define required parameter with validation
147
+ # Parameter.required(:email, klass: MyTask, type: :string, validates: { format: /@/ })
114
148
  def required(*names, **options, &)
115
149
  optional(*names, **options.merge(required: true), &)
116
150
  end
117
151
 
118
152
  end
119
153
 
120
- # Creates one or more optional child parameters under this parameter.
154
+ # Defines optional child parameters for nested parameter structures.
121
155
  #
122
- # @param names [Array<Symbol>] Parameter names to create
123
- # @param options [Hash] Configuration options for all parameters
124
- # @param block [Proc] Optional block for defining nested parameters
125
- # @return [Array<Parameter>] The created optional child parameters
156
+ # @param names [Array<Symbol>] parameter names to define as optional children
157
+ # @param options [Hash] parameter configuration options
158
+ # @option options [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
159
+ # @option options [Symbol] :source the source context for parameter resolution
160
+ # @option options [Symbol, String] :as custom method name (only allowed for single parameter)
161
+ # @option options [Hash] :validates validation rules to apply to the parameter
162
+ # @option options [Object] :default default value when parameter is not provided
163
+ # @param block [Proc] optional block for defining nested parameters
126
164
  #
127
- # @example Add optional child parameters
128
- # user_param.optional(:nickname, :bio, type: :string)
165
+ # @return [Array<Parameter>] array of created optional child parameter instances
129
166
  #
130
- # @example Add optional child with further nesting
131
- # user_param.optional(:preferences, type: :hash) do
132
- # required :theme, type: :string
133
- # end
167
+ # @raise [ArgumentError] if no parameter names are provided
168
+ # @raise [ArgumentError] if :as option is used with multiple parameter names
169
+ #
170
+ # @example Define optional child parameters
171
+ # user_param = Parameter.new(:user, klass: MyTask, type: :hash)
172
+ # user_param.optional(:description, :bio, type: :string)
134
173
  def optional(*names, **options, &)
135
174
  parameters = Parameter.optional(*names, **options.merge(klass: @klass, parent: self), &)
136
175
  children.concat(parameters)
137
176
  end
138
177
 
139
- # Creates one or more required child parameters under this parameter.
178
+ # Defines required child parameters for nested parameter structures.
179
+ #
180
+ # @param names [Array<Symbol>] parameter names to define as required children
181
+ # @param options [Hash] parameter configuration options
182
+ # @option options [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
183
+ # @option options [Symbol] :source the source context for parameter resolution
184
+ # @option options [Symbol, String] :as custom method name (only allowed for single parameter)
185
+ # @option options [Hash] :validates validation rules to apply to the parameter
186
+ # @option options [Object] :default default value when parameter is not provided
187
+ # @param block [Proc] optional block for defining nested parameters
140
188
  #
141
- # @param names [Array<Symbol>] Parameter names to create
142
- # @param options [Hash] Configuration options for all parameters
143
- # @param block [Proc] Optional block for defining nested parameters
144
- # @return [Array<Parameter>] The created required child parameters
189
+ # @return [Array<Parameter>] array of created required child parameter instances
145
190
  #
146
- # @example Add required child parameters
147
- # user_param.required(:first_name, :last_name, type: :string)
191
+ # @raise [ArgumentError] if no parameter names are provided
192
+ # @raise [ArgumentError] if :as option is used with multiple parameter names
148
193
  #
149
- # @example Add required child with validation
150
- # user_param.required(:email, type: :string, validate: { format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i } })
194
+ # @example Define required child parameters
195
+ # user_param = Parameter.new(:user, klass: MyTask, type: :hash)
196
+ # user_param.required(:name, :email, type: :string)
151
197
  def required(*names, **options, &)
152
198
  parameters = Parameter.required(*names, **options.merge(klass: @klass, parent: self), &)
153
199
  children.concat(parameters)
154
200
  end
155
201
 
156
- # Checks if this parameter is required.
202
+ # Checks if the parameter is marked as required for task execution.
157
203
  #
158
- # @return [Boolean] True if the parameter is required, false otherwise
204
+ # @return [Boolean] true if the parameter is required, false otherwise
159
205
  #
160
206
  # @example Check if parameter is required
161
- # param.required? # => true
207
+ # param = Parameter.new(:name, klass: MyTask, required: true)
208
+ # param.required? #=> true
162
209
  def required?
163
210
  !!@required
164
211
  end
165
212
 
166
- # Checks if this parameter is optional.
213
+ # Checks if the parameter is marked as optional for task execution.
167
214
  #
168
- # @return [Boolean] True if the parameter is optional, false otherwise
215
+ # @return [Boolean] true if the parameter is optional, false otherwise
169
216
  #
170
217
  # @example Check if parameter is optional
171
- # param.optional? # => false
218
+ # param = Parameter.new(:description, klass: MyTask, required: false)
219
+ # param.optional? #=> true
172
220
  def optional?
173
221
  !required?
174
222
  end
175
223
 
176
- # Gets the method name that will be used to access this parameter's value.
224
+ # Generates the method name that will be created on the task class for this parameter.
177
225
  #
178
- # @return [Symbol] The method name for accessing this parameter
226
+ # @return [Symbol] the method name with any configured prefix, suffix, or custom naming
179
227
  #
180
- # @example Get method name
181
- # param.method_name # => :user_name
228
+ # @example Get method name for simple parameter
229
+ # param = Parameter.new(:user_id, klass: MyTask)
230
+ # param.method_name #=> :user_id
231
+ #
232
+ # @example Get method name with custom naming
233
+ # param = Parameter.new(:user_id, klass: MyTask, as: :current_user_id)
234
+ # param.method_name #=> :current_user_id
182
235
  def method_name
183
236
  @method_name ||= Utils::NameAffix.call(name, method_source, options)
184
237
  end
185
238
 
186
- # Gets the source object from which this parameter's value will be retrieved.
239
+ # Determines the source context for parameter resolution and method name generation.
240
+ #
241
+ # @return [Symbol] the source identifier used for parameter resolution
187
242
  #
188
- # @return [Symbol] The method source (:context by default, or parent's method_name)
243
+ # @example Get method source for simple parameter
244
+ # param = Parameter.new(:user_id, klass: MyTask)
245
+ # param.method_source #=> :context
189
246
  #
190
- # @example Get method source
191
- # param.method_source # => :context
247
+ # @example Get method source for nested parameter
248
+ # parent = Parameter.new(:user, klass: MyTask)
249
+ # child = Parameter.new(:name, klass: MyTask, parent: parent)
250
+ # child.method_source #=> :user
192
251
  def method_source
193
252
  @method_source ||= options[:source] || parent&.method_name || :context
194
253
  end
195
254
 
196
- # Converts the parameter to a hash representation.
255
+ # Converts the parameter to a hash representation for serialization.
197
256
  #
198
- # @return [Hash] A hash representation of the parameter
257
+ # @return [Hash] hash containing all parameter metadata and configuration
199
258
  #
200
- # @example Convert to hash
201
- # param.to_h # => { name: :user_name, type: :string, required: true, ... }
259
+ # @example Convert parameter to hash
260
+ # param = Parameter.new(:user_id, klass: MyTask, type: :integer, required: true)
261
+ # param.to_h
262
+ # #=> { name: :user_id, type: :integer, required: true, ... }
202
263
  def to_h
203
264
  ParameterSerializer.call(self)
204
265
  end
205
266
 
206
- # Converts the parameter to a string representation.
267
+ # Converts the parameter to a formatted string representation for inspection.
207
268
  #
208
- # @return [String] A string representation of the parameter
269
+ # @return [String] human-readable string representation of the parameter
209
270
  #
210
- # @example Convert to string
211
- # param.to_s # => "Parameter(name: user_name, type: string, required: true)"
271
+ # @example Convert parameter to string
272
+ # param = Parameter.new(:user_id, klass: MyTask, type: :integer, required: true)
273
+ # param.to_s
274
+ # #=> "Parameter: name=user_id type=integer required=true ..."
212
275
  def to_s
213
276
  ParameterInspector.call(to_h)
214
277
  end
215
278
 
216
279
  private
217
280
 
218
- # Defines the attribute accessor method for this parameter on the task class.
219
- # The method handles parameter value retrieval, coercion, and validation.
281
+ # Dynamically defines a method on the task class for parameter value access.
282
+ #
283
+ # @param parameter [Parameter] the parameter to create a method for
220
284
  #
221
- # @param parameter [Parameter] The parameter to define the method for
222
285
  # @return [void]
223
- # @raise [CoercionError] If parameter value cannot be coerced to the expected type
224
- # @raise [ValidationError] If parameter value fails validation
225
286
  #
226
- # @example Define parameter method (internal use)
227
- # define_attribute(param) # Defines a private method on the task class
287
+ # @example Define parameter method on task class
288
+ # # Creates a private method that evaluates and caches parameter values
289
+ # # with automatic error handling for coercion and validation failures
228
290
  def define_attribute(parameter)
229
291
  klass.send(:define_method, parameter.method_name) do
230
292
  @cmd_parameter_value_cache ||= {}
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Parameter inspection and formatting utilities for readable parameter representation.
4
+ # Provides formatted inspection and display functionality for parameter objects.
5
5
  #
6
- # This module provides functionality to format parameter metadata into human-readable
7
- # strings for debugging, logging, and introspection purposes. It handles nested
8
- # parameter structures with proper indentation and displays essential parameter
9
- # information in a structured format.
6
+ # This module formats parameter information into human-readable string representations,
7
+ # including nested parameter structures with proper indentation. It processes parameter
8
+ # hashes in a consistent order and handles child parameter relationships for complex
9
+ # parameter hierarchies.
10
10
  module ParameterInspector
11
11
 
12
12
  ORDERED_KEYS = %i[
@@ -15,30 +15,43 @@ module CMDx
15
15
 
16
16
  module_function
17
17
 
18
- # Formats a parameter hash into a human-readable string representation.
18
+ # Formats a parameter hash into a human-readable inspection string.
19
19
  #
20
- # This method converts parameter metadata into a structured string format
21
- # that displays key parameter information in a consistent order. For parameters
22
- # with nested children, it recursively formats child parameters with proper
23
- # indentation to show the hierarchical structure.
20
+ # Creates a formatted string representation of parameter information,
21
+ # displaying attributes in a consistent order with proper indentation
22
+ # for nested child parameters. The method recursively processes child
23
+ # parameters with increased indentation depth for visual hierarchy.
24
24
  #
25
25
  # @param parameter [Hash] the parameter hash to format
26
- # @param depth [Integer] the current nesting depth for indentation (default: 1)
26
+ # @option parameter [Symbol, String] :name the parameter name
27
+ # @option parameter [Symbol, Array<Symbol>] :type the parameter type(s)
28
+ # @option parameter [Symbol] :source the parameter source context
29
+ # @option parameter [Boolean] :required whether the parameter is required
30
+ # @option parameter [Hash] :options additional parameter configuration options
31
+ # @option parameter [Array<Hash>] :children nested child parameter definitions
32
+ # @param depth [Integer] the indentation depth for nested parameters (defaults to 1)
27
33
  #
28
- # @return [String] a formatted string representation of the parameter
34
+ # @return [String] formatted multi-line string representation of the parameter
29
35
  #
30
- # @example Format a parameter with nested children
31
- # param = {
32
- # name: :user, type: :hash, source: :context, required: false, options: {},
36
+ # @example Format a simple parameter
37
+ # parameter = { name: :user_id, type: :integer, required: true }
38
+ # ParameterInspector.call(parameter)
39
+ # #=> "Parameter: name=user_id type=integer required=true"
40
+ #
41
+ # @example Format a parameter with children
42
+ # parameter = {
43
+ # name: :payment,
44
+ # type: :hash,
45
+ # required: true,
33
46
  # children: [
34
- # { name: :name, type: :string, source: :user, required: true, options: {}, children: [] },
35
- # { name: :age, type: :integer, source: :user, required: false, options: {}, children: [] }
47
+ # { name: :amount, type: :big_decimal, required: true },
48
+ # { name: :currency, type: :string, required: true }
36
49
  # ]
37
50
  # }
38
- # ParameterInspector.call(param)
39
- # # => "Parameter: name=user type=hash source=context required=false options={}
40
- # # ↳ Parameter: name=name type=string source=user required=true options={}
41
- # # ↳ Parameter: name=age type=integer source=user required=false options={}"
51
+ # ParameterInspector.call(parameter)
52
+ # #=> "Parameter: name=payment type=hash required=true
53
+ # # ↳ Parameter: name=amount type=big_decimal required=true
54
+ # # ↳ Parameter: name=currency type=string required=true"
42
55
  def call(parameter, depth = 1)
43
56
  ORDERED_KEYS.filter_map do |key|
44
57
  value = parameter[key]
@@ -1,34 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Registry for managing parameter definitions and validation within tasks.
4
+ # Registry for managing parameter definitions within tasks.
5
5
  #
6
- # This registry handles the storage and validation of parameter definitions,
7
- # including nested parameter structures and recursive validation logic.
6
+ # This registry maintains a collection of parameter definitions and provides
7
+ # validation functionality to ensure all parameters are properly configured
8
+ # and accessible on their associated tasks. It supports both flat and nested
9
+ # parameter structures through recursive validation.
8
10
  class ParameterRegistry
9
11
 
10
- # The internal array storing parameter definitions.
11
- #
12
- # @return [Array] array containing parameter definition objects
12
+ # @return [Array<Parameter>] array containing parameter definition objects
13
13
  attr_reader :registry
14
14
 
15
- # Initializes a new parameter registry.
15
+ # Initializes a new parameter registry with an empty parameter collection.
16
16
  #
17
17
  # @return [ParameterRegistry] a new parameter registry instance
18
18
  #
19
- # @example Creating an empty registry
20
- # ParameterRegistry.new
19
+ # @example Creating a new registry
20
+ # registry = ParameterRegistry.new
21
+ # registry.registry #=> []
21
22
  def initialize
22
23
  @registry = []
23
24
  end
24
25
 
25
- # Creates a deep copy of the parameter registry.
26
+ # Creates a duplicate of the parameter registry with deep-copied parameters.
27
+ #
28
+ # This method creates a new registry instance with duplicated parameter
29
+ # definitions, ensuring changes to the duplicate don't affect the original.
26
30
  #
27
31
  # @return [ParameterRegistry] a new registry instance with duplicated parameters
28
32
  #
29
- # @example Duplicating a registry
33
+ # @example Duplicate a registry
30
34
  # original = ParameterRegistry.new
31
- # copy = original.dup
35
+ # duplicate = original.dup
36
+ # duplicate.object_id != original.object_id #=> true
32
37
  def dup
33
38
  new_registry = self.class.new
34
39
  new_registry.instance_variable_set(:@registry, registry.map(&:dup))
@@ -39,43 +44,45 @@ module CMDx
39
44
  #
40
45
  # @return [Boolean] true if all parameters are valid, false otherwise
41
46
  #
42
- # @example Checking registry validity
43
- # registry.valid?
44
- # # => true
47
+ # @example Check registry validity
48
+ # registry.valid? #=> true
45
49
  def valid?
46
50
  registry.all?(&:valid?)
47
51
  end
48
52
 
49
53
  # Validates all parameters in the registry against a task instance.
50
54
  #
55
+ # This method ensures that each parameter is properly defined and accessible
56
+ # on the provided task, including nested parameters through recursive validation.
57
+ #
51
58
  # @param task [Task] the task instance to validate parameters against
52
59
  #
53
60
  # @return [void]
54
61
  #
55
- # @example Validating parameters
56
- # registry.validate!(task)
62
+ # @raise [NoMethodError] if a parameter method is not defined on the task
63
+ #
64
+ # @example Validate parameters against a task
65
+ # registry.validate!(task_instance)
57
66
  def validate!(task)
58
67
  registry.each { |p| recursive_validate!(task, p) }
59
68
  end
60
69
 
61
- # Returns a hash representation of the registry.
70
+ # Converts the parameter registry to a hash representation.
62
71
  #
63
- # @return [Hash] serialized hash representation of all parameters
72
+ # @return [Array<Hash>] array of parameter hash representations
64
73
  #
65
- # @example Getting registry hash
66
- # registry.to_h
67
- # # => { name: { type: :string, required: true }, age: { type: :integer } }
74
+ # @example Convert registry to hash
75
+ # registry.to_h #=> [{name: :user_id, type: :integer}, {name: :email, type: :string}]
68
76
  def to_h
69
77
  registry.map(&:to_h)
70
78
  end
71
79
 
72
- # Returns a string representation of the registry.
80
+ # Converts the parameter registry to a string representation.
73
81
  #
74
- # @return [String] formatted string representation of all parameters
82
+ # @return [String] string representation of all parameters, joined by newlines
75
83
  #
76
- # @example Getting registry string
77
- # registry.to_s
78
- # # => "name (string, required), age (integer)"
84
+ # @example Convert registry to string
85
+ # registry.to_s #=> "user_id: integer\nemail: string"
79
86
  def to_s
80
87
  registry.map(&:to_s).join("\n")
81
88
  end
@@ -84,13 +91,12 @@ module CMDx
84
91
 
85
92
  # Recursively validates a parameter and its children against a task.
86
93
  #
87
- # @param task [Task] the task instance to validate against
94
+ # @param task [Task] the task instance to validate the parameter against
88
95
  # @param parameter [Parameter] the parameter to validate
89
96
  #
90
97
  # @return [void]
91
98
  #
92
- # @example Recursive validation (internal use)
93
- # recursive_validate!(task, parameter)
99
+ # @raise [NoMethodError] if the parameter method is not defined on the task
94
100
  def recursive_validate!(task, parameter)
95
101
  task.send(parameter.method_name) # Make sure parameter is defined on task
96
102
  parameter.children.each { |child| recursive_validate!(task, child) }
@@ -1,32 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Parameter serialization module for converting parameter objects to hash format.
4
+ # Parameter serialization utilities for converting parameter objects to hash representations.
5
5
  #
6
- # This module provides functionality to serialize parameter objects into a
7
- # standardized hash representation that includes essential metadata about
8
- # the parameter such as its source, name, type, required status, options,
9
- # and child parameters. The serialized format is commonly used for debugging,
10
- # logging, and introspection purposes.
6
+ # ParameterSerializer provides functionality to convert parameter definition objects
7
+ # into structured hash format for serialization, introspection, and data exchange.
8
+ # It extracts essential parameter metadata including source context, method names,
9
+ # type information, requirement status, options, and nested child parameters.
11
10
  module ParameterSerializer
12
11
 
13
12
  module_function
14
13
 
15
- # Serializes a parameter object into a hash representation.
14
+ # Converts a parameter object into a hash representation for serialization.
16
15
  #
17
- # @param parameter [Parameter] the parameter object to serialize
16
+ # This method extracts key metadata from a parameter definition and structures
17
+ # it into a hash format suitable for serialization, storage, or transmission.
18
+ # Child parameters are recursively serialized to maintain nested structure.
18
19
  #
19
- # @return [Hash] a hash containing the parameter's metadata
20
+ # @param parameter [CMDx::Parameter] the parameter object to serialize
20
21
  #
21
- # @raise [NoMethodError] if the parameter doesn't respond to required methods
22
+ # @return [Hash] a hash containing the parameter's metadata and configuration
23
+ # @option return [Symbol] :source the source context for parameter resolution
24
+ # @option return [Symbol] :name the method name generated for this parameter
25
+ # @option return [Symbol, Array<Symbol>] :type the parameter type(s) for coercion
26
+ # @option return [Boolean] :required whether the parameter is required for execution
27
+ # @option return [Hash] :options the parameter configuration options
28
+ # @option return [Array<Hash>] :children serialized child parameters for nested structures
22
29
  #
23
- # @example Serialize a parameter with nested children
24
- # param = Parameter.new(:user, klass: MyTask, type: :hash) do
30
+ # @example Serialize a nested parameter with children
31
+ # user_param = Parameter.new(:user, klass: MyTask, type: :hash) do
25
32
  # required :name, type: :string
26
33
  # optional :age, type: :integer
27
34
  # end
28
- # ParameterSerializer.call(param)
29
- # # => {
35
+ # ParameterSerializer.call(user_param)
36
+ # #=> {
30
37
  # # source: :context,
31
38
  # # name: :user,
32
39
  # # type: :hash,