cmdx 1.1.2 → 1.5.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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CMDx
|
4
|
-
# Logger serialization module for converting messages and task data into structured log format.
|
5
|
-
#
|
6
|
-
# LoggerSerializer provides functionality to serialize task execution messages into a
|
7
|
-
# standardized hash representation suitable for logging systems. It handles both result
|
8
|
-
# objects and arbitrary messages, applying consistent formatting with optional ANSI
|
9
|
-
# colorization for terminal output. The serializer intelligently processes different
|
10
|
-
# message types and enriches log data with task metadata and origin information.
|
11
|
-
module LoggerSerializer
|
12
|
-
|
13
|
-
COLORED_KEYS = %i[
|
14
|
-
state status outcome
|
15
|
-
].freeze
|
16
|
-
|
17
|
-
module_function
|
18
|
-
|
19
|
-
# Serializes a log message with task context into structured hash format.
|
20
|
-
#
|
21
|
-
# Converts log messages into a standardized hash representation suitable for
|
22
|
-
# various logging systems and output formats. When the message is a Result object,
|
23
|
-
# it extracts the result's hash representation and optionally applies ANSI colors
|
24
|
-
# to specific keys for enhanced terminal visibility. For non-result messages,
|
25
|
-
# it enriches the log entry with task metadata from TaskSerializer. All log
|
26
|
-
# entries are tagged with CMDx origin for source identification.
|
27
|
-
#
|
28
|
-
# @param severity [Symbol] the log severity level (not used in current implementation)
|
29
|
-
# @param time [Time] the timestamp of the log entry (not used in current implementation)
|
30
|
-
# @param task [CMDx::Task, CMDx::Workflow] the task or workflow instance providing context
|
31
|
-
# @param message [CMDx::Result, Object] the primary message content to serialize
|
32
|
-
# @param options [Hash] additional options for serialization behavior
|
33
|
-
# @option options [Boolean] :ansi_colorize whether to apply ANSI colors to result keys
|
34
|
-
#
|
35
|
-
# @return [Hash] a structured hash containing the serialized log message and metadata
|
36
|
-
# @option return [String] :origin always set to "CMDx" for source identification
|
37
|
-
# @option return [Integer] :index the task's position index in the execution chain (when message is not Result)
|
38
|
-
# @option return [String] :chain_id the unique identifier of the task's execution chain (when message is not Result)
|
39
|
-
# @option return [String] :type the task type, either "Task" or "Workflow" (when message is not Result)
|
40
|
-
# @option return [String] :class the full class name of the task (when message is not Result)
|
41
|
-
# @option return [String] :id the unique identifier of the task instance (when message is not Result)
|
42
|
-
# @option return [Array] :tags the tags associated with the task from cmd settings (when message is not Result)
|
43
|
-
# @option return [Object] :message the original message content (when message is not Result)
|
44
|
-
# @option return [Symbol] :state the execution state with optional ANSI colors (when message is Result)
|
45
|
-
# @option return [Symbol] :status the execution status with optional ANSI colors (when message is Result)
|
46
|
-
# @option return [Symbol] :outcome the execution outcome with optional ANSI colors (when message is Result)
|
47
|
-
# @option return [Hash] :metadata additional metadata from result (when message is Result)
|
48
|
-
# @option return [Float] :runtime execution runtime in seconds (when message is Result)
|
49
|
-
#
|
50
|
-
# @raise [NoMethodError] if task doesn't respond to required methods for TaskSerializer
|
51
|
-
# @raise [NoMethodError] if result message doesn't respond to to_h method
|
52
|
-
#
|
53
|
-
# @example Serialize a result message with ANSI colors
|
54
|
-
# task = ProcessDataTask.call(data: "test")
|
55
|
-
# LoggerSerializer.call(:info, Time.now, task, task.result, ansi_colorize: true)
|
56
|
-
# #=> {
|
57
|
-
# # origin: "CMDx",
|
58
|
-
# # index: 0,
|
59
|
-
# # chain_id: "abc123",
|
60
|
-
# # type: "Task",
|
61
|
-
# # class: "ProcessDataTask",
|
62
|
-
# # id: "def456",
|
63
|
-
# # tags: [],
|
64
|
-
# # state: "\e[0;32;49mcomplete\e[0m",
|
65
|
-
# # status: "\e[0;32;49msuccess\e[0m",
|
66
|
-
# # outcome: "\e[0;32;49mgood\e[0m",
|
67
|
-
# # metadata: {},
|
68
|
-
# # runtime: 0.045
|
69
|
-
# # }
|
70
|
-
#
|
71
|
-
# @example Serialize a string message with task context
|
72
|
-
# task = MyTask.new(context: {data: "test"})
|
73
|
-
# LoggerSerializer.call(:warn, Time.now, task, "Processing started")
|
74
|
-
# #=> {
|
75
|
-
# # origin: "CMDx",
|
76
|
-
# # index: 0,
|
77
|
-
# # chain_id: "abc123",
|
78
|
-
# # type: "Task",
|
79
|
-
# # class: "MyTask",
|
80
|
-
# # id: "def456",
|
81
|
-
# # tags: [],
|
82
|
-
# # message: "Processing started"
|
83
|
-
# # }
|
84
|
-
#
|
85
|
-
# @example Serialize a result message without colors
|
86
|
-
# task = ValidationTask.call(email: "invalid")
|
87
|
-
# LoggerSerializer.call(:error, Time.now, task, task.result)
|
88
|
-
# #=> {
|
89
|
-
# # origin: "CMDx",
|
90
|
-
# # index: 1,
|
91
|
-
# # chain_id: "xyz789",
|
92
|
-
# # type: "Task",
|
93
|
-
# # class: "ValidationTask",
|
94
|
-
# # id: "ghi012",
|
95
|
-
# # tags: [],
|
96
|
-
# # state: :interrupted,
|
97
|
-
# # status: :failed,
|
98
|
-
# # outcome: :bad,
|
99
|
-
# # metadata: { reason: "Invalid email format" },
|
100
|
-
# # runtime: 0.012
|
101
|
-
# # }
|
102
|
-
def call(severity, time, task, message, **options) # rubocop:disable Lint/UnusedMethodArgument
|
103
|
-
m = message.is_a?(Result) ? message.to_h : {}
|
104
|
-
|
105
|
-
if options.delete(:ansi_colorize) && message.is_a?(Result)
|
106
|
-
COLORED_KEYS.each { |k| m[k] = ResultAnsi.call(m[k]) if m.key?(k) }
|
107
|
-
elsif !message.is_a?(Result)
|
108
|
-
m.merge!(TaskSerializer.call(task), message: message)
|
109
|
-
end
|
110
|
-
|
111
|
-
m[:origin] ||= "CMDx"
|
112
|
-
m
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
data/lib/cmdx/middleware.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CMDx
|
4
|
-
# Base class for implementing middleware functionality in task processing pipelines.
|
5
|
-
#
|
6
|
-
# Middleware provides a way to wrap task execution with custom logic that runs before
|
7
|
-
# and after task processing. Middleware can be used for cross-cutting concerns such as
|
8
|
-
# logging, authentication, caching, error handling, and other aspects that should be
|
9
|
-
# applied consistently across multiple tasks. All middleware implementations must
|
10
|
-
# inherit from this class and implement the abstract call method.
|
11
|
-
class Middleware
|
12
|
-
|
13
|
-
# Executes middleware by creating a new instance and calling it.
|
14
|
-
#
|
15
|
-
# This class method provides a convenient way to execute middleware without
|
16
|
-
# manually instantiating the middleware class. It creates a new instance
|
17
|
-
# and delegates to the instance call method with the provided arguments.
|
18
|
-
#
|
19
|
-
# @param task [CMDx::Task] the task instance being processed
|
20
|
-
# @param callable [Proc] the callable that executes the next middleware or task logic
|
21
|
-
#
|
22
|
-
# @return [Object] the result returned by the middleware implementation
|
23
|
-
#
|
24
|
-
# @raise [UndefinedCallError] when the middleware subclass doesn't implement call
|
25
|
-
#
|
26
|
-
# @example Execute middleware on a task
|
27
|
-
# class LoggingMiddleware < CMDx::Middleware
|
28
|
-
# def call(task, callable)
|
29
|
-
# task.logger.info "Starting #{task.class.name}"
|
30
|
-
# result = callable.call
|
31
|
-
# task.logger.info "Completed #{task.class.name}"
|
32
|
-
# result
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# LoggingMiddleware.call(my_task, -> { my_task.process })
|
37
|
-
def self.call(task, callable)
|
38
|
-
new.call(task, callable)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Abstract method that must be implemented by middleware subclasses.
|
42
|
-
#
|
43
|
-
# This method contains the actual middleware logic that wraps task execution.
|
44
|
-
# Subclasses must override this method to provide their specific middleware
|
45
|
-
# implementation. The method should call the provided callable to continue
|
46
|
-
# the middleware chain or execute the task logic.
|
47
|
-
#
|
48
|
-
# @param _task [CMDx::Task] the task instance being processed
|
49
|
-
# @param _callable [Proc] the callable that executes the next middleware or task logic
|
50
|
-
#
|
51
|
-
# @return [Object] the result of the middleware processing
|
52
|
-
#
|
53
|
-
# @raise [UndefinedCallError] always raised in the base class
|
54
|
-
#
|
55
|
-
# @example Implement middleware in a subclass
|
56
|
-
# class TimingMiddleware < CMDx::Middleware
|
57
|
-
# def call(task, callable)
|
58
|
-
# start_time = Time.now
|
59
|
-
# result = callable.call
|
60
|
-
# duration = Time.now - start_time
|
61
|
-
# task.logger.info "Task completed in #{duration}s"
|
62
|
-
# result
|
63
|
-
# end
|
64
|
-
# end
|
65
|
-
def call(_task, _callable)
|
66
|
-
raise UndefinedCallError, "call method not defined in #{self.class.name}"
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
data/lib/cmdx/parameter.rb
DELETED
@@ -1,312 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CMDx
|
4
|
-
# Parameter definition and management for task attribute configuration.
|
5
|
-
#
|
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.
|
11
|
-
class Parameter
|
12
|
-
|
13
|
-
cmdx_attr_delegator :invalid?, :valid?,
|
14
|
-
to: :errors
|
15
|
-
|
16
|
-
# @return [CMDx::Task] The task class this parameter belongs to
|
17
|
-
attr_accessor :task
|
18
|
-
|
19
|
-
# @return [Class] The task class this parameter is defined in
|
20
|
-
attr_reader :klass
|
21
|
-
|
22
|
-
# @return [Parameter, nil] The parent parameter for nested parameters
|
23
|
-
attr_reader :parent
|
24
|
-
|
25
|
-
# @return [Symbol] The parameter name
|
26
|
-
attr_reader :name
|
27
|
-
|
28
|
-
# @return [Symbol, Array<Symbol>] The parameter type(s) for coercion
|
29
|
-
attr_reader :type
|
30
|
-
|
31
|
-
# @return [Hash] The parameter configuration options
|
32
|
-
attr_reader :options
|
33
|
-
|
34
|
-
# @return [Array<Parameter>] Child parameters for nested parameter definitions
|
35
|
-
attr_reader :children
|
36
|
-
|
37
|
-
# @return [CMDx::Errors] Validation errors for this parameter
|
38
|
-
attr_reader :errors
|
39
|
-
|
40
|
-
# Creates a new parameter definition with the specified configuration.
|
41
|
-
#
|
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
|
53
|
-
#
|
54
|
-
# @return [Parameter] a new parameter instance
|
55
|
-
#
|
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
|
65
|
-
# Parameter.new(:user, klass: MyTask, type: :hash) do
|
66
|
-
# required :name, type: :string
|
67
|
-
# optional :age, type: :integer
|
68
|
-
# end
|
69
|
-
def initialize(name, **options, &)
|
70
|
-
@klass = options.delete(:klass) || raise(KeyError, "klass option required")
|
71
|
-
@parent = options.delete(:parent)
|
72
|
-
@type = options.delete(:type) || :virtual
|
73
|
-
@required = options.delete(:required) || false
|
74
|
-
|
75
|
-
@name = name
|
76
|
-
@options = options
|
77
|
-
@children = []
|
78
|
-
@errors = Errors.new
|
79
|
-
|
80
|
-
define_attribute(self)
|
81
|
-
instance_eval(&) if block_given?
|
82
|
-
end
|
83
|
-
|
84
|
-
class << self
|
85
|
-
|
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
|
98
|
-
#
|
99
|
-
# @return [Array<Parameter>] array of created optional parameter instances
|
100
|
-
#
|
101
|
-
# @raise [ArgumentError] if no parameter names are provided
|
102
|
-
# @raise [ArgumentError] if :as option is used with multiple parameter names
|
103
|
-
#
|
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)
|
112
|
-
def optional(*names, **options, &)
|
113
|
-
if names.none?
|
114
|
-
raise ArgumentError, "no parameters given"
|
115
|
-
elsif !names.one? && options.key?(:as)
|
116
|
-
raise ArgumentError, ":as option only supports one parameter per definition"
|
117
|
-
end
|
118
|
-
|
119
|
-
names.filter_map { |n| new(n, **options, &) }
|
120
|
-
end
|
121
|
-
|
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
|
136
|
-
#
|
137
|
-
# @raise [ArgumentError] if no parameter names are provided
|
138
|
-
# @raise [ArgumentError] if :as option is used with multiple parameter names
|
139
|
-
#
|
140
|
-
# @example Define single required parameter
|
141
|
-
# Parameter.required(:user_id, klass: MyTask, type: :integer)
|
142
|
-
#
|
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: /@/ })
|
148
|
-
def required(*names, **options, &)
|
149
|
-
optional(*names, **options.merge(required: true), &)
|
150
|
-
end
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
# Defines optional child parameters for nested parameter structures.
|
155
|
-
#
|
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
|
164
|
-
#
|
165
|
-
# @return [Array<Parameter>] array of created optional child parameter instances
|
166
|
-
#
|
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)
|
173
|
-
def optional(*names, **options, &)
|
174
|
-
parameters = Parameter.optional(*names, **options.merge(klass: @klass, parent: self), &)
|
175
|
-
children.concat(parameters)
|
176
|
-
end
|
177
|
-
|
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
|
188
|
-
#
|
189
|
-
# @return [Array<Parameter>] array of created required child parameter instances
|
190
|
-
#
|
191
|
-
# @raise [ArgumentError] if no parameter names are provided
|
192
|
-
# @raise [ArgumentError] if :as option is used with multiple parameter names
|
193
|
-
#
|
194
|
-
# @example Define required child parameters
|
195
|
-
# user_param = Parameter.new(:user, klass: MyTask, type: :hash)
|
196
|
-
# user_param.required(:name, :email, type: :string)
|
197
|
-
def required(*names, **options, &)
|
198
|
-
parameters = Parameter.required(*names, **options.merge(klass: @klass, parent: self), &)
|
199
|
-
children.concat(parameters)
|
200
|
-
end
|
201
|
-
|
202
|
-
# Checks if the parameter is marked as required for task execution.
|
203
|
-
#
|
204
|
-
# @return [Boolean] true if the parameter is required, false otherwise
|
205
|
-
#
|
206
|
-
# @example Check if parameter is required
|
207
|
-
# param = Parameter.new(:name, klass: MyTask, required: true)
|
208
|
-
# param.required? #=> true
|
209
|
-
def required?
|
210
|
-
!!@required
|
211
|
-
end
|
212
|
-
|
213
|
-
# Checks if the parameter is marked as optional for task execution.
|
214
|
-
#
|
215
|
-
# @return [Boolean] true if the parameter is optional, false otherwise
|
216
|
-
#
|
217
|
-
# @example Check if parameter is optional
|
218
|
-
# param = Parameter.new(:description, klass: MyTask, required: false)
|
219
|
-
# param.optional? #=> true
|
220
|
-
def optional?
|
221
|
-
!required?
|
222
|
-
end
|
223
|
-
|
224
|
-
# Generates the method name that will be created on the task class for this parameter.
|
225
|
-
#
|
226
|
-
# @return [Symbol] the method name with any configured prefix, suffix, or custom naming
|
227
|
-
#
|
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
|
235
|
-
def method_name
|
236
|
-
@method_name ||= Utils::NameAffix.call(name, method_source, options)
|
237
|
-
end
|
238
|
-
|
239
|
-
# Determines the source context for parameter resolution and method name generation.
|
240
|
-
#
|
241
|
-
# @return [Symbol] the source identifier used for parameter resolution
|
242
|
-
#
|
243
|
-
# @example Get method source for simple parameter
|
244
|
-
# param = Parameter.new(:user_id, klass: MyTask)
|
245
|
-
# param.method_source #=> :context
|
246
|
-
#
|
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
|
251
|
-
def method_source
|
252
|
-
@method_source ||= options[:source] || parent&.method_name || :context
|
253
|
-
end
|
254
|
-
|
255
|
-
# Converts the parameter to a hash representation for serialization.
|
256
|
-
#
|
257
|
-
# @return [Hash] hash containing all parameter metadata and configuration
|
258
|
-
#
|
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, ... }
|
263
|
-
def to_h
|
264
|
-
ParameterSerializer.call(self)
|
265
|
-
end
|
266
|
-
|
267
|
-
# Converts the parameter to a formatted string representation for inspection.
|
268
|
-
#
|
269
|
-
# @return [String] human-readable string representation of the parameter
|
270
|
-
#
|
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 ..."
|
275
|
-
def to_s
|
276
|
-
ParameterInspector.call(to_h)
|
277
|
-
end
|
278
|
-
|
279
|
-
private
|
280
|
-
|
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
|
284
|
-
#
|
285
|
-
# @return [void]
|
286
|
-
#
|
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
|
290
|
-
def define_attribute(parameter)
|
291
|
-
klass.send(:define_method, parameter.method_name) do
|
292
|
-
@cmd_parameter_value_cache ||= {}
|
293
|
-
|
294
|
-
unless @cmd_parameter_value_cache.key?(parameter.method_name)
|
295
|
-
begin
|
296
|
-
parameter_value = ParameterEvaluator.call(self, parameter)
|
297
|
-
rescue CoercionError, ValidationError => e
|
298
|
-
parameter.errors.add(parameter.method_name, e.message)
|
299
|
-
errors.merge!(parameter.errors.to_hash)
|
300
|
-
ensure
|
301
|
-
@cmd_parameter_value_cache[parameter.method_name] = parameter_value
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
@cmd_parameter_value_cache[parameter.method_name]
|
306
|
-
end
|
307
|
-
|
308
|
-
klass.send(:private, parameter.method_name)
|
309
|
-
end
|
310
|
-
|
311
|
-
end
|
312
|
-
end
|