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/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 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 color codes
|
7
|
+
# to result states and statuses for enhanced console output readability.
|
8
|
+
# It maps execution states and completion statuses to corresponding colors
|
9
|
+
# and provides methods to format strings with these colors.
|
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
|
@@ -50,71 +23,32 @@ module CMDx
|
|
50
23
|
|
51
24
|
module_function
|
52
25
|
|
53
|
-
# Applies ANSI color formatting to a
|
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
|
26
|
+
# Applies ANSI color formatting to a string based on its state or status.
|
61
27
|
#
|
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)
|
28
|
+
# @param s [String] the string to format with ANSI color codes
|
67
29
|
#
|
68
|
-
# @
|
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)
|
30
|
+
# @return [String] the formatted string with appropriate ANSI color codes
|
72
31
|
#
|
73
|
-
# @example
|
74
|
-
# ResultAnsi.call(
|
32
|
+
# @example Format a result state
|
33
|
+
# ResultAnsi.call(Result::EXECUTING) #=> "\e[0;33;49mexecuting\e[0m"
|
75
34
|
#
|
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)
|
35
|
+
# @example Format a result status
|
36
|
+
# ResultAnsi.call(Result::SUCCESS) #=> "\e[0;32;49msuccess\e[0m"
|
82
37
|
def call(s)
|
83
38
|
Utils::AnsiColor.call(s, color: color(s))
|
84
39
|
end
|
85
40
|
|
86
|
-
# Determines the appropriate color for a
|
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
|
41
|
+
# Determines the appropriate ANSI color for a given state or status.
|
101
42
|
#
|
102
|
-
# @
|
103
|
-
# color("success") # => :green
|
104
|
-
# color("skipped") # => :yellow
|
105
|
-
# color("failed") # => :red
|
43
|
+
# @param s [String] the state or status string to determine color for
|
106
44
|
#
|
107
|
-
# @
|
108
|
-
# color("unknown") # => :default
|
109
|
-
# color("pending") # => :default
|
45
|
+
# @return [Symbol] the color symbol corresponding to the state/status, or :default if not found
|
110
46
|
#
|
111
|
-
# @example
|
112
|
-
#
|
113
|
-
# color("some_value") # => STATE_COLORS["some_value"] || STATUS_COLORS["some_value"] || :default
|
47
|
+
# @example Get color for a state
|
48
|
+
# ResultAnsi.color(Result::COMPLETE) #=> :green
|
114
49
|
#
|
115
|
-
# @
|
116
|
-
#
|
117
|
-
# @see STATUS_COLORS The mapping hash for result statuses
|
50
|
+
# @example Get color for unknown value
|
51
|
+
# ResultAnsi.color("unknown") #=> :default
|
118
52
|
def color(s)
|
119
53
|
STATE_COLORS[s] || STATUS_COLORS[s] || :default
|
120
54
|
end
|
@@ -1,53 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
4
|
+
# Provides formatted inspection functionality for task execution results.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
6
|
+
# This module formats result hash data into a human-readable string representation
|
7
|
+
# with ordered key-value pairs. It handles special formatting for specific keys
|
8
|
+
# like failure references and applies consistent ordering to result attributes.
|
45
9
|
module ResultInspector
|
46
10
|
|
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
11
|
ORDERED_KEYS = %i[
|
52
12
|
class type index id state status outcome metadata
|
53
13
|
tags pid runtime caused_failure threw_failure
|
@@ -55,37 +15,36 @@ module CMDx
|
|
55
15
|
|
56
16
|
module_function
|
57
17
|
|
58
|
-
#
|
18
|
+
# Formats a result hash into a human-readable inspection string.
|
19
|
+
#
|
20
|
+
# Creates a formatted string representation of a result hash with ordered
|
21
|
+
# key-value pairs. Special keys like :class, :caused_failure, and :threw_failure
|
22
|
+
# receive custom formatting for better readability and debugging.
|
59
23
|
#
|
60
|
-
#
|
61
|
-
# special handling for different attribute types. The class name appears
|
62
|
-
# first followed by a colon, and failure references are specially formatted.
|
24
|
+
# @param result [Hash] the result hash to format and inspect
|
63
25
|
#
|
64
|
-
# @
|
65
|
-
# @return [String] Formatted result description
|
26
|
+
# @return [String] formatted string with space-separated key-value pairs
|
66
27
|
#
|
67
|
-
# @
|
68
|
-
# ResultInspector.call(result_hash)
|
69
|
-
# # => "ProcessOrderTask: type=Task index=0 id=018c2b95... state=complete status=success"
|
28
|
+
# @raise [NoMethodError] if result doesn't respond to key? or []
|
70
29
|
#
|
71
|
-
# @example
|
72
|
-
#
|
73
|
-
# ResultInspector.call(
|
74
|
-
# # => "MyTask:
|
30
|
+
# @example Formatting a basic result
|
31
|
+
# result = { class: "MyTask", state: "complete", status: "success" }
|
32
|
+
# ResultInspector.call(result)
|
33
|
+
# # => "MyTask: state=complete status=success"
|
75
34
|
#
|
76
|
-
# @example
|
77
|
-
#
|
78
|
-
# class: "
|
79
|
-
#
|
80
|
-
#
|
35
|
+
# @example Formatting result with failure information
|
36
|
+
# result = {
|
37
|
+
# class: "ProcessTask",
|
38
|
+
# state: "interrupted",
|
39
|
+
# caused_failure: { index: 2, class: "ValidationError", id: "val_123" }
|
81
40
|
# }
|
82
|
-
# ResultInspector.call(
|
83
|
-
# # => "
|
41
|
+
# ResultInspector.call(result)
|
42
|
+
# # => "ProcessTask: state=interrupted caused_failure=<[2] ValidationError: val_123>"
|
84
43
|
#
|
85
|
-
# @example
|
86
|
-
#
|
87
|
-
# ResultInspector.call(
|
88
|
-
# # => "
|
44
|
+
# @example Formatting empty or minimal result
|
45
|
+
# result = { id: "task_456" }
|
46
|
+
# ResultInspector.call(result)
|
47
|
+
# # => "id=task_456"
|
89
48
|
def call(result)
|
90
49
|
ORDERED_KEYS.filter_map do |key|
|
91
50
|
next unless result.key?(key)
|
data/lib/cmdx/result_logger.rb
CHANGED
@@ -1,47 +1,13 @@
|
|
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
|
-
# log
|
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
|
6
|
+
# This module provides functionality to log task execution results with appropriate
|
7
|
+
# severity levels based on the result status. It automatically maps result statuses
|
8
|
+
# to corresponding log levels and delegates to the task's configured logger.
|
39
9
|
module ResultLogger
|
40
10
|
|
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
11
|
STATUS_TO_SEVERITY = {
|
46
12
|
Result::SUCCESS => :info, # Successful task completion
|
47
13
|
Result::SKIPPED => :warn, # Task was skipped
|
@@ -50,54 +16,25 @@ module CMDx
|
|
50
16
|
|
51
17
|
module_function
|
52
18
|
|
53
|
-
# Logs
|
19
|
+
# Logs the task execution result with appropriate severity level.
|
54
20
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
21
|
+
# This method retrieves the logger from the task and logs the result using
|
22
|
+
# the severity level mapped from the result's status. If no logger is configured
|
23
|
+
# for the task, the method returns early without logging.
|
58
24
|
#
|
59
|
-
# @param result [
|
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
|
25
|
+
# @param result [Result] the task execution result to log
|
78
26
|
#
|
79
|
-
# @
|
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
|
27
|
+
# @return [void]
|
94
28
|
#
|
95
|
-
#
|
96
|
-
#
|
29
|
+
# @example Log a successful task result
|
30
|
+
# result = task.process
|
31
|
+
# CMDx::ResultLogger.call(result)
|
32
|
+
# # => logs at info level: "Task completed successfully"
|
97
33
|
#
|
98
|
-
# @
|
99
|
-
#
|
100
|
-
#
|
34
|
+
# @example Log a failed task result
|
35
|
+
# result = failing_task.process
|
36
|
+
# CMDx::ResultLogger.call(result)
|
37
|
+
# # => logs at error level: "Task failed with error"
|
101
38
|
def call(result)
|
102
39
|
logger = result.task.send(:logger)
|
103
40
|
return if logger.nil?
|
@@ -1,84 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module CMDx
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
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
|
+
# Provides serialization functionality for CMDx::Result objects,
|
5
|
+
# converting them into structured hash representations suitable for
|
6
|
+
# logging, storage, or transmission.
|
75
7
|
module ResultSerializer
|
76
8
|
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# This
|
80
|
-
# and threw_failure) while preventing infinite recursion by stripping
|
81
|
-
# the same fields from nested failure objects.
|
9
|
+
# A proc that removes failure-related metadata from the hash representation
|
10
|
+
# when the result hasn't actually failed in the specified way.
|
11
|
+
# This prevents duplicate failure information from appearing in logs.
|
82
12
|
STRIP_FAILURE = proc do |h, r, k|
|
83
13
|
unless r.send(:"#{k}?")
|
84
14
|
# Strip caused/threw failures since its the same info as the log line
|
@@ -88,67 +18,33 @@ module CMDx
|
|
88
18
|
|
89
19
|
module_function
|
90
20
|
|
91
|
-
# Converts a Result object
|
92
|
-
#
|
93
|
-
#
|
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
|
21
|
+
# Converts a Result object into a structured hash representation.
|
22
|
+
# Combines task serialization data with result-specific information
|
23
|
+
# including execution state, status, outcome, metadata, and runtime.
|
99
24
|
#
|
100
|
-
# @
|
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
|
-
# # }
|
25
|
+
# @param result [CMDx::Result] the result object to serialize
|
116
26
|
#
|
117
|
-
# @
|
118
|
-
#
|
119
|
-
#
|
120
|
-
# result = task.result
|
27
|
+
# @return [Hash] a structured hash containing task information and result details
|
28
|
+
# including :state, :status, :outcome, :metadata, :runtime, and optionally
|
29
|
+
# :caused_failure and :threw_failure if the result failed
|
121
30
|
#
|
122
|
-
#
|
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
|
-
# # }
|
31
|
+
# @raise [NoMethodError] if result doesn't respond to expected methods
|
32
|
+
# @raise [TypeError] if result.task is invalid for TaskSerializer
|
134
33
|
#
|
135
|
-
# @example
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
34
|
+
# @example Serializing a successful result
|
35
|
+
# result = task.call
|
36
|
+
# CMDx::ResultSerializer.call(result)
|
37
|
+
# #=> { index: 0, chain_id: "abc123", type: "Task", class: "MyTask",
|
38
|
+
# # id: "def456", tags: [], state: "complete", status: "success",
|
39
|
+
# # outcome: "good", metadata: {}, runtime: 0.05 }
|
139
40
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
144
|
-
# #
|
145
|
-
# #
|
146
|
-
# #
|
147
|
-
# # status: "skipped",
|
148
|
-
# # outcome: "skipped",
|
149
|
-
# # metadata: { reason: "Order already processed" },
|
150
|
-
# # runtime: 0.05
|
151
|
-
# # }
|
41
|
+
# @example Serializing a failed result
|
42
|
+
# result = task.call
|
43
|
+
# CMDx::ResultSerializer.call(result)
|
44
|
+
# #=> { index: 0, chain_id: "abc123", type: "Task", class: "MyTask",
|
45
|
+
# # id: "def456", tags: [], state: "interrupted", status: "failed",
|
46
|
+
# # outcome: "bad", metadata: { error: "Something went wrong" },
|
47
|
+
# # runtime: 0.02, caused_failure: {...}, threw_failure: {...} }
|
152
48
|
def call(result)
|
153
49
|
TaskSerializer.call(result.task).tap do |hash|
|
154
50
|
hash.merge!(
|
@@ -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"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher for asserting that a task result has been executed.
|
4
|
+
#
|
5
|
+
# This matcher checks if a CMDx::Result object is in an executed state,
|
6
|
+
# which occurs when the task has finished execution regardless of whether
|
7
|
+
# it succeeded, failed, or was skipped. A result is considered executed
|
8
|
+
# when it's in either "complete" or "interrupted" state.
|
9
|
+
#
|
10
|
+
# @return [Boolean] true if the result is executed (complete or interrupted)
|
11
|
+
#
|
12
|
+
# @example Basic usage with successful task
|
13
|
+
# result = MyTask.call(user_id: 123)
|
14
|
+
# expect(result).to be_executed
|
15
|
+
#
|
16
|
+
# @example Usage with failed task
|
17
|
+
# result = FailingTask.call
|
18
|
+
# expect(result).to be_executed
|
19
|
+
#
|
20
|
+
# @example Negative assertion
|
21
|
+
# task = MyTask.new
|
22
|
+
# expect(task.result).not_to be_executed
|
23
|
+
#
|
24
|
+
# @example In workflow integration tests
|
25
|
+
# result = MyWorkflow.call(data: "test")
|
26
|
+
# expect(result).to be_executed
|
27
|
+
# expect(result.context.processed).to be(true)
|
28
|
+
RSpec::Matchers.define :be_executed do
|
29
|
+
match(&:executed?)
|
30
|
+
|
31
|
+
failure_message do |result|
|
32
|
+
"expected result to be executed, but was in #{result.state} state"
|
33
|
+
end
|
34
|
+
|
35
|
+
failure_message_when_negated do |result|
|
36
|
+
"expected result not to be executed, but it was (state: #{result.state})"
|
37
|
+
end
|
38
|
+
|
39
|
+
description do
|
40
|
+
"be executed"
|
41
|
+
end
|
42
|
+
end
|