cmdx 1.0.1 → 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.
- checksums.yaml +4 -4
- data/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
@@ -0,0 +1,231 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Parameter evaluation system for task execution context.
|
5
|
+
#
|
6
|
+
# ParameterEvaluator processes parameter definitions by extracting values from
|
7
|
+
# task context sources, applying type coercions, performing validations, and
|
8
|
+
# handling optional parameters with default values. It ensures parameter values
|
9
|
+
# meet the requirements defined in parameter specifications before task execution.
|
10
|
+
class ParameterEvaluator
|
11
|
+
|
12
|
+
cmdx_attr_delegator :parent, :method_source, :name, :options, :required?, :optional?, :type,
|
13
|
+
to: :parameter,
|
14
|
+
private: true
|
15
|
+
|
16
|
+
# @return [CMDx::Task] The task instance being processed
|
17
|
+
attr_reader :task
|
18
|
+
|
19
|
+
# @return [CMDx::Parameter] The parameter definition being processed
|
20
|
+
attr_reader :parameter
|
21
|
+
|
22
|
+
# Creates a new parameter evaluator instance.
|
23
|
+
#
|
24
|
+
# @param task [CMDx::Task] the task instance containing parameter context
|
25
|
+
# @param parameter [CMDx::Parameter] the parameter definition to evaluate
|
26
|
+
#
|
27
|
+
# @example Create evaluator for a task parameter
|
28
|
+
# evaluator = ParameterEvaluator.new(task, parameter)
|
29
|
+
def initialize(task, parameter)
|
30
|
+
@task = task
|
31
|
+
@parameter = parameter
|
32
|
+
end
|
33
|
+
|
34
|
+
# Evaluates a parameter by creating a new evaluator instance and calling it.
|
35
|
+
#
|
36
|
+
# @param task [CMDx::Task] the task instance containing parameter context
|
37
|
+
# @param parameter [CMDx::Parameter] the parameter definition to evaluate
|
38
|
+
#
|
39
|
+
# @return [Object] the coerced and validated parameter value
|
40
|
+
#
|
41
|
+
# @raise [ValidationError] when parameter source is undefined or required parameter is missing
|
42
|
+
# @raise [CoercionError] when parameter value cannot be coerced to expected type
|
43
|
+
#
|
44
|
+
# @example Evaluate a parameter value
|
45
|
+
# value = ParameterEvaluator.call(task, parameter)
|
46
|
+
def self.call(task, parameter)
|
47
|
+
new(task, parameter).call
|
48
|
+
end
|
49
|
+
|
50
|
+
# Evaluates the parameter by applying coercion and validation.
|
51
|
+
#
|
52
|
+
# @return [Object] the coerced and validated parameter value
|
53
|
+
#
|
54
|
+
# @raise [ValidationError] when parameter source is undefined or required parameter is missing
|
55
|
+
# @raise [CoercionError] when parameter value cannot be coerced to expected type
|
56
|
+
#
|
57
|
+
# @example Evaluate parameter with coercion and validation
|
58
|
+
# evaluator = ParameterEvaluator.new(task, parameter)
|
59
|
+
# value = evaluator.call
|
60
|
+
def call
|
61
|
+
coerce!.tap { validate! }
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Checks if the parameter source method is defined on the task.
|
67
|
+
#
|
68
|
+
# @return [Boolean] true if the source method exists, false otherwise
|
69
|
+
#
|
70
|
+
# @example Check if parameter source is defined
|
71
|
+
# evaluator.send(:source_defined?) #=> true
|
72
|
+
def source_defined?
|
73
|
+
task.respond_to?(method_source, true) || task.cmdx_try(method_source)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Retrieves the parameter source object from the task.
|
77
|
+
#
|
78
|
+
# @return [Object] the source object containing parameter values
|
79
|
+
#
|
80
|
+
# @raise [ValidationError] when the source method is not defined on the task
|
81
|
+
#
|
82
|
+
# @example Get parameter source
|
83
|
+
# evaluator.send(:source) #=> #<Context:...>
|
84
|
+
def source
|
85
|
+
return @source if defined?(@source)
|
86
|
+
|
87
|
+
unless source_defined?
|
88
|
+
raise ValidationError, I18n.t(
|
89
|
+
"cmdx.parameters.undefined",
|
90
|
+
default: "delegates to undefined method #{method_source}",
|
91
|
+
source: method_source
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
@source = task.cmdx_try(method_source)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Checks if the parameter value exists in the source object.
|
99
|
+
#
|
100
|
+
# @return [Boolean] true if the parameter value exists, false otherwise
|
101
|
+
#
|
102
|
+
# @example Check if parameter value exists
|
103
|
+
# evaluator.send(:source_value?) #=> true
|
104
|
+
def source_value?
|
105
|
+
return false if source.nil?
|
106
|
+
|
107
|
+
source.cmdx_respond_to?(name, true)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Checks if a required parameter value is missing from the source.
|
111
|
+
#
|
112
|
+
# @return [Boolean] true if required parameter is missing, false otherwise
|
113
|
+
#
|
114
|
+
# @example Check if required parameter is missing
|
115
|
+
# evaluator.send(:source_value_required?) #=> false
|
116
|
+
def source_value_required?
|
117
|
+
return false if parent&.optional? && source.nil?
|
118
|
+
|
119
|
+
required? && !source_value?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Extracts the parameter value from the source with default handling.
|
123
|
+
#
|
124
|
+
# @return [Object] the parameter value or default value
|
125
|
+
#
|
126
|
+
# @raise [ValidationError] when a required parameter is missing
|
127
|
+
#
|
128
|
+
# @example Get parameter value with default
|
129
|
+
# evaluator.send(:value) #=> "default_value"
|
130
|
+
def value
|
131
|
+
return @value if defined?(@value)
|
132
|
+
|
133
|
+
if source_value_required?
|
134
|
+
raise ValidationError, I18n.t(
|
135
|
+
"cmdx.parameters.required",
|
136
|
+
default: "is a required parameter"
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
@value = source.cmdx_try(name)
|
141
|
+
return @value unless @value.nil? && options.key?(:default)
|
142
|
+
|
143
|
+
@value = task.cmdx_yield(options[:default])
|
144
|
+
end
|
145
|
+
|
146
|
+
# Applies type coercion to the parameter value.
|
147
|
+
#
|
148
|
+
# @return [Object] the coerced parameter value
|
149
|
+
#
|
150
|
+
# @raise [CoercionError] when value cannot be coerced to expected type
|
151
|
+
#
|
152
|
+
# @example Coerce parameter value
|
153
|
+
# evaluator.send(:coerce!) #=> 42
|
154
|
+
def coerce!
|
155
|
+
types = Array(type)
|
156
|
+
tsize = types.size - 1
|
157
|
+
|
158
|
+
types.each_with_index do |key, i|
|
159
|
+
break CMDx.configuration.coercions.call(task, key, value, options)
|
160
|
+
rescue CoercionError => e
|
161
|
+
next if tsize != i
|
162
|
+
|
163
|
+
raise(e) if tsize.zero?
|
164
|
+
|
165
|
+
values = types.map(&:to_s).join(", ")
|
166
|
+
raise CoercionError, I18n.t(
|
167
|
+
"cmdx.coercions.into_any",
|
168
|
+
values:,
|
169
|
+
default: "could not coerce into one of: #{values}"
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Checks if validations should be skipped for optional missing arguments.
|
175
|
+
#
|
176
|
+
# @return [Boolean] true if validations should be skipped, false otherwise
|
177
|
+
#
|
178
|
+
# @example Check if validations should be skipped
|
179
|
+
# evaluator.send(:skip_validations_due_to_optional_missing_argument?) #=> false
|
180
|
+
def skip_validations_due_to_optional_missing_argument?
|
181
|
+
optional? && value.nil? && !source.nil? && !source.cmdx_respond_to?(name, true)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Checks if validator should be skipped due to conditional options.
|
185
|
+
#
|
186
|
+
# @param opts [Hash] the validator options
|
187
|
+
#
|
188
|
+
# @return [Boolean] true if validator should be skipped, false otherwise
|
189
|
+
#
|
190
|
+
# @example Check if validator should be skipped
|
191
|
+
# evaluator.send(:skip_validator_due_to_conditional?, :presence) #=> false
|
192
|
+
def skip_validator_due_to_conditional?(opts)
|
193
|
+
opts.is_a?(Hash) && !task.cmdx_eval(opts)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Checks if validator should be skipped due to allow_nil option.
|
197
|
+
#
|
198
|
+
# @param opts [Symbol] the validator options
|
199
|
+
#
|
200
|
+
# @return [Boolean] true if validator should be skipped, false otherwise
|
201
|
+
#
|
202
|
+
# @example Check if validator should be skipped for nil
|
203
|
+
# evaluator.send(:skip_validator_due_to_allow_nil?, :presence) #=> true
|
204
|
+
def skip_validator_due_to_allow_nil?(opts)
|
205
|
+
opts.is_a?(Hash) && opts[:allow_nil] && value.nil?
|
206
|
+
end
|
207
|
+
|
208
|
+
# Applies all configured validations to the parameter value.
|
209
|
+
#
|
210
|
+
# @return [void]
|
211
|
+
#
|
212
|
+
# @raise [ValidationError] when parameter value fails validation
|
213
|
+
#
|
214
|
+
# @example Validate parameter value
|
215
|
+
# evaluator.send(:validate!)
|
216
|
+
def validate!
|
217
|
+
return if skip_validations_due_to_optional_missing_argument?
|
218
|
+
|
219
|
+
types = CMDx.configuration.validators.registry.keys
|
220
|
+
|
221
|
+
options.slice(*types).each_key do |key|
|
222
|
+
opts = options[key]
|
223
|
+
next if skip_validator_due_to_allow_nil?(opts)
|
224
|
+
next if skip_validator_due_to_conditional?(opts)
|
225
|
+
|
226
|
+
CMDx.configuration.validators.call(task, key, value, opts)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
@@ -1,75 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Provides formatted inspection and display functionality for parameter objects.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# @example Basic parameter inspection
|
11
|
-
# parameter_hash = {
|
12
|
-
# name: :user_id,
|
13
|
-
# type: :integer,
|
14
|
-
# source: :context,
|
15
|
-
# required: true,
|
16
|
-
# options: { numeric: { min: 1 } },
|
17
|
-
# children: []
|
18
|
-
# }
|
19
|
-
# ParameterInspector.call(parameter_hash)
|
20
|
-
# # => "Parameter: name=user_id type=integer source=context required=true options={numeric: {min: 1}}"
|
21
|
-
#
|
22
|
-
# @example Nested parameter inspection
|
23
|
-
# nested_parameter = {
|
24
|
-
# name: :address,
|
25
|
-
# type: :virtual,
|
26
|
-
# source: :context,
|
27
|
-
# required: true,
|
28
|
-
# options: {},
|
29
|
-
# children: [
|
30
|
-
# { name: :street, type: :string, source: :address, required: true, options: {}, children: [] }
|
31
|
-
# ]
|
32
|
-
# }
|
33
|
-
# ParameterInspector.call(nested_parameter)
|
34
|
-
# # => "Parameter: name=address type=virtual source=context required=true options={}
|
35
|
-
# # ↳ Parameter: name=street type=string source=address required=true options={}"
|
36
|
-
#
|
37
|
-
# @see CMDx::Parameter Parameter hash serialization via to_h
|
38
|
-
# @see CMDx::ParameterSerializer Parameter-to-hash conversion
|
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.
|
39
10
|
module ParameterInspector
|
40
11
|
|
41
|
-
# Ordered keys for consistent parameter inspection output.
|
42
|
-
#
|
43
|
-
# Defines the order in which parameter attributes are displayed
|
44
|
-
# in the inspection string, with children handled specially.
|
45
12
|
ORDERED_KEYS = %i[
|
46
13
|
name type source required options children
|
47
14
|
].freeze
|
48
15
|
|
49
16
|
module_function
|
50
17
|
|
51
|
-
#
|
18
|
+
# Formats a parameter hash into a human-readable inspection string.
|
52
19
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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.
|
56
24
|
#
|
57
|
-
# @param parameter [Hash]
|
58
|
-
# @
|
59
|
-
# @
|
25
|
+
# @param parameter [Hash] the parameter hash to format
|
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)
|
60
33
|
#
|
61
|
-
# @
|
62
|
-
# ParameterInspector.call(param_hash)
|
63
|
-
# # => "Parameter: name=user_id type=integer source=context required=true"
|
34
|
+
# @return [String] formatted multi-line string representation of the parameter
|
64
35
|
#
|
65
|
-
# @example
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
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"
|
69
40
|
#
|
70
|
-
# @example
|
71
|
-
#
|
72
|
-
#
|
41
|
+
# @example Format a parameter with children
|
42
|
+
# parameter = {
|
43
|
+
# name: :payment,
|
44
|
+
# type: :hash,
|
45
|
+
# required: true,
|
46
|
+
# children: [
|
47
|
+
# { name: :amount, type: :big_decimal, required: true },
|
48
|
+
# { name: :currency, type: :string, required: true }
|
49
|
+
# ]
|
50
|
+
# }
|
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"
|
73
55
|
def call(parameter, depth = 1)
|
74
56
|
ORDERED_KEYS.filter_map do |key|
|
75
57
|
value = parameter[key]
|
@@ -1,124 +1,105 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Registry for managing parameter definitions within tasks.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
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
|
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.
|
10
|
+
class ParameterRegistry
|
27
11
|
|
28
|
-
#
|
12
|
+
# @return [Array<Parameter>] array containing parameter definition objects
|
13
|
+
attr_reader :registry
|
14
|
+
|
15
|
+
# Initializes a new parameter registry with an empty parameter collection.
|
29
16
|
#
|
30
|
-
# @return [
|
17
|
+
# @return [ParameterRegistry] a new parameter registry instance
|
31
18
|
#
|
32
|
-
# @example
|
33
|
-
#
|
34
|
-
|
35
|
-
|
19
|
+
# @example Creating a new registry
|
20
|
+
# registry = ParameterRegistry.new
|
21
|
+
# registry.registry #=> []
|
22
|
+
def initialize
|
23
|
+
@registry = []
|
36
24
|
end
|
37
25
|
|
38
|
-
#
|
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.
|
30
|
+
#
|
31
|
+
# @return [ParameterRegistry] a new registry instance with duplicated parameters
|
32
|
+
#
|
33
|
+
# @example Duplicate a registry
|
34
|
+
# original = ParameterRegistry.new
|
35
|
+
# duplicate = original.dup
|
36
|
+
# duplicate.object_id != original.object_id #=> true
|
37
|
+
def dup
|
38
|
+
new_registry = self.class.new
|
39
|
+
new_registry.instance_variable_set(:@registry, registry.map(&:dup))
|
40
|
+
new_registry
|
41
|
+
end
|
42
|
+
|
43
|
+
# Checks if all parameters in the registry are valid.
|
39
44
|
#
|
40
45
|
# @return [Boolean] true if all parameters are valid, false otherwise
|
41
46
|
#
|
42
|
-
# @example
|
43
|
-
#
|
47
|
+
# @example Check registry validity
|
48
|
+
# registry.valid? #=> true
|
44
49
|
def valid?
|
45
|
-
all?(&:valid?)
|
50
|
+
registry.all?(&:valid?)
|
46
51
|
end
|
47
52
|
|
48
|
-
# Validates all parameters in the
|
53
|
+
# Validates all parameters in the registry against a task instance.
|
49
54
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
55
|
+
# This method ensures that each parameter is properly defined and accessible
|
56
|
+
# on the provided task, including nested parameters through recursive validation.
|
57
|
+
#
|
58
|
+
# @param task [Task] the task instance to validate parameters against
|
53
59
|
#
|
54
|
-
# @param task [CMDx::Task] The task instance to validate parameters against
|
55
60
|
# @return [void]
|
56
61
|
#
|
57
|
-
# @
|
58
|
-
# task = ProcessOrderTask.new
|
59
|
-
# parameter_registry.validate!(task) # Validates all parameters
|
62
|
+
# @raise [NoMethodError] if a parameter method is not defined on the task
|
60
63
|
#
|
61
|
-
# @example
|
62
|
-
#
|
63
|
-
# parameter_registry.validate!(task_with_nested_params)
|
64
|
+
# @example Validate parameters against a task
|
65
|
+
# registry.validate!(task_instance)
|
64
66
|
def validate!(task)
|
65
|
-
each { |p| recursive_validate!(task, p) }
|
67
|
+
registry.each { |p| recursive_validate!(task, p) }
|
66
68
|
end
|
67
69
|
|
68
|
-
# Converts the parameter
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
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
|
-
# # ]
|
70
|
+
# Converts the parameter registry to a hash representation.
|
71
|
+
#
|
72
|
+
# @return [Array<Hash>] array of parameter hash representations
|
73
|
+
#
|
74
|
+
# @example Convert registry to hash
|
75
|
+
# registry.to_h #=> [{name: :user_id, type: :integer}, {name: :email, type: :string}]
|
88
76
|
def to_h
|
89
|
-
|
77
|
+
registry.map(&:to_h)
|
90
78
|
end
|
91
|
-
alias to_a to_h
|
92
79
|
|
93
|
-
# Converts the parameter
|
94
|
-
#
|
95
|
-
# Creates a human-readable string representation of all parameters
|
96
|
-
# in the collection using the ParametersInspector.
|
80
|
+
# Converts the parameter registry to a string representation.
|
97
81
|
#
|
98
|
-
# @return [String]
|
82
|
+
# @return [String] string representation of all parameters, joined by newlines
|
99
83
|
#
|
100
|
-
# @example
|
101
|
-
#
|
102
|
-
# # => "Parameter: name=user_id type=integer source=context required=true
|
103
|
-
# # Parameter: name=email type=string source=context required=false"
|
84
|
+
# @example Convert registry to string
|
85
|
+
# registry.to_s #=> "user_id: integer\nemail: string"
|
104
86
|
def to_s
|
105
|
-
|
87
|
+
registry.map(&:to_s).join("\n")
|
106
88
|
end
|
107
89
|
|
108
90
|
private
|
109
91
|
|
110
|
-
# Recursively validates a parameter and
|
92
|
+
# Recursively validates a parameter and its children against a task.
|
111
93
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# structures.
|
94
|
+
# @param task [Task] the task instance to validate the parameter against
|
95
|
+
# @param parameter [Parameter] the parameter to validate
|
115
96
|
#
|
116
|
-
# @param task [CMDx::Task] The task instance to validate against
|
117
|
-
# @param parameter [CMDx::Parameter] The parameter to validate
|
118
97
|
# @return [void]
|
98
|
+
#
|
99
|
+
# @raise [NoMethodError] if the parameter method is not defined on the task
|
119
100
|
def recursive_validate!(task, parameter)
|
120
|
-
task.send(parameter.method_name)
|
121
|
-
parameter.children.each { |
|
101
|
+
task.send(parameter.method_name) # Make sure parameter is defined on task
|
102
|
+
parameter.children.each { |child| recursive_validate!(task, child) }
|
122
103
|
end
|
123
104
|
|
124
105
|
end
|
@@ -1,92 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
# Parameter serialization
|
4
|
+
# Parameter serialization utilities for converting parameter objects to hash representations.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
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.
|
54
10
|
module ParameterSerializer
|
55
11
|
|
56
12
|
module_function
|
57
13
|
|
58
|
-
# Converts a
|
14
|
+
# Converts a parameter object into a hash representation for serialization.
|
59
15
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
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.
|
63
19
|
#
|
64
|
-
# @param parameter [CMDx::Parameter]
|
65
|
-
# @return [Hash] Structured hash representation of the parameter
|
20
|
+
# @param parameter [CMDx::Parameter] the parameter object to serialize
|
66
21
|
#
|
67
|
-
# @
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# # required: true,
|
75
|
-
# # options: {},
|
76
|
-
# # children: []
|
77
|
-
# # }
|
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
|
78
29
|
#
|
79
|
-
# @example
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# #
|
30
|
+
# @example Serialize a nested parameter with children
|
31
|
+
# user_param = Parameter.new(:user, klass: MyTask, type: :hash) do
|
32
|
+
# required :name, type: :string
|
33
|
+
# optional :age, type: :integer
|
34
|
+
# end
|
35
|
+
# ParameterSerializer.call(user_param)
|
36
|
+
# #=> {
|
37
|
+
# # source: :context,
|
38
|
+
# # name: :user,
|
39
|
+
# # type: :hash,
|
87
40
|
# # required: false,
|
88
|
-
# # options: {
|
89
|
-
# # children: [
|
41
|
+
# # options: {},
|
42
|
+
# # children: [
|
43
|
+
# # { source: :user, name: :name, type: :string, required: true, options: {}, children: [] },
|
44
|
+
# # { source: :user, name: :age, type: :integer, required: false, options: {}, children: [] }
|
45
|
+
# # ]
|
90
46
|
# # }
|
91
47
|
def call(parameter)
|
92
48
|
{
|