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
data/lib/cmdx/result_ansi.rb
CHANGED
@@ -1,47 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
# ANSI color formatting
|
4
|
+
# ANSI color formatting utilities for result states and statuses.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
6
|
+
# This module provides functionality to apply appropriate ANSI colors to
|
7
|
+
# result states and statuses for enhanced terminal output visibility.
|
8
|
+
# It maps different execution states and statuses to their corresponding
|
9
|
+
# colors and delegates the actual color application to the AnsiColor utility.
|
28
10
|
module ResultAnsi
|
29
11
|
|
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.
|
34
12
|
STATE_COLORS = {
|
35
13
|
Result::INITIALIZED => :blue, # Initial state - blue
|
36
14
|
Result::EXECUTING => :yellow, # Currently executing - yellow
|
37
15
|
Result::COMPLETE => :green, # Successfully completed - green
|
38
16
|
Result::INTERRUPTED => :red # Execution interrupted - red
|
39
17
|
}.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.
|
45
18
|
STATUS_COLORS = {
|
46
19
|
Result::SUCCESS => :green, # Successful completion - green
|
47
20
|
Result::SKIPPED => :yellow, # Intentionally skipped - yellow
|
@@ -52,69 +25,44 @@ module CMDx
|
|
52
25
|
|
53
26
|
# Applies ANSI color formatting to a result state or status string.
|
54
27
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
28
|
+
# Takes a result state or status string and applies the appropriate ANSI
|
29
|
+
# color formatting using the predefined color mappings. This provides
|
30
|
+
# visual distinction for different execution outcomes in terminal output.
|
58
31
|
#
|
59
|
-
# @param s [String]
|
60
|
-
# @return [String] The string with ANSI color codes applied
|
32
|
+
# @param s [String] the result state or status string to colorize
|
61
33
|
#
|
62
|
-
# @
|
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)
|
34
|
+
# @return [String] the input string with ANSI color codes applied
|
67
35
|
#
|
68
|
-
# @example
|
69
|
-
# ResultAnsi.call("success")
|
70
|
-
# ResultAnsi.call("skipped") # => "\e[33mskipped\e[0m" (yellow)
|
71
|
-
# ResultAnsi.call("failed") # => "\e[31mfailed\e[0m" (red)
|
36
|
+
# @example Colorize a success status
|
37
|
+
# ResultAnsi.call("success") #=> "\e[0;32;49msuccess\e[0m" (green)
|
72
38
|
#
|
73
|
-
# @example
|
74
|
-
# ResultAnsi.call("
|
39
|
+
# @example Colorize a failed status
|
40
|
+
# ResultAnsi.call("failed") #=> "\e[0;31;49mfailed\e[0m" (red)
|
75
41
|
#
|
76
|
-
# @example
|
77
|
-
#
|
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)
|
42
|
+
# @example Colorize an executing state
|
43
|
+
# ResultAnsi.call("executing") #=> "\e[0;33;49mexecuting\e[0m" (yellow)
|
82
44
|
def call(s)
|
83
45
|
Utils::AnsiColor.call(s, color: color(s))
|
84
46
|
end
|
85
47
|
|
86
|
-
# Determines the appropriate color for a result state or status
|
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.
|
48
|
+
# Determines the appropriate color for a result state or status.
|
92
49
|
#
|
93
|
-
#
|
94
|
-
#
|
50
|
+
# Looks up the color mapping for the given state or status string,
|
51
|
+
# returning the corresponding color symbol or :default if no specific
|
52
|
+
# mapping is found.
|
95
53
|
#
|
96
|
-
# @
|
97
|
-
# color("initialized") # => :blue
|
98
|
-
# color("executing") # => :yellow
|
99
|
-
# color("complete") # => :green
|
100
|
-
# color("interrupted") # => :red
|
54
|
+
# @param s [String] the result state or status string to find color for
|
101
55
|
#
|
102
|
-
# @
|
103
|
-
# color("success") # => :green
|
104
|
-
# color("skipped") # => :yellow
|
105
|
-
# color("failed") # => :red
|
56
|
+
# @return [Symbol] the color symbol (:blue, :yellow, :green, :red, or :default)
|
106
57
|
#
|
107
|
-
# @example
|
108
|
-
# color("
|
109
|
-
# color("pending") # => :default
|
58
|
+
# @example Get color for success status
|
59
|
+
# ResultAnsi.color("success") #=> :green
|
110
60
|
#
|
111
|
-
# @example
|
112
|
-
#
|
113
|
-
# color("some_value") # => STATE_COLORS["some_value"] || STATUS_COLORS["some_value"] || :default
|
61
|
+
# @example Get color for unknown value
|
62
|
+
# ResultAnsi.color("unknown") #=> :default
|
114
63
|
#
|
115
|
-
# @
|
116
|
-
#
|
117
|
-
# @see STATUS_COLORS The mapping hash for result statuses
|
64
|
+
# @example Get color for executing state
|
65
|
+
# ResultAnsi.color("executing") #=> :yellow
|
118
66
|
def color(s)
|
119
67
|
STATE_COLORS[s] || STATUS_COLORS[s] || :default
|
120
68
|
end
|
@@ -1,53 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
# Result inspection
|
4
|
+
# Result inspection and formatting utilities for readable result representation.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
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
|
6
|
+
# This module provides functionality to format result metadata into human-readable
|
7
|
+
# strings for debugging, logging, and introspection purposes. It processes result
|
8
|
+
# hashes and displays essential result information in a structured, ordered format
|
9
|
+
# that emphasizes the most important attributes first.
|
45
10
|
module ResultInspector
|
46
11
|
|
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.
|
51
12
|
ORDERED_KEYS = %i[
|
52
13
|
class type index id state status outcome metadata
|
53
14
|
tags pid runtime caused_failure threw_failure
|
@@ -55,37 +16,40 @@ module CMDx
|
|
55
16
|
|
56
17
|
module_function
|
57
18
|
|
58
|
-
#
|
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.
|
19
|
+
# Formats a result hash into a human-readable string representation.
|
63
20
|
#
|
64
|
-
#
|
65
|
-
#
|
21
|
+
# This method converts result metadata into a structured string format that
|
22
|
+
# displays key result information in a predefined order. It handles special
|
23
|
+
# formatting for class names, failure references, and standard key-value pairs.
|
24
|
+
# The method filters the result hash to only include keys defined in ORDERED_KEYS
|
25
|
+
# and applies appropriate formatting based on the key type.
|
66
26
|
#
|
67
|
-
# @
|
68
|
-
#
|
69
|
-
#
|
27
|
+
# @param result [Hash] the result hash to format
|
28
|
+
# @option result [String] :class the class name of the task or workflow
|
29
|
+
# @option result [String] :type the type identifier (e.g., "Task", "Workflow")
|
30
|
+
# @option result [Integer] :index the position index in the execution chain
|
31
|
+
# @option result [String] :id the unique identifier of the result
|
32
|
+
# @option result [String] :state the execution state (e.g., "executed", "skipped")
|
33
|
+
# @option result [String] :status the execution status (e.g., "success", "failure")
|
34
|
+
# @option result [String] :outcome the overall outcome (e.g., "good", "bad")
|
35
|
+
# @option result [Hash] :metadata additional metadata associated with the result
|
36
|
+
# @option result [Array] :tags the tags associated with the result
|
37
|
+
# @option result [Integer] :pid the process ID if applicable
|
38
|
+
# @option result [Float] :runtime the execution runtime in seconds
|
39
|
+
# @option result [Hash] :caused_failure reference to a failure this result caused
|
40
|
+
# @option result [Hash] :threw_failure reference to a failure this result threw
|
70
41
|
#
|
71
|
-
# @
|
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}"
|
42
|
+
# @return [String] a formatted string representation of the result with key information
|
75
43
|
#
|
76
|
-
# @example
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
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>"
|
44
|
+
# @example Format a successful task result
|
45
|
+
# result = MyTask.call
|
46
|
+
# ResultInspector.call(result)
|
47
|
+
# #=> "MyTask: type=Task index=0 id=abc123 state=executed status=success outcome=good"
|
84
48
|
#
|
85
|
-
# @example
|
86
|
-
#
|
87
|
-
# ResultInspector.call(
|
88
|
-
#
|
49
|
+
# @example Format a result with failure reference
|
50
|
+
# result = MyTask.call
|
51
|
+
# ResultInspector.call(result)
|
52
|
+
# #=> "MyTask: index=1 state=executed status=failure caused_failure=<[2] ValidationError: def456>"
|
89
53
|
def call(result)
|
90
54
|
ORDERED_KEYS.filter_map do |key|
|
91
55
|
next unless result.key?(key)
|
data/lib/cmdx/result_logger.rb
CHANGED
@@ -1,47 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Logger utilities for task execution results.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
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
|
6
|
+
# This module provides functionality to log task execution results with
|
7
|
+
# appropriate severity levels based on the result status. It automatically
|
8
|
+
# determines the correct log level (info, warn, error) based on whether
|
9
|
+
# the task succeeded, was skipped, or failed, and delegates to the task's
|
10
|
+
# configured logger instance.
|
39
11
|
module ResultLogger
|
40
12
|
|
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.
|
45
13
|
STATUS_TO_SEVERITY = {
|
46
14
|
Result::SUCCESS => :info, # Successful task completion
|
47
15
|
Result::SKIPPED => :warn, # Task was skipped
|
@@ -50,54 +18,32 @@ module CMDx
|
|
50
18
|
|
51
19
|
module_function
|
52
20
|
|
53
|
-
# Logs a task result
|
21
|
+
# Logs a task execution result with the appropriate severity level.
|
54
22
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
23
|
+
# Retrieves the logger from the task instance and logs the result object
|
24
|
+
# using the severity level determined by the result's status. If no logger
|
25
|
+
# is configured for the task, the method returns early without logging.
|
26
|
+
# The logger level is temporarily set to match the severity to ensure
|
27
|
+
# the message is captured regardless of current log level configuration.
|
28
|
+
#
|
29
|
+
# @param result [CMDx::Result] the task execution result to log
|
58
30
|
#
|
59
|
-
# @param result [CMDx::Result] The task result to log
|
60
31
|
# @return [void]
|
61
32
|
#
|
62
|
-
# @example
|
63
|
-
# task =
|
33
|
+
# @example Log a successful task result
|
34
|
+
# task = ProcessDataTask.call(data: "input")
|
64
35
|
# ResultLogger.call(task.result)
|
65
|
-
# # Logs at
|
36
|
+
# # Logs at :info level: "Result: ProcessDataTask completed successfully"
|
66
37
|
#
|
67
|
-
# @example
|
68
|
-
# task =
|
69
|
-
# task.fail!(reason: "Invalid order ID")
|
38
|
+
# @example Log a failed task result
|
39
|
+
# task = ValidateDataTask.call(data: "invalid")
|
70
40
|
# ResultLogger.call(task.result)
|
71
|
-
# # Logs at
|
41
|
+
# # Logs at :error level: "Result: ValidateDataTask failed with error"
|
72
42
|
#
|
73
|
-
# @example
|
74
|
-
# task =
|
75
|
-
# task.skip!(reason: "Order already processed")
|
43
|
+
# @example Log a skipped task result
|
44
|
+
# task = ConditionalTask.call(condition: false)
|
76
45
|
# ResultLogger.call(task.result)
|
77
|
-
# # Logs at
|
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.
|
46
|
+
# # Logs at :warn level: "Result: ConditionalTask was skipped"
|
101
47
|
def call(result)
|
102
48
|
logger = result.task.send(:logger)
|
103
49
|
return if logger.nil?
|
@@ -1,84 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
# Result serialization
|
4
|
+
# Result serialization module for converting result objects to hash format.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
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
|
6
|
+
# This module provides functionality to serialize result objects into a
|
7
|
+
# standardized hash representation that includes essential metadata about
|
8
|
+
# the result such as task information, execution state, status, outcome,
|
9
|
+
# metadata, and runtime. For failed results, it intelligently strips
|
10
|
+
# redundant failure information to avoid duplication in serialized output.
|
75
11
|
module ResultSerializer
|
76
12
|
|
77
|
-
# Proc for stripping failure
|
78
|
-
#
|
79
|
-
#
|
80
|
-
# and threw_failure) while preventing infinite recursion by stripping
|
81
|
-
# the same fields from nested failure objects.
|
13
|
+
# Proc for stripping failure information from serialized results.
|
14
|
+
# Removes caused_failure and threw_failure keys when the result doesn't
|
15
|
+
# have the corresponding failure state, avoiding redundant information.
|
82
16
|
STRIP_FAILURE = proc do |h, r, k|
|
83
17
|
unless r.send(:"#{k}?")
|
84
18
|
# Strip caused/threw failures since its the same info as the log line
|
@@ -88,66 +22,66 @@ module CMDx
|
|
88
22
|
|
89
23
|
module_function
|
90
24
|
|
91
|
-
#
|
25
|
+
# Serializes a result object into a hash representation.
|
92
26
|
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
27
|
+
# Converts a result instance into a standardized hash format containing
|
28
|
+
# task metadata and execution information. For failed results, applies
|
29
|
+
# intelligent failure stripping to remove redundant caused_failure and
|
30
|
+
# threw_failure information that would duplicate log output.
|
96
31
|
#
|
97
|
-
# @param result [CMDx::Result]
|
98
|
-
# @return [Hash] Structured hash representation of the result
|
32
|
+
# @param result [CMDx::Result] the result object to serialize
|
99
33
|
#
|
100
|
-
# @
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# # runtime: 0.5
|
115
|
-
# # }
|
34
|
+
# @return [Hash] a hash containing the result's metadata and execution information
|
35
|
+
# @option return [Integer] :index the result's position index in the execution chain
|
36
|
+
# @option return [String] :chain_id the unique identifier of the result's execution chain
|
37
|
+
# @option return [String] :type the task type, either "Task" or "Workflow"
|
38
|
+
# @option return [String] :class the full class name of the task
|
39
|
+
# @option return [String] :id the unique identifier of the task instance
|
40
|
+
# @option return [Array] :tags the tags associated with the task from cmd settings
|
41
|
+
# @option return [Symbol] :state the execution state (:executing, :complete, :interrupted)
|
42
|
+
# @option return [Symbol] :status the execution status (:success, :failed, :skipped)
|
43
|
+
# @option return [Symbol] :outcome the execution outcome (:good, :bad)
|
44
|
+
# @option return [Hash] :metadata additional metadata collected during execution
|
45
|
+
# @option return [Float] :runtime the execution runtime in seconds
|
46
|
+
# @option return [Hash] :caused_failure failure information if result caused a failure (stripped for non-failed results)
|
47
|
+
# @option return [Hash] :threw_failure failure information if result threw a failure (stripped for non-failed results)
|
116
48
|
#
|
117
|
-
# @
|
118
|
-
# task = ProcessOrderTask.new
|
119
|
-
# task.fail!(reason: "Validation failed", errors: ["Invalid email"])
|
120
|
-
# result = task.result
|
49
|
+
# @raise [NoMethodError] if the result doesn't respond to required methods
|
121
50
|
#
|
51
|
+
# @example Serialize a successful result
|
52
|
+
# task = SuccessfulTask.new(data: "test")
|
122
53
|
# ResultSerializer.call(result)
|
123
|
-
#
|
124
|
-
# # class: "ProcessOrderTask",
|
125
|
-
# # type: "Task",
|
54
|
+
# #=> {
|
126
55
|
# # index: 0,
|
127
|
-
# #
|
128
|
-
# #
|
129
|
-
# #
|
130
|
-
# #
|
131
|
-
# #
|
132
|
-
# #
|
56
|
+
# # chain_id: "abc123",
|
57
|
+
# # type: "Task",
|
58
|
+
# # class: "SuccessfulTask",
|
59
|
+
# # id: "def456",
|
60
|
+
# # tags: [],
|
61
|
+
# # state: :complete,
|
62
|
+
# # status: :success,
|
63
|
+
# # outcome: :good,
|
64
|
+
# # metadata: {},
|
65
|
+
# # runtime: 0.045
|
133
66
|
# # }
|
134
67
|
#
|
135
|
-
# @example
|
136
|
-
# task =
|
137
|
-
# task.
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
# # => {
|
142
|
-
# # class: "ProcessOrderTask",
|
68
|
+
# @example Serialize a failed result with failure stripping
|
69
|
+
# task = FailingTask.call
|
70
|
+
# ResultSerializer.call(task.result)
|
71
|
+
# #=> {
|
72
|
+
# # index: 1,
|
73
|
+
# # chain_id: "xyz789",
|
143
74
|
# # type: "Task",
|
144
|
-
# #
|
145
|
-
# # id: "
|
146
|
-
# #
|
147
|
-
# #
|
148
|
-
# #
|
149
|
-
# #
|
150
|
-
# #
|
75
|
+
# # class: "FailingTask",
|
76
|
+
# # id: "ghi012",
|
77
|
+
# # tags: [],
|
78
|
+
# # state: :interrupted,
|
79
|
+
# # status: :failed,
|
80
|
+
# # outcome: :bad,
|
81
|
+
# # metadata: { reason: "Database connection failed" },
|
82
|
+
# # runtime: 0.012,
|
83
|
+
# # caused_failure: { message: "Task failed", ... },
|
84
|
+
# # threw_failure: { message: "Validation error", ... },
|
151
85
|
# # }
|
152
86
|
def call(result)
|
153
87
|
TaskSerializer.call(result.task).tap do |hash|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Result matchers
|
4
|
+
require_relative "result_matchers/be_successful_task"
|
5
|
+
require_relative "result_matchers/be_failed_task"
|
6
|
+
require_relative "result_matchers/be_skipped_task"
|
7
|
+
require_relative "result_matchers/be_executed"
|
8
|
+
require_relative "result_matchers/be_state_matchers"
|
9
|
+
require_relative "result_matchers/be_status_matchers"
|
10
|
+
require_relative "result_matchers/have_good_outcome"
|
11
|
+
require_relative "result_matchers/have_bad_outcome"
|
12
|
+
require_relative "result_matchers/have_runtime"
|
13
|
+
require_relative "result_matchers/have_metadata"
|
14
|
+
require_relative "result_matchers/have_empty_metadata"
|
15
|
+
require_relative "result_matchers/have_context"
|
16
|
+
require_relative "result_matchers/have_preserved_context"
|
17
|
+
require_relative "result_matchers/have_caused_failure"
|
18
|
+
require_relative "result_matchers/have_thrown_failure"
|
19
|
+
require_relative "result_matchers/have_received_thrown_failure"
|
20
|
+
require_relative "result_matchers/have_chain_index"
|
21
|
+
|
22
|
+
# Task matchers
|
23
|
+
require_relative "task_matchers/be_well_formed_task"
|
24
|
+
require_relative "task_matchers/have_cmd_setting"
|
25
|
+
require_relative "task_matchers/have_middleware"
|
26
|
+
require_relative "task_matchers/have_callback"
|
27
|
+
require_relative "task_matchers/have_parameter"
|
28
|
+
require_relative "task_matchers/have_executed_callbacks"
|