cmdx 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/rules/cursor-instructions.mdc +6 -0
- data/.rubocop.yml +16 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +42 -1
- data/README.md +72 -25
- data/docs/ai_prompts.md +309 -0
- data/docs/basics/call.md +225 -14
- data/docs/basics/chain.md +271 -0
- data/docs/basics/context.md +232 -33
- data/docs/basics/setup.md +76 -12
- data/docs/callbacks.md +273 -0
- data/docs/configuration.md +158 -28
- data/docs/getting_started.md +134 -22
- data/docs/interruptions/exceptions.md +189 -11
- data/docs/interruptions/faults.md +187 -44
- data/docs/interruptions/halt.md +179 -35
- data/docs/logging.md +194 -53
- data/docs/middlewares.md +735 -0
- data/docs/outcomes/result.md +296 -10
- data/docs/outcomes/states.md +212 -19
- data/docs/outcomes/statuses.md +284 -18
- data/docs/parameters/coercions.md +402 -29
- data/docs/parameters/defaults.md +249 -25
- data/docs/parameters/definitions.md +238 -72
- data/docs/parameters/namespacing.md +250 -27
- data/docs/parameters/validations.md +193 -168
- data/docs/testing.md +550 -0
- data/docs/tips_and_tricks.md +95 -43
- data/docs/workflows.md +319 -0
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +69 -0
- data/lib/cmdx/callback_registry.rb +106 -0
- data/lib/cmdx/chain.rb +190 -0
- data/lib/cmdx/chain_inspector.rb +149 -0
- data/lib/cmdx/chain_serializer.rb +175 -0
- data/lib/cmdx/coercions/array.rb +37 -0
- data/lib/cmdx/coercions/big_decimal.rb +33 -0
- data/lib/cmdx/coercions/boolean.rb +41 -1
- data/lib/cmdx/coercions/complex.rb +31 -0
- data/lib/cmdx/coercions/date.rb +39 -0
- data/lib/cmdx/coercions/date_time.rb +39 -0
- data/lib/cmdx/coercions/float.rb +31 -0
- data/lib/cmdx/coercions/hash.rb +42 -0
- data/lib/cmdx/coercions/integer.rb +32 -0
- data/lib/cmdx/coercions/rational.rb +31 -0
- data/lib/cmdx/coercions/string.rb +31 -0
- data/lib/cmdx/coercions/time.rb +39 -0
- data/lib/cmdx/coercions/virtual.rb +31 -0
- data/lib/cmdx/configuration.rb +217 -9
- data/lib/cmdx/context.rb +173 -2
- data/lib/cmdx/core_ext/hash.rb +72 -0
- data/lib/cmdx/core_ext/module.rb +94 -0
- data/lib/cmdx/core_ext/object.rb +105 -0
- data/lib/cmdx/correlator.rb +217 -0
- data/lib/cmdx/error.rb +210 -8
- data/lib/cmdx/errors.rb +256 -1
- data/lib/cmdx/fault.rb +177 -2
- data/lib/cmdx/faults.rb +158 -2
- data/lib/cmdx/immutator.rb +121 -2
- data/lib/cmdx/lazy_struct.rb +261 -18
- data/lib/cmdx/log_formatters/json.rb +46 -0
- data/lib/cmdx/log_formatters/key_value.rb +46 -0
- data/lib/cmdx/log_formatters/line.rb +54 -0
- data/lib/cmdx/log_formatters/logstash.rb +64 -0
- data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
- data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
- data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
- data/lib/cmdx/log_formatters/raw.rb +54 -0
- data/lib/cmdx/logger.rb +85 -0
- data/lib/cmdx/logger_ansi.rb +93 -7
- data/lib/cmdx/logger_serializer.rb +116 -0
- data/lib/cmdx/middleware.rb +74 -0
- data/lib/cmdx/middleware_registry.rb +106 -0
- data/lib/cmdx/middlewares/correlate.rb +266 -0
- data/lib/cmdx/middlewares/timeout.rb +232 -0
- data/lib/cmdx/parameter.rb +228 -1
- data/lib/cmdx/parameter_inspector.rb +61 -0
- data/lib/cmdx/parameter_registry.rb +125 -0
- data/lib/cmdx/parameter_serializer.rb +83 -0
- data/lib/cmdx/parameter_validator.rb +62 -0
- data/lib/cmdx/parameter_value.rb +109 -1
- data/lib/cmdx/parameters_inspector.rb +59 -0
- data/lib/cmdx/parameters_serializer.rb +102 -0
- data/lib/cmdx/railtie.rb +123 -3
- data/lib/cmdx/result.rb +399 -20
- data/lib/cmdx/result_ansi.rb +105 -9
- data/lib/cmdx/result_inspector.rb +76 -0
- data/lib/cmdx/result_logger.rb +90 -3
- data/lib/cmdx/result_serializer.rb +137 -0
- data/lib/cmdx/rspec/result_matchers.rb +917 -0
- data/lib/cmdx/rspec/task_matchers.rb +570 -0
- data/lib/cmdx/task.rb +409 -34
- data/lib/cmdx/task_serializer.rb +74 -2
- data/lib/cmdx/utils/ansi_color.rb +95 -0
- data/lib/cmdx/utils/log_timestamp.rb +48 -0
- data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
- data/lib/cmdx/utils/name_affix.rb +78 -0
- data/lib/cmdx/validators/custom.rb +82 -0
- data/lib/cmdx/validators/exclusion.rb +94 -0
- data/lib/cmdx/validators/format.rb +102 -8
- data/lib/cmdx/validators/inclusion.rb +104 -0
- data/lib/cmdx/validators/length.rb +128 -0
- data/lib/cmdx/validators/numeric.rb +128 -0
- data/lib/cmdx/validators/presence.rb +93 -7
- data/lib/cmdx/version.rb +7 -1
- data/lib/cmdx/workflow.rb +394 -0
- data/lib/cmdx.rb +25 -64
- data/lib/generators/cmdx/install_generator.rb +37 -1
- data/lib/generators/cmdx/task_generator.rb +69 -1
- data/lib/generators/cmdx/templates/install.rb +8 -12
- data/lib/generators/cmdx/workflow_generator.rb +109 -0
- metadata +54 -15
- data/docs/basics/run.md +0 -34
- data/docs/batch.md +0 -53
- data/docs/example.md +0 -82
- data/docs/hooks.md +0 -59
- data/lib/cmdx/batch.rb +0 -43
- data/lib/cmdx/parameters.rb +0 -34
- data/lib/cmdx/run.rb +0 -38
- data/lib/cmdx/run_inspector.rb +0 -26
- data/lib/cmdx/run_serializer.rb +0 -16
- data/lib/cmdx/task_hook.rb +0 -18
- data/lib/generators/cmdx/batch_generator.rb +0 -30
- /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
data/lib/cmdx/result_ansi.rb
CHANGED
@@ -1,26 +1,122 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
+
# ANSI color formatting module for result states and statuses.
|
5
|
+
#
|
6
|
+
# The ResultAnsi module provides ANSI color formatting for result state and
|
7
|
+
# status values to enhance readability in terminal output. It maps different
|
8
|
+
# result states and statuses to appropriate colors for visual distinction.
|
9
|
+
#
|
10
|
+
# @example Basic result colorization
|
11
|
+
# ResultAnsi.call("complete") # => Green colored text
|
12
|
+
# ResultAnsi.call("success") # => Green colored text
|
13
|
+
# ResultAnsi.call("failed") # => Red colored text
|
14
|
+
# ResultAnsi.call("interrupted") # => Red colored text
|
15
|
+
#
|
16
|
+
# @example Usage in log formatters
|
17
|
+
# result_data = { state: "complete", status: "success" }
|
18
|
+
# colored_state = ResultAnsi.call(result_data[:state])
|
19
|
+
# colored_status = ResultAnsi.call(result_data[:status])
|
20
|
+
#
|
21
|
+
# @example Integration with pretty formatters
|
22
|
+
# # Used internally by PrettyLine, PrettyJson, PrettyKeyValue formatters
|
23
|
+
# formatted_status = ResultAnsi.call("failed") # => Red "failed"
|
24
|
+
#
|
25
|
+
# @see CMDx::Result Result states and statuses
|
26
|
+
# @see CMDx::Utils::AnsiColor ANSI color utility functions
|
27
|
+
# @see CMDx::LogFormatters::PrettyLine Pretty line formatter with colors
|
4
28
|
module ResultAnsi
|
5
29
|
|
30
|
+
# Mapping of result states to ANSI colors.
|
31
|
+
#
|
32
|
+
# Maps Result state constants to their corresponding color codes
|
33
|
+
# for consistent visual representation of execution states.
|
6
34
|
STATE_COLORS = {
|
7
|
-
Result::INITIALIZED => :blue,
|
8
|
-
Result::EXECUTING => :yellow,
|
9
|
-
Result::COMPLETE => :green,
|
10
|
-
Result::INTERRUPTED => :red
|
35
|
+
Result::INITIALIZED => :blue, # Initial state - blue
|
36
|
+
Result::EXECUTING => :yellow, # Currently executing - yellow
|
37
|
+
Result::COMPLETE => :green, # Successfully completed - green
|
38
|
+
Result::INTERRUPTED => :red # Execution interrupted - red
|
11
39
|
}.freeze
|
40
|
+
|
41
|
+
# Mapping of result statuses to ANSI colors.
|
42
|
+
#
|
43
|
+
# Maps Result status constants to their corresponding color codes
|
44
|
+
# for consistent visual representation of execution outcomes.
|
12
45
|
STATUS_COLORS = {
|
13
|
-
Result::SUCCESS => :green,
|
14
|
-
Result::SKIPPED => :yellow,
|
15
|
-
Result::FAILED => :red
|
46
|
+
Result::SUCCESS => :green, # Successful completion - green
|
47
|
+
Result::SKIPPED => :yellow, # Intentionally skipped - yellow
|
48
|
+
Result::FAILED => :red # Failed execution - red
|
16
49
|
}.freeze
|
17
50
|
|
18
51
|
module_function
|
19
52
|
|
53
|
+
# Applies ANSI color formatting to a result state or status string.
|
54
|
+
#
|
55
|
+
# Formats the input string with appropriate ANSI color codes based on
|
56
|
+
# whether it matches a known result state or status value. Falls back
|
57
|
+
# to default color for unknown values.
|
58
|
+
#
|
59
|
+
# @param s [String] The state or status string to colorize
|
60
|
+
# @return [String] The string with ANSI color codes applied
|
61
|
+
#
|
62
|
+
# @example Colorizing result states
|
63
|
+
# ResultAnsi.call("initialized") # => "\e[34minitialized\e[0m" (blue)
|
64
|
+
# ResultAnsi.call("executing") # => "\e[33mexecuting\e[0m" (yellow)
|
65
|
+
# ResultAnsi.call("complete") # => "\e[32mcomplete\e[0m" (green)
|
66
|
+
# ResultAnsi.call("interrupted") # => "\e[31minterrupted\e[0m" (red)
|
67
|
+
#
|
68
|
+
# @example Colorizing result statuses
|
69
|
+
# ResultAnsi.call("success") # => "\e[32msuccess\e[0m" (green)
|
70
|
+
# ResultAnsi.call("skipped") # => "\e[33mskipped\e[0m" (yellow)
|
71
|
+
# ResultAnsi.call("failed") # => "\e[31mfailed\e[0m" (red)
|
72
|
+
#
|
73
|
+
# @example Unknown value
|
74
|
+
# ResultAnsi.call("unknown") # => "\e[39munknown\e[0m" (default color)
|
75
|
+
#
|
76
|
+
# @example Usage in result formatting
|
77
|
+
# result = ProcessOrderTask.call
|
78
|
+
# colored_state = ResultAnsi.call(result.state)
|
79
|
+
# colored_status = ResultAnsi.call(result.status)
|
80
|
+
# puts "Task #{colored_state} with #{colored_status}"
|
81
|
+
# # => "Task complete with success" (with appropriate colors)
|
20
82
|
def call(s)
|
21
|
-
|
83
|
+
Utils::AnsiColor.call(s, color: color(s))
|
84
|
+
end
|
22
85
|
|
23
|
-
|
86
|
+
# Determines the appropriate color for a result state or status string.
|
87
|
+
#
|
88
|
+
# Looks up the input string in both the STATE_COLORS and STATUS_COLORS
|
89
|
+
# mapping hashes to find the corresponding color symbol. First checks
|
90
|
+
# STATE_COLORS, then STATUS_COLORS, and falls back to the default color
|
91
|
+
# if no mapping is found in either hash.
|
92
|
+
#
|
93
|
+
# @param s [String] The state or status string to determine color for
|
94
|
+
# @return [Symbol] The color symbol for the state or status
|
95
|
+
#
|
96
|
+
# @example Result state color mapping
|
97
|
+
# color("initialized") # => :blue
|
98
|
+
# color("executing") # => :yellow
|
99
|
+
# color("complete") # => :green
|
100
|
+
# color("interrupted") # => :red
|
101
|
+
#
|
102
|
+
# @example Result status color mapping
|
103
|
+
# color("success") # => :green
|
104
|
+
# color("skipped") # => :yellow
|
105
|
+
# color("failed") # => :red
|
106
|
+
#
|
107
|
+
# @example Unknown state or status
|
108
|
+
# color("unknown") # => :default
|
109
|
+
# color("pending") # => :default
|
110
|
+
#
|
111
|
+
# @example Precedence behavior
|
112
|
+
# # If a string exists in both hashes, STATE_COLORS takes precedence
|
113
|
+
# color("some_value") # => STATE_COLORS["some_value"] || STATUS_COLORS["some_value"] || :default
|
114
|
+
#
|
115
|
+
# @note STATE_COLORS mapping is checked before STATUS_COLORS mapping
|
116
|
+
# @see STATE_COLORS The mapping hash for result states
|
117
|
+
# @see STATUS_COLORS The mapping hash for result statuses
|
118
|
+
def color(s)
|
119
|
+
STATE_COLORS[s] || STATUS_COLORS[s] || :default
|
24
120
|
end
|
25
121
|
|
26
122
|
end
|
@@ -1,8 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
+
# Result inspection utility for generating human-readable result descriptions.
|
5
|
+
#
|
6
|
+
# The ResultInspector module provides functionality to convert result hash
|
7
|
+
# representations into formatted, human-readable strings. It handles special
|
8
|
+
# formatting for different result attributes and provides consistent ordering
|
9
|
+
# of result information.
|
10
|
+
#
|
11
|
+
# @example Basic result inspection
|
12
|
+
# result_hash = {
|
13
|
+
# class: "ProcessOrderTask",
|
14
|
+
# type: "Task",
|
15
|
+
# index: 0,
|
16
|
+
# id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
17
|
+
# state: "complete",
|
18
|
+
# status: "success",
|
19
|
+
# outcome: "success",
|
20
|
+
# metadata: { order_id: 123 },
|
21
|
+
# runtime: 0.5
|
22
|
+
# }
|
23
|
+
#
|
24
|
+
# ResultInspector.call(result_hash)
|
25
|
+
# # => "ProcessOrderTask: type=Task index=0 id=018c2b95-b764-7615-a924-cc5b910ed1e5 state=complete status=success outcome=success metadata={order_id: 123} runtime=0.5"
|
26
|
+
#
|
27
|
+
# @example Result with failure information
|
28
|
+
# failed_result = {
|
29
|
+
# class: "ProcessOrderTask",
|
30
|
+
# type: "Task",
|
31
|
+
# index: 1,
|
32
|
+
# id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
33
|
+
# state: "interrupted",
|
34
|
+
# status: "failed",
|
35
|
+
# outcome: "failed",
|
36
|
+
# caused_failure: { index: 0, class: "ValidationTask", id: "018c2b95..." },
|
37
|
+
# threw_failure: { index: 0, class: "ValidationTask", id: "018c2b95..." }
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# ResultInspector.call(failed_result)
|
41
|
+
# # => "ProcessOrderTask: type=Task index=1 id=018c2b95... state=interrupted status=failed outcome=failed caused_failure=<[0] ValidationTask: 018c2b95...> threw_failure=<[0] ValidationTask: 018c2b95...>"
|
42
|
+
#
|
43
|
+
# @see CMDx::Result Result hash serialization via to_h
|
44
|
+
# @see CMDx::ResultSerializer Result-to-hash conversion
|
4
45
|
module ResultInspector
|
5
46
|
|
47
|
+
# Ordered keys for consistent result inspection output.
|
48
|
+
#
|
49
|
+
# Defines the order in which result attributes are displayed in the
|
50
|
+
# inspection string, ensuring consistent and logical presentation.
|
6
51
|
ORDERED_KEYS = %i[
|
7
52
|
class type index id state status outcome metadata
|
8
53
|
tags pid runtime caused_failure threw_failure
|
@@ -10,6 +55,37 @@ module CMDx
|
|
10
55
|
|
11
56
|
module_function
|
12
57
|
|
58
|
+
# Converts a result hash to a human-readable string representation.
|
59
|
+
#
|
60
|
+
# Formats result data into a structured string with proper ordering and
|
61
|
+
# special handling for different attribute types. The class name appears
|
62
|
+
# first followed by a colon, and failure references are specially formatted.
|
63
|
+
#
|
64
|
+
# @param result [Hash] The result hash to inspect
|
65
|
+
# @return [String] Formatted result description
|
66
|
+
#
|
67
|
+
# @example Simple result inspection
|
68
|
+
# ResultInspector.call(result_hash)
|
69
|
+
# # => "ProcessOrderTask: type=Task index=0 id=018c2b95... state=complete status=success"
|
70
|
+
#
|
71
|
+
# @example Result with metadata
|
72
|
+
# result_with_metadata = { class: "MyTask", metadata: { user_id: 123, action: "create" } }
|
73
|
+
# ResultInspector.call(result_with_metadata)
|
74
|
+
# # => "MyTask: metadata={user_id: 123, action: create}"
|
75
|
+
#
|
76
|
+
# @example Result with failure references
|
77
|
+
# result_with_failures = {
|
78
|
+
# class: "MainTask",
|
79
|
+
# caused_failure: { index: 2, class: "SubTask", id: "abc123" },
|
80
|
+
# threw_failure: { index: 1, class: "HelperTask", id: "def456" }
|
81
|
+
# }
|
82
|
+
# ResultInspector.call(result_with_failures)
|
83
|
+
# # => "MainTask: caused_failure=<[2] SubTask: abc123> threw_failure=<[1] HelperTask: def456>"
|
84
|
+
#
|
85
|
+
# @example Result with runtime information
|
86
|
+
# result_with_runtime = { class: "SlowTask", runtime: 2.5, pid: 1234 }
|
87
|
+
# ResultInspector.call(result_with_runtime)
|
88
|
+
# # => "SlowTask: runtime=2.5 pid=1234"
|
13
89
|
def call(result)
|
14
90
|
ORDERED_KEYS.filter_map do |key|
|
15
91
|
next unless result.key?(key)
|
data/lib/cmdx/result_logger.rb
CHANGED
@@ -1,16 +1,103 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
+
# Result-specific logging module for task execution outcomes.
|
5
|
+
#
|
6
|
+
# The ResultLogger module provides specialized logging functionality for
|
7
|
+
# CMDx task results. It automatically maps result statuses to appropriate
|
8
|
+
# log severity levels and handles conditional logging based on logger
|
9
|
+
# availability and configuration.
|
10
|
+
#
|
11
|
+
# @example Successful task result logging
|
12
|
+
# task = ProcessOrderTask.call(order_id: 123)
|
13
|
+
# ResultLogger.call(task.result)
|
14
|
+
# # Logs at INFO level: "ProcessOrderTask completed successfully"
|
15
|
+
#
|
16
|
+
# @example Failed task result logging
|
17
|
+
# task = ProcessOrderTask.call(invalid_params)
|
18
|
+
# ResultLogger.call(task.result)
|
19
|
+
# # Logs at ERROR level: "ProcessOrderTask failed with errors"
|
20
|
+
#
|
21
|
+
# @example Skipped task result logging
|
22
|
+
# task = ProcessOrderTask.new
|
23
|
+
# task.skip!(reason: "Order already processed")
|
24
|
+
# ResultLogger.call(task.result)
|
25
|
+
# # Logs at WARN level: "ProcessOrderTask was skipped"
|
26
|
+
#
|
27
|
+
# @example Integration with task execution
|
28
|
+
# class ProcessOrderTask < CMDx::Task
|
29
|
+
# def call
|
30
|
+
# # Task logic here
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# # ResultLogger.call is automatically invoked after task execution
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# @see CMDx::Result Result object status and state management
|
37
|
+
# @see CMDx::Logger Logger configuration and setup
|
38
|
+
# @see CMDx::Task Task execution and result handling
|
4
39
|
module ResultLogger
|
5
40
|
|
41
|
+
# Mapping of result statuses to corresponding log severity levels.
|
42
|
+
#
|
43
|
+
# Maps CMDx result status constants to Ruby Logger severity levels
|
44
|
+
# to ensure appropriate logging levels for different task outcomes.
|
6
45
|
STATUS_TO_SEVERITY = {
|
7
|
-
Result::SUCCESS => :info,
|
8
|
-
Result::SKIPPED => :warn,
|
9
|
-
Result::FAILED => :error
|
46
|
+
Result::SUCCESS => :info, # Successful task completion
|
47
|
+
Result::SKIPPED => :warn, # Task was skipped
|
48
|
+
Result::FAILED => :error # Task execution failed
|
10
49
|
}.freeze
|
11
50
|
|
12
51
|
module_function
|
13
52
|
|
53
|
+
# Logs a task result at the appropriate severity level.
|
54
|
+
#
|
55
|
+
# Determines the appropriate log severity based on the result status
|
56
|
+
# and logs the result object using the task's configured logger.
|
57
|
+
# Does nothing if no logger is configured for the task.
|
58
|
+
#
|
59
|
+
# @param result [CMDx::Result] The task result to log
|
60
|
+
# @return [void]
|
61
|
+
#
|
62
|
+
# @example Logging a successful result
|
63
|
+
# task = ProcessOrderTask.call(order_id: 123)
|
64
|
+
# ResultLogger.call(task.result)
|
65
|
+
# # Logs at INFO level with result details
|
66
|
+
#
|
67
|
+
# @example Logging a failed result
|
68
|
+
# task = ProcessOrderTask.new
|
69
|
+
# task.fail!(reason: "Invalid order ID")
|
70
|
+
# ResultLogger.call(task.result)
|
71
|
+
# # Logs at ERROR level with failure details
|
72
|
+
#
|
73
|
+
# @example Logging a skipped result
|
74
|
+
# task = ProcessOrderTask.new
|
75
|
+
# task.skip!(reason: "Order already processed")
|
76
|
+
# ResultLogger.call(task.result)
|
77
|
+
# # Logs at WARN level with skip reason
|
78
|
+
#
|
79
|
+
# @example No logger configured
|
80
|
+
# class SimpleTask < CMDx::Task
|
81
|
+
# # No logger setting
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# task = SimpleTask.call
|
85
|
+
# ResultLogger.call(task.result) # Does nothing - no logger available
|
86
|
+
#
|
87
|
+
# @example Custom logger configuration
|
88
|
+
# class MyTask < CMDx::Task
|
89
|
+
# task_settings!(
|
90
|
+
# logger: Logger.new(STDOUT),
|
91
|
+
# log_formatter: CMDx::LogFormatters::Json.new
|
92
|
+
# )
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# task = MyTask.call
|
96
|
+
# ResultLogger.call(task.result) # Logs in JSON format to STDOUT
|
97
|
+
#
|
98
|
+
# @note This method is typically called automatically by the CMDx framework
|
99
|
+
# after task execution completes, ensuring that all task results are
|
100
|
+
# properly logged according to their outcome.
|
14
101
|
def call(result)
|
15
102
|
logger = result.task.send(:logger)
|
16
103
|
return if logger.nil?
|
@@ -1,8 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
+
# Result serialization utility for converting Result objects to hash representations.
|
5
|
+
#
|
6
|
+
# The ResultSerializer module provides functionality to serialize Result instances
|
7
|
+
# into structured hash representations suitable for inspection, logging, debugging,
|
8
|
+
# and data interchange. It handles failure chain information and integrates with
|
9
|
+
# TaskSerializer for comprehensive result data.
|
10
|
+
#
|
11
|
+
# @example Basic result serialization
|
12
|
+
# task = ProcessOrderTask.call(order_id: 123)
|
13
|
+
# result = task.result
|
14
|
+
#
|
15
|
+
# ResultSerializer.call(result)
|
16
|
+
# # => {
|
17
|
+
# # class: "ProcessOrderTask",
|
18
|
+
# # type: "Task",
|
19
|
+
# # index: 0,
|
20
|
+
# # chain_id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
21
|
+
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
22
|
+
# # tags: [],
|
23
|
+
# # state: "complete",
|
24
|
+
# # status: "success",
|
25
|
+
# # outcome: "success",
|
26
|
+
# # metadata: {},
|
27
|
+
# # runtime: 0.5
|
28
|
+
# # }
|
29
|
+
#
|
30
|
+
# @example Failed result serialization
|
31
|
+
# task = ProcessOrderTask.new
|
32
|
+
# task.fail!(reason: "Invalid order data", code: 422)
|
33
|
+
# result = task.result
|
34
|
+
#
|
35
|
+
# ResultSerializer.call(result)
|
36
|
+
# # => {
|
37
|
+
# # class: "ProcessOrderTask",
|
38
|
+
# # type: "Task",
|
39
|
+
# # index: 0,
|
40
|
+
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
41
|
+
# # state: "interrupted",
|
42
|
+
# # status: "failed",
|
43
|
+
# # outcome: "failed",
|
44
|
+
# # metadata: { reason: "Invalid order data", code: 422 },
|
45
|
+
# # runtime: 0.1,
|
46
|
+
# # caused_failure: { ... }, # Failure chain information
|
47
|
+
# # threw_failure: { ... }
|
48
|
+
# # }
|
49
|
+
#
|
50
|
+
# @example Result with failure chain
|
51
|
+
# # When a result has failure chain information, it's included but
|
52
|
+
# # stripped of recursive caused_failure/threw_failure to prevent cycles
|
53
|
+
# ResultSerializer.call(result_with_failures)
|
54
|
+
# # => {
|
55
|
+
# # # ... standard result data ...
|
56
|
+
# # caused_failure: {
|
57
|
+
# # class: "ValidationTask",
|
58
|
+
# # index: 1,
|
59
|
+
# # state: "interrupted",
|
60
|
+
# # status: "failed"
|
61
|
+
# # # caused_failure and threw_failure are stripped to prevent recursion
|
62
|
+
# # },
|
63
|
+
# # threw_failure: {
|
64
|
+
# # class: "ProcessingTask",
|
65
|
+
# # index: 2,
|
66
|
+
# # state: "interrupted",
|
67
|
+
# # status: "failed"
|
68
|
+
# # # caused_failure and threw_failure are stripped to prevent recursion
|
69
|
+
# # }
|
70
|
+
# # }
|
71
|
+
#
|
72
|
+
# @see CMDx::Result Result object creation and state management
|
73
|
+
# @see CMDx::TaskSerializer Task serialization functionality
|
74
|
+
# @see CMDx::ResultInspector Human-readable result formatting
|
4
75
|
module ResultSerializer
|
5
76
|
|
77
|
+
# Proc for stripping failure chain information to prevent recursion.
|
78
|
+
#
|
79
|
+
# This proc is used to include failure chain information (caused_failure
|
80
|
+
# and threw_failure) while preventing infinite recursion by stripping
|
81
|
+
# the same fields from nested failure objects.
|
6
82
|
STRIP_FAILURE = proc do |h, r, k|
|
7
83
|
unless r.send(:"#{k}?")
|
8
84
|
# Strip caused/threw failures since its the same info as the log line
|
@@ -12,6 +88,67 @@ module CMDx
|
|
12
88
|
|
13
89
|
module_function
|
14
90
|
|
91
|
+
# Converts a Result object to a hash representation.
|
92
|
+
#
|
93
|
+
# Serializes a Result instance into a structured hash containing all
|
94
|
+
# relevant result information including task data, execution state,
|
95
|
+
# status, metadata, runtime, and failure chain information.
|
96
|
+
#
|
97
|
+
# @param result [CMDx::Result] The result object to serialize
|
98
|
+
# @return [Hash] Structured hash representation of the result
|
99
|
+
#
|
100
|
+
# @example Successful result serialization
|
101
|
+
# result = ProcessOrderTask.call(order_id: 123).result
|
102
|
+
# ResultSerializer.call(result)
|
103
|
+
# # => {
|
104
|
+
# # class: "ProcessOrderTask",
|
105
|
+
# # type: "Task",
|
106
|
+
# # index: 0,
|
107
|
+
# # chain_id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
108
|
+
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
109
|
+
# # tags: [],
|
110
|
+
# # state: "complete",
|
111
|
+
# # status: "success",
|
112
|
+
# # outcome: "success",
|
113
|
+
# # metadata: {},
|
114
|
+
# # runtime: 0.5
|
115
|
+
# # }
|
116
|
+
#
|
117
|
+
# @example Failed result with metadata
|
118
|
+
# task = ProcessOrderTask.new
|
119
|
+
# task.fail!(reason: "Validation failed", errors: ["Invalid email"])
|
120
|
+
# result = task.result
|
121
|
+
#
|
122
|
+
# ResultSerializer.call(result)
|
123
|
+
# # => {
|
124
|
+
# # class: "ProcessOrderTask",
|
125
|
+
# # type: "Task",
|
126
|
+
# # index: 0,
|
127
|
+
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
128
|
+
# # state: "interrupted",
|
129
|
+
# # status: "failed",
|
130
|
+
# # outcome: "failed",
|
131
|
+
# # metadata: { reason: "Validation failed", errors: ["Invalid email"] },
|
132
|
+
# # runtime: 0.1
|
133
|
+
# # }
|
134
|
+
#
|
135
|
+
# @example Skipped result serialization
|
136
|
+
# task = ProcessOrderTask.new
|
137
|
+
# task.skip!(reason: "Order already processed")
|
138
|
+
# result = task.result
|
139
|
+
#
|
140
|
+
# ResultSerializer.call(result)
|
141
|
+
# # => {
|
142
|
+
# # class: "ProcessOrderTask",
|
143
|
+
# # type: "Task",
|
144
|
+
# # index: 0,
|
145
|
+
# # id: "018c2b95-b764-7615-a924-cc5b910ed1e5",
|
146
|
+
# # state: "interrupted",
|
147
|
+
# # status: "skipped",
|
148
|
+
# # outcome: "skipped",
|
149
|
+
# # metadata: { reason: "Order already processed" },
|
150
|
+
# # runtime: 0.05
|
151
|
+
# # }
|
15
152
|
def call(result)
|
16
153
|
TaskSerializer.call(result.task).tap do |hash|
|
17
154
|
hash.merge!(
|