cmdx 1.0.1 → 1.1.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/.cursor/prompts/rspec.md +20 -0
- data/.cursor/prompts/yardoc.md +8 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +17 -2
- data/README.md +1 -1
- data/docs/basics/call.md +2 -2
- data/docs/basics/chain.md +1 -1
- data/docs/callbacks.md +3 -36
- data/docs/configuration.md +58 -12
- data/docs/interruptions/exceptions.md +1 -1
- data/docs/interruptions/faults.md +2 -2
- data/docs/logging.md +4 -4
- data/docs/middlewares.md +43 -43
- data/docs/parameters/coercions.md +49 -38
- data/docs/parameters/defaults.md +1 -1
- data/docs/parameters/validations.md +0 -39
- data/docs/testing.md +11 -12
- data/docs/workflows.md +4 -4
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +36 -56
- data/lib/cmdx/callback_registry.rb +82 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +22 -115
- data/lib/cmdx/chain_serializer.rb +17 -148
- data/lib/cmdx/coercion.rb +49 -0
- data/lib/cmdx/coercion_registry.rb +94 -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 +57 -171
- data/lib/cmdx/context.rb +22 -165
- 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 +40 -156
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +165 -202
- data/lib/cmdx/fault.rb +55 -158
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -109
- data/lib/cmdx/lazy_struct.rb +103 -187
- 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 +20 -82
- data/lib/cmdx/logger_ansi.rb +18 -75
- data/lib/cmdx/logger_serializer.rb +24 -114
- data/lib/cmdx/middleware.rb +38 -60
- data/lib/cmdx/middleware_registry.rb +81 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +120 -198
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +25 -56
- data/lib/cmdx/parameter_registry.rb +59 -84
- data/lib/cmdx/parameter_serializer.rb +23 -74
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -260
- data/lib/cmdx/result_ansi.rb +19 -85
- data/lib/cmdx/result_inspector.rb +27 -68
- data/lib/cmdx/result_logger.rb +18 -81
- data/lib/cmdx/result_serializer.rb +28 -132
- 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 +213 -425
- data/lib/cmdx/task_deprecator.rb +55 -0
- data/lib/cmdx/task_processor.rb +245 -0
- data/lib/cmdx/task_serializer.rb +22 -70
- 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 +13 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +48 -0
- data/lib/cmdx/validator_registry.rb +86 -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 +46 -339
- 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 +34 -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
data/lib/cmdx/chain_inspector.rb
CHANGED
@@ -1,137 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Provides formatted inspection and display functionality for execution chains.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# with visual separators for easy debugging and monitoring.
|
10
|
-
#
|
11
|
-
# @example Basic chain inspection
|
12
|
-
# result = ProcessOrderTask.call(order_id: 123)
|
13
|
-
# chain = result.chain
|
14
|
-
#
|
15
|
-
# ChainInspector.call(chain)
|
16
|
-
# # => "
|
17
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
18
|
-
# # ================================================
|
19
|
-
# #
|
20
|
-
# # {
|
21
|
-
# # class: "ProcessOrderTask",
|
22
|
-
# # type: "Task",
|
23
|
-
# # index: 0,
|
24
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
25
|
-
# # tags: [],
|
26
|
-
# # state: "complete",
|
27
|
-
# # status: "success",
|
28
|
-
# # outcome: "success",
|
29
|
-
# # metadata: {},
|
30
|
-
# # runtime: 0.5
|
31
|
-
# # }
|
32
|
-
# #
|
33
|
-
# # ================================================
|
34
|
-
# # state: complete | status: success | outcome: success | runtime: 0.5
|
35
|
-
# # "
|
36
|
-
#
|
37
|
-
# @example Chain with multiple tasks
|
38
|
-
# class ComplexTask < CMDx::Task
|
39
|
-
# def call
|
40
|
-
# SubTask1.call(context)
|
41
|
-
# SubTask2.call(context)
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# result = ComplexTask.call
|
46
|
-
# ChainInspector.call(result.chain)
|
47
|
-
# # => Shows formatted output with all three task results and summary
|
48
|
-
#
|
49
|
-
# @example Failed chain inspection
|
50
|
-
# # When a chain contains failed tasks, the summary reflects the failure state
|
51
|
-
# ChainInspector.call(failed_chain)
|
52
|
-
# # => Shows all task results with failure information and failed summary
|
53
|
-
#
|
54
|
-
# @see CMDx::Chain Chain execution context and result tracking
|
55
|
-
# @see CMDx::Result Individual result inspection via to_h
|
6
|
+
# This module formats chain execution information into a human-readable string
|
7
|
+
# representation, including the chain ID, individual task results, and summary
|
8
|
+
# information about the chain's final state.
|
56
9
|
module ChainInspector
|
57
10
|
|
58
|
-
# Keys to display in the chain summary footer.
|
59
|
-
#
|
60
|
-
# These keys represent the most important chain-level information
|
61
|
-
# that should be displayed in the summary footer for quick reference.
|
62
11
|
FOOTER_KEYS = %i[
|
63
12
|
state status outcome runtime
|
64
13
|
].freeze
|
65
14
|
|
66
15
|
module_function
|
67
16
|
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# Creates a formatted summary that includes:
|
71
|
-
# - Chain header with unique ID
|
72
|
-
# - Visual separator line
|
73
|
-
# - Pretty-printed hash representation of each result
|
74
|
-
# - Visual separator line
|
75
|
-
# - Summary footer with key chain statistics
|
17
|
+
# Formats a chain into a human-readable inspection string.
|
76
18
|
#
|
77
|
-
#
|
78
|
-
#
|
19
|
+
# Creates a formatted display showing the chain ID, individual task results,
|
20
|
+
# and summary footer with execution state information. The output includes
|
21
|
+
# visual separators and structured formatting for easy reading.
|
79
22
|
#
|
80
|
-
# @
|
81
|
-
# chain = SimpleTask.call.chain
|
82
|
-
# ChainInspector.call(chain)
|
83
|
-
# # => "
|
84
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
85
|
-
# # ================================================
|
86
|
-
# #
|
87
|
-
# # {
|
88
|
-
# # class: "SimpleTask",
|
89
|
-
# # type: "Task",
|
90
|
-
# # index: 0,
|
91
|
-
# # state: "complete",
|
92
|
-
# # status: "success",
|
93
|
-
# # outcome: "success",
|
94
|
-
# # runtime: 0.1
|
95
|
-
# # }
|
96
|
-
# #
|
97
|
-
# # ================================================
|
98
|
-
# # state: complete | status: success | outcome: success | runtime: 0.1
|
99
|
-
# # "
|
23
|
+
# @param chain [CMDx::Chain] the chain object to inspect
|
100
24
|
#
|
101
|
-
# @
|
102
|
-
# class ParentTask < CMDx::Task
|
103
|
-
# def call
|
104
|
-
# ChildTask1.call(context)
|
105
|
-
# ChildTask2.call(context)
|
106
|
-
# end
|
107
|
-
# end
|
25
|
+
# @return [String] formatted multi-line string representation of the chain
|
108
26
|
#
|
109
|
-
#
|
110
|
-
# ChainInspector.call(chain)
|
111
|
-
# # => "
|
112
|
-
# # chain: 018c2b95-b764-7615-a924-cc5b910ed1e5
|
113
|
-
# # ================================================
|
114
|
-
# #
|
115
|
-
# # { class: "ParentTask", index: 0, state: "complete", status: "success", ... }
|
116
|
-
# # { class: "ChildTask1", index: 1, state: "complete", status: "success", ... }
|
117
|
-
# # { class: "ChildTask2", index: 2, state: "complete", status: "success", ... }
|
118
|
-
# #
|
119
|
-
# # ================================================
|
120
|
-
# # state: complete | status: success | outcome: success | runtime: 0.5
|
121
|
-
# # "
|
27
|
+
# @raise [NoMethodError] if chain doesn't respond to required methods (id, results, state, status, outcome, runtime)
|
122
28
|
#
|
123
|
-
# @example
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
# #
|
29
|
+
# @example Inspect a simple chain
|
30
|
+
# chain = CMDx::Chain.new(id: "abc123")
|
31
|
+
# result = CMDx::Result.new(task)
|
32
|
+
# chain.results << result
|
33
|
+
# puts CMDx::ChainInspector.call(chain)
|
34
|
+
# # Output:
|
35
|
+
# # chain: abc123
|
36
|
+
# # ===================
|
129
37
|
# #
|
130
|
-
# #
|
38
|
+
# # {:state=>"complete", :status=>"success", ...}
|
131
39
|
# #
|
132
|
-
# #
|
133
|
-
# #
|
134
|
-
# # "
|
40
|
+
# # ===================
|
41
|
+
# # state: complete | status: success | outcome: success | runtime: 0.001
|
135
42
|
def call(chain)
|
136
43
|
header = "\nchain: #{chain.id}"
|
137
44
|
footer = FOOTER_KEYS.map { |key| "#{key}: #{chain.send(key)}" }.join(" | ")
|
@@ -1,164 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# into structured hash representations suitable for inspection, logging,
|
8
|
-
# debugging, and data interchange. It creates comprehensive data structures
|
9
|
-
# that include chain metadata and all associated task results.
|
10
|
-
#
|
11
|
-
# @example Basic chain serialization
|
12
|
-
# result = ProcessOrderTask.call(order_id: 123)
|
13
|
-
# chain = result.chain
|
14
|
-
#
|
15
|
-
# ChainSerializer.call(chain)
|
16
|
-
# # => {
|
17
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
18
|
-
# # state: "complete",
|
19
|
-
# # status: "success",
|
20
|
-
# # outcome: "success",
|
21
|
-
# # runtime: 0.5,
|
22
|
-
# # results: [
|
23
|
-
# # {
|
24
|
-
# # class: "ProcessOrderTask",
|
25
|
-
# # type: "Task",
|
26
|
-
# # index: 0,
|
27
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
28
|
-
# # chain_id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
29
|
-
# # tags: [],
|
30
|
-
# # state: "complete",
|
31
|
-
# # status: "success",
|
32
|
-
# # outcome: "success",
|
33
|
-
# # metadata: {},
|
34
|
-
# # runtime: 0.5
|
35
|
-
# # }
|
36
|
-
# # ]
|
37
|
-
# # }
|
38
|
-
#
|
39
|
-
# @example Chain with multiple tasks
|
40
|
-
# class ComplexTask < CMDx::Task
|
41
|
-
# def call
|
42
|
-
# SubTask1.call(context)
|
43
|
-
# SubTask2.call(context)
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# result = ComplexTask.call
|
48
|
-
# chain = result.chain
|
49
|
-
#
|
50
|
-
# ChainSerializer.call(chain)
|
51
|
-
# # => {
|
52
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
53
|
-
# # state: "complete",
|
54
|
-
# # status: "success",
|
55
|
-
# # outcome: "success",
|
56
|
-
# # runtime: 1.2,
|
57
|
-
# # results: [
|
58
|
-
# # { class: "ComplexTask", index: 0, state: "complete", status: "success", ... },
|
59
|
-
# # { class: "SubTask1", index: 1, state: "complete", status: "success", ... },
|
60
|
-
# # { class: "SubTask2", index: 2, state: "complete", status: "success", ... }
|
61
|
-
# # ]
|
62
|
-
# # }
|
63
|
-
#
|
64
|
-
# @example Failed chain serialization
|
65
|
-
# failed_result = FailingTask.call
|
66
|
-
# failed_chain = failed_result.chain
|
67
|
-
#
|
68
|
-
# ChainSerializer.call(failed_chain)
|
69
|
-
# # => {
|
70
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
71
|
-
# # state: "interrupted",
|
72
|
-
# # status: "failed",
|
73
|
-
# # outcome: "failed",
|
74
|
-
# # runtime: 0.1,
|
75
|
-
# # results: [
|
76
|
-
# # {
|
77
|
-
# # class: "FailingTask",
|
78
|
-
# # state: "interrupted",
|
79
|
-
# # status: "failed",
|
80
|
-
# # outcome: "failed",
|
81
|
-
# # metadata: { reason: "Something went wrong" },
|
82
|
-
# # runtime: 0.1,
|
83
|
-
# # ...
|
84
|
-
# # }
|
85
|
-
# # ]
|
86
|
-
# # }
|
87
|
-
#
|
88
|
-
# @see CMDx::Chain Chain execution context and result tracking
|
89
|
-
# @see CMDx::ResultSerializer Individual result serialization
|
90
|
-
# @see CMDx::ChainInspector Human-readable chain formatting
|
4
|
+
# Serializes Chain objects into hash representations for external consumption.
|
5
|
+
# Provides a consistent interface for converting chain execution data into
|
6
|
+
# structured format suitable for logging, API responses, or persistence.
|
91
7
|
module ChainSerializer
|
92
8
|
|
93
9
|
module_function
|
94
10
|
|
95
|
-
# Converts a
|
11
|
+
# Converts a chain object into a hash representation containing execution metadata.
|
12
|
+
# Extracts key chain attributes and serializes all contained results for complete
|
13
|
+
# execution state capture.
|
96
14
|
#
|
97
|
-
#
|
98
|
-
# and all associated task results. The chain-level data is derived from the
|
99
|
-
# first result in the collection, while all individual results are included
|
100
|
-
# in their full serialized form.
|
15
|
+
# @param chain [Chain] the chain instance to serialize
|
101
16
|
#
|
102
|
-
# @
|
103
|
-
# @return [Hash] Structured hash representation of the chain and all results
|
17
|
+
# @return [Hash] hash containing chain metadata and serialized results
|
104
18
|
#
|
105
|
-
# @
|
106
|
-
# chain = SimpleTask.call.chain
|
107
|
-
# ChainSerializer.call(chain)
|
108
|
-
# # => {
|
109
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
110
|
-
# # state: "complete",
|
111
|
-
# # status: "success",
|
112
|
-
# # outcome: "success",
|
113
|
-
# # runtime: 0.1,
|
114
|
-
# # results: [
|
115
|
-
# # {
|
116
|
-
# # class: "SimpleTask",
|
117
|
-
# # type: "Task",
|
118
|
-
# # index: 0,
|
119
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
120
|
-
# # chain_id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
121
|
-
# # tags: [],
|
122
|
-
# # state: "complete",
|
123
|
-
# # status: "success",
|
124
|
-
# # outcome: "success",
|
125
|
-
# # metadata: {},
|
126
|
-
# # runtime: 0.1
|
127
|
-
# # }
|
128
|
-
# # ]
|
129
|
-
# # }
|
130
|
-
#
|
131
|
-
# @example Multi-task chain serialization
|
132
|
-
# class ParentTask < CMDx::Task
|
133
|
-
# def call
|
134
|
-
# ChildTask.call(context)
|
135
|
-
# end
|
136
|
-
# end
|
19
|
+
# @raise [NoMethodError] if chain doesn't respond to required methods
|
137
20
|
#
|
138
|
-
#
|
21
|
+
# @example Serializing a workflow chain
|
22
|
+
# chain = UserWorkflow.call(user_id: 123)
|
139
23
|
# ChainSerializer.call(chain)
|
140
24
|
# # => {
|
141
|
-
# # id: "
|
142
|
-
# # state:
|
143
|
-
# # status:
|
144
|
-
# # outcome:
|
145
|
-
# # runtime: 0.
|
146
|
-
# # results: [
|
147
|
-
# # { class: "ParentTask", index: 0, ... },
|
148
|
-
# # { class: "ChildTask", index: 1, ... }
|
149
|
-
# # ]
|
150
|
-
# # }
|
151
|
-
#
|
152
|
-
# @example Empty chain serialization
|
153
|
-
# empty_chain = Chain.new
|
154
|
-
# ChainSerializer.call(empty_chain)
|
155
|
-
# # => {
|
156
|
-
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
157
|
-
# # state: nil,
|
158
|
-
# # status: nil,
|
159
|
-
# # outcome: nil,
|
160
|
-
# # runtime: nil,
|
161
|
-
# # results: []
|
25
|
+
# # id: "abc123",
|
26
|
+
# # state: :complete,
|
27
|
+
# # status: :success,
|
28
|
+
# # outcome: :good,
|
29
|
+
# # runtime: 0.045,
|
30
|
+
# # results: [...]
|
162
31
|
# # }
|
163
32
|
def call(chain)
|
164
33
|
{
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Base class for implementing type coercion functionality in parameter processing.
|
5
|
+
#
|
6
|
+
# Coercions are used to convert parameter values from one type to another,
|
7
|
+
# supporting both built-in types and custom coercion logic. All coercion
|
8
|
+
# implementations must inherit from this class and implement the abstract call method.
|
9
|
+
class Coercion
|
10
|
+
|
11
|
+
# Executes a coercion by creating a new instance and calling it.
|
12
|
+
#
|
13
|
+
# @param value [Object] the value to be coerced
|
14
|
+
# @param options [Hash] additional options for the coercion
|
15
|
+
#
|
16
|
+
# @return [Object] the coerced value
|
17
|
+
#
|
18
|
+
# @raise [UndefinedCallError] when the coercion subclass doesn't implement call
|
19
|
+
#
|
20
|
+
# @example Execute a coercion on a value
|
21
|
+
# IntegerCoercion.call("42")
|
22
|
+
# # => 42
|
23
|
+
def self.call(value, options = {})
|
24
|
+
new.call(value, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Abstract method that must be implemented by coercion subclasses.
|
28
|
+
#
|
29
|
+
# This method contains the actual coercion logic to convert the input
|
30
|
+
# value to the desired type. Subclasses must override this method to
|
31
|
+
# provide their specific coercion implementation.
|
32
|
+
#
|
33
|
+
# @param _value [Object] the value to be coerced
|
34
|
+
# @param _options [Hash] additional options for the coercion
|
35
|
+
#
|
36
|
+
# @return [Object] the coerced value
|
37
|
+
#
|
38
|
+
# @raise [UndefinedCallError] always raised in the base class
|
39
|
+
#
|
40
|
+
# @example Implement in a subclass
|
41
|
+
# def call(value, options = {})
|
42
|
+
# Integer(value)
|
43
|
+
# end
|
44
|
+
def call(_value, _options = {})
|
45
|
+
raise UndefinedCallError, "call method not defined in #{self.class.name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CMDx
|
4
|
+
# Registry for managing type coercion definitions and execution within tasks.
|
5
|
+
#
|
6
|
+
# This registry handles the registration and execution of coercions that convert
|
7
|
+
# parameter values from one type to another, supporting both built-in types and
|
8
|
+
# custom coercion logic.
|
9
|
+
class CoercionRegistry
|
10
|
+
|
11
|
+
# The internal hash storing coercion definitions.
|
12
|
+
#
|
13
|
+
# @return [Hash] hash containing coercion type keys and coercion class/callable values
|
14
|
+
attr_reader :registry
|
15
|
+
|
16
|
+
# Initializes a new coercion registry with default type coercions.
|
17
|
+
#
|
18
|
+
# Sets up the registry with built-in coercions for standard Ruby types
|
19
|
+
# including primitives, numerics, dates, and collections.
|
20
|
+
#
|
21
|
+
# @return [CoercionRegistry] a new coercion registry instance
|
22
|
+
#
|
23
|
+
# @example Creating a new registry
|
24
|
+
# registry = CoercionRegistry.new
|
25
|
+
# registry.registry[:string] # => Coercions::String
|
26
|
+
def initialize
|
27
|
+
@registry = {
|
28
|
+
array: Coercions::Array,
|
29
|
+
big_decimal: Coercions::BigDecimal,
|
30
|
+
boolean: Coercions::Boolean,
|
31
|
+
complex: Coercions::Complex,
|
32
|
+
date: Coercions::Date,
|
33
|
+
datetime: Coercions::DateTime,
|
34
|
+
float: Coercions::Float,
|
35
|
+
hash: Coercions::Hash,
|
36
|
+
integer: Coercions::Integer,
|
37
|
+
rational: Coercions::Rational,
|
38
|
+
string: Coercions::String,
|
39
|
+
time: Coercions::Time,
|
40
|
+
virtual: Coercions::Virtual
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Registers a custom coercion for a specific type.
|
45
|
+
#
|
46
|
+
# @param type [Symbol] the type identifier for the coercion
|
47
|
+
# @param coercion [Object] the coercion callable (class, proc, symbol, or string)
|
48
|
+
#
|
49
|
+
# @return [CoercionRegistry] returns self for method chaining
|
50
|
+
#
|
51
|
+
# @example Registering a custom coercion class
|
52
|
+
# registry.register(:uuid, UUIDCoercion)
|
53
|
+
#
|
54
|
+
# @example Registering a proc coercion
|
55
|
+
# registry.register(:upcase, ->(value, options) { value.to_s.upcase })
|
56
|
+
#
|
57
|
+
# @example Chaining registrations
|
58
|
+
# registry.register(:custom1, MyCoercion).register(:custom2, AnotherCoercion)
|
59
|
+
def register(type, coercion)
|
60
|
+
registry[type] = coercion
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Executes a coercion for the specified type and value.
|
65
|
+
#
|
66
|
+
# @param task [Task] the task instance executing the coercion
|
67
|
+
# @param type [Symbol] the coercion type to execute
|
68
|
+
# @param value [Object] the value to be coerced
|
69
|
+
# @param options [Hash] additional options for the coercion
|
70
|
+
#
|
71
|
+
# @return [Object] the coerced value
|
72
|
+
#
|
73
|
+
# @raise [UnknownCoercionError] when the coercion type is not registered
|
74
|
+
#
|
75
|
+
# @example Coercing a string to integer
|
76
|
+
# registry.call(task, :integer, "42")
|
77
|
+
# # => 42
|
78
|
+
#
|
79
|
+
# @example Coercing with options
|
80
|
+
# registry.call(task, :array, "a,b,c", delimiter: ",")
|
81
|
+
# # => ["a", "b", "c"]
|
82
|
+
def call(task, type, value, options = {})
|
83
|
+
raise UnknownCoercionError, "unknown coercion #{type}" unless registry.key?(type)
|
84
|
+
|
85
|
+
case coercion = registry[type]
|
86
|
+
when Symbol, String, Proc
|
87
|
+
task.cmdx_try(coercion, value, options)
|
88
|
+
else
|
89
|
+
coercion.call(value, options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
data/lib/cmdx/coercions/array.rb
CHANGED
@@ -2,47 +2,29 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Coercions
|
5
|
-
#
|
5
|
+
# Coercion class for converting values to arrays.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
#
|
11
|
-
# @example Basic array coercion
|
12
|
-
# class ProcessOrderTask < CMDx::Task
|
13
|
-
# optional :tags, type: :array, default: []
|
14
|
-
# optional :item_ids, type: :array
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# @example Coercion behavior
|
18
|
-
# Coercions::Array.call([1, 2, 3]) # => [1, 2, 3]
|
19
|
-
# Coercions::Array.call("hello") # => ["hello"]
|
20
|
-
# Coercions::Array.call('["a","b"]') # => ["a", "b"] (JSON)
|
21
|
-
# Coercions::Array.call('[1,2,3]') # => [1, 2, 3] (JSON)
|
22
|
-
# Coercions::Array.call(nil) # => []
|
23
|
-
# Coercions::Array.call(42) # => [42]
|
24
|
-
#
|
25
|
-
# @see ParameterValue Parameter value coercion
|
26
|
-
# @see Parameter Parameter type definitions
|
27
|
-
module Array
|
7
|
+
# This coercion handles conversion of various types to arrays, with special
|
8
|
+
# handling for JSON-formatted strings that start with "[".
|
9
|
+
class Array < Coercion
|
28
10
|
|
29
|
-
|
30
|
-
|
31
|
-
#
|
11
|
+
# Converts the given value to an array.
|
12
|
+
#
|
13
|
+
# @param value [Object] the value to convert to an array
|
14
|
+
# @param _options [Hash] optional configuration (currently unused)
|
15
|
+
#
|
16
|
+
# @return [Array] the converted array value
|
32
17
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# general array conversion.
|
18
|
+
# @raise [JSON::ParserError] if value is a JSON string that cannot be parsed
|
19
|
+
# @raise [TypeError] if the value cannot be converted to an array
|
36
20
|
#
|
37
|
-
# @
|
38
|
-
#
|
39
|
-
# @return [Array] coerced array value
|
40
|
-
# @raise [JSON::ParserError] if JSON parsing fails
|
21
|
+
# @example Converting a JSON string
|
22
|
+
# Coercions::Array.call('["a", "b", "c"]') #=> ["a", "b", "c"]
|
41
23
|
#
|
42
|
-
# @example
|
43
|
-
# Coercions::Array.call("
|
44
|
-
# Coercions::Array.call(
|
45
|
-
# Coercions::Array.call(
|
24
|
+
# @example Converting other values
|
25
|
+
# Coercions::Array.call("hello") #=> ["hello"]
|
26
|
+
# Coercions::Array.call(123) #=> [123]
|
27
|
+
# Coercions::Array.call(nil) #=> []
|
46
28
|
def call(value, _options = {})
|
47
29
|
if value.is_a?(::String) && value.start_with?("[")
|
48
30
|
JSON.parse(value)
|
@@ -2,45 +2,33 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Coercions
|
5
|
-
#
|
5
|
+
# Coercion class for converting values to BigDecimal.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# and
|
10
|
-
|
11
|
-
# @example Basic BigDecimal coercion
|
12
|
-
# class ProcessOrderTask < CMDx::Task
|
13
|
-
# required :total_amount, type: :big_decimal
|
14
|
-
# optional :tax_rate, type: :big_decimal, precision: 4
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# @example Coercion behavior
|
18
|
-
# Coercions::BigDecimal.call("123.45") # => #<BigDecimal:...,'0.12345E3',18(27)>
|
19
|
-
# Coercions::BigDecimal.call(42) # => #<BigDecimal:...,'0.42E2',9(18)>
|
20
|
-
# Coercions::BigDecimal.call("0.333333", precision: 6) # Custom precision
|
21
|
-
#
|
22
|
-
# @see ParameterValue Parameter value coercion
|
23
|
-
# @see Parameter Parameter type definitions
|
24
|
-
module BigDecimal
|
7
|
+
# This coercion handles conversion of various types to BigDecimal with
|
8
|
+
# configurable precision. It provides precise decimal arithmetic capabilities
|
9
|
+
# for financial calculations and other use cases requiring exact decimal representation.
|
10
|
+
class BigDecimal < Coercion
|
25
11
|
|
26
|
-
# Default precision for BigDecimal calculations
|
27
|
-
# @return [Integer] default precision value
|
28
12
|
DEFAULT_PRECISION = 14
|
29
13
|
|
30
|
-
|
31
|
-
|
32
|
-
#
|
14
|
+
# Converts the given value to a BigDecimal.
|
15
|
+
#
|
16
|
+
# @param value [Object] the value to convert to a BigDecimal
|
17
|
+
# @param options [Hash] optional configuration
|
18
|
+
# @option options [Integer] :precision the precision for the BigDecimal (defaults to 14)
|
19
|
+
#
|
20
|
+
# @return [BigDecimal] the converted BigDecimal value
|
21
|
+
#
|
22
|
+
# @raise [CoercionError] if the value cannot be converted to a BigDecimal
|
23
|
+
#
|
24
|
+
# @example Converting a string
|
25
|
+
# Coercions::BigDecimal.call('123.45') #=> #<BigDecimal:...,'0.12345E3',18(27)>
|
33
26
|
#
|
34
|
-
# @
|
35
|
-
#
|
36
|
-
# @option options [Integer] :precision decimal precision (default: 14)
|
37
|
-
# @return [BigDecimal] coerced BigDecimal value
|
38
|
-
# @raise [CoercionError] if coercion fails
|
27
|
+
# @example Converting with custom precision
|
28
|
+
# Coercions::BigDecimal.call('123.456789', precision: 10) #=> #<BigDecimal:...,'0.123456789E3',18(27)>
|
39
29
|
#
|
40
|
-
# @example
|
41
|
-
# Coercions::BigDecimal.call(
|
42
|
-
# Coercions::BigDecimal.call("0.333", precision: 10) # => BigDecimal with custom precision
|
43
|
-
# Coercions::BigDecimal.call(42.5) # => BigDecimal from float
|
30
|
+
# @example Converting an integer
|
31
|
+
# Coercions::BigDecimal.call(100) #=> #<BigDecimal:...,'0.1E3',9(18)>
|
44
32
|
def call(value, options = {})
|
45
33
|
BigDecimal(value, options[:precision] || DEFAULT_PRECISION)
|
46
34
|
rescue ArgumentError, TypeError
|