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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/docs.md +9 -0
  3. data/.cursor/prompts/rspec.md +21 -0
  4. data/.cursor/prompts/yardoc.md +13 -0
  5. data/.rubocop.yml +2 -0
  6. data/CHANGELOG.md +29 -3
  7. data/README.md +2 -1
  8. data/docs/ai_prompts.md +269 -195
  9. data/docs/basics/call.md +126 -60
  10. data/docs/basics/chain.md +190 -160
  11. data/docs/basics/context.md +242 -154
  12. data/docs/basics/setup.md +302 -32
  13. data/docs/callbacks.md +382 -119
  14. data/docs/configuration.md +211 -49
  15. data/docs/deprecation.md +245 -0
  16. data/docs/getting_started.md +161 -39
  17. data/docs/internationalization.md +590 -70
  18. data/docs/interruptions/exceptions.md +135 -118
  19. data/docs/interruptions/faults.md +152 -127
  20. data/docs/interruptions/halt.md +134 -80
  21. data/docs/logging.md +183 -120
  22. data/docs/middlewares.md +165 -392
  23. data/docs/outcomes/result.md +140 -112
  24. data/docs/outcomes/states.md +134 -99
  25. data/docs/outcomes/statuses.md +204 -146
  26. data/docs/parameters/coercions.md +251 -289
  27. data/docs/parameters/defaults.md +224 -169
  28. data/docs/parameters/definitions.md +289 -141
  29. data/docs/parameters/namespacing.md +250 -161
  30. data/docs/parameters/validations.md +247 -159
  31. data/docs/testing.md +196 -203
  32. data/docs/workflows.md +146 -101
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +39 -55
  35. data/lib/cmdx/callback_registry.rb +80 -73
  36. data/lib/cmdx/chain.rb +65 -122
  37. data/lib/cmdx/chain_inspector.rb +23 -116
  38. data/lib/cmdx/chain_serializer.rb +34 -146
  39. data/lib/cmdx/coercion.rb +57 -0
  40. data/lib/cmdx/coercion_registry.rb +113 -0
  41. data/lib/cmdx/coercions/array.rb +18 -36
  42. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  43. data/lib/cmdx/coercions/boolean.rb +21 -40
  44. data/lib/cmdx/coercions/complex.rb +18 -31
  45. data/lib/cmdx/coercions/date.rb +20 -39
  46. data/lib/cmdx/coercions/date_time.rb +22 -39
  47. data/lib/cmdx/coercions/float.rb +19 -32
  48. data/lib/cmdx/coercions/hash.rb +22 -41
  49. data/lib/cmdx/coercions/integer.rb +20 -33
  50. data/lib/cmdx/coercions/rational.rb +20 -32
  51. data/lib/cmdx/coercions/string.rb +23 -31
  52. data/lib/cmdx/coercions/time.rb +24 -40
  53. data/lib/cmdx/coercions/virtual.rb +14 -31
  54. data/lib/cmdx/configuration.rb +101 -162
  55. data/lib/cmdx/context.rb +34 -166
  56. data/lib/cmdx/core_ext/hash.rb +42 -67
  57. data/lib/cmdx/core_ext/module.rb +35 -79
  58. data/lib/cmdx/core_ext/object.rb +63 -98
  59. data/lib/cmdx/correlator.rb +59 -154
  60. data/lib/cmdx/error.rb +37 -202
  61. data/lib/cmdx/errors.rb +153 -216
  62. data/lib/cmdx/fault.rb +68 -150
  63. data/lib/cmdx/faults.rb +26 -137
  64. data/lib/cmdx/immutator.rb +22 -110
  65. data/lib/cmdx/lazy_struct.rb +110 -186
  66. data/lib/cmdx/log_formatters/json.rb +14 -40
  67. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  68. data/lib/cmdx/log_formatters/line.rb +14 -48
  69. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  70. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  71. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  72. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  73. data/lib/cmdx/log_formatters/raw.rb +19 -49
  74. data/lib/cmdx/logger.rb +22 -79
  75. data/lib/cmdx/logger_ansi.rb +31 -72
  76. data/lib/cmdx/logger_serializer.rb +74 -103
  77. data/lib/cmdx/middleware.rb +56 -60
  78. data/lib/cmdx/middleware_registry.rb +82 -77
  79. data/lib/cmdx/middlewares/correlate.rb +41 -226
  80. data/lib/cmdx/middlewares/timeout.rb +46 -185
  81. data/lib/cmdx/parameter.rb +167 -183
  82. data/lib/cmdx/parameter_evaluator.rb +231 -0
  83. data/lib/cmdx/parameter_inspector.rb +37 -55
  84. data/lib/cmdx/parameter_registry.rb +65 -84
  85. data/lib/cmdx/parameter_serializer.rb +32 -76
  86. data/lib/cmdx/railtie.rb +24 -107
  87. data/lib/cmdx/result.rb +254 -259
  88. data/lib/cmdx/result_ansi.rb +28 -80
  89. data/lib/cmdx/result_inspector.rb +34 -70
  90. data/lib/cmdx/result_logger.rb +23 -77
  91. data/lib/cmdx/result_serializer.rb +59 -125
  92. data/lib/cmdx/rspec/matchers.rb +28 -0
  93. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  94. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  96. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  97. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  98. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  99. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  100. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  101. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  102. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  103. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  104. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  105. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  106. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  107. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  108. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  109. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  110. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  111. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  112. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  113. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  114. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  115. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  116. data/lib/cmdx/task.rb +336 -427
  117. data/lib/cmdx/task_deprecator.rb +52 -0
  118. data/lib/cmdx/task_processor.rb +246 -0
  119. data/lib/cmdx/task_serializer.rb +34 -69
  120. data/lib/cmdx/utils/ansi_color.rb +13 -89
  121. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  122. data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
  123. data/lib/cmdx/utils/name_affix.rb +21 -71
  124. data/lib/cmdx/validator.rb +57 -0
  125. data/lib/cmdx/validator_registry.rb +108 -0
  126. data/lib/cmdx/validators/exclusion.rb +55 -94
  127. data/lib/cmdx/validators/format.rb +31 -85
  128. data/lib/cmdx/validators/inclusion.rb +65 -110
  129. data/lib/cmdx/validators/length.rb +117 -133
  130. data/lib/cmdx/validators/numeric.rb +123 -130
  131. data/lib/cmdx/validators/presence.rb +38 -79
  132. data/lib/cmdx/version.rb +1 -7
  133. data/lib/cmdx/workflow.rb +58 -330
  134. data/lib/cmdx.rb +1 -1
  135. data/lib/generators/cmdx/install_generator.rb +14 -31
  136. data/lib/generators/cmdx/task_generator.rb +39 -55
  137. data/lib/generators/cmdx/templates/install.rb +24 -6
  138. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  139. data/lib/locales/ar.yml +0 -1
  140. data/lib/locales/cs.yml +0 -1
  141. data/lib/locales/da.yml +0 -1
  142. data/lib/locales/de.yml +0 -1
  143. data/lib/locales/el.yml +0 -1
  144. data/lib/locales/en.yml +0 -1
  145. data/lib/locales/es.yml +0 -1
  146. data/lib/locales/fi.yml +0 -1
  147. data/lib/locales/fr.yml +0 -1
  148. data/lib/locales/he.yml +0 -1
  149. data/lib/locales/hi.yml +0 -1
  150. data/lib/locales/it.yml +0 -1
  151. data/lib/locales/ja.yml +0 -1
  152. data/lib/locales/ko.yml +0 -1
  153. data/lib/locales/nl.yml +0 -1
  154. data/lib/locales/no.yml +0 -1
  155. data/lib/locales/pl.yml +0 -1
  156. data/lib/locales/pt.yml +0 -1
  157. data/lib/locales/ru.yml +0 -1
  158. data/lib/locales/sv.yml +0 -1
  159. data/lib/locales/th.yml +0 -1
  160. data/lib/locales/tr.yml +0 -1
  161. data/lib/locales/vi.yml +0 -1
  162. data/lib/locales/zh.yml +0 -1
  163. metadata +36 -8
  164. data/lib/cmdx/parameter_validator.rb +0 -81
  165. data/lib/cmdx/parameter_value.rb +0 -244
  166. data/lib/cmdx/parameters_inspector.rb +0 -72
  167. data/lib/cmdx/parameters_serializer.rb +0 -115
  168. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  169. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  170. data/lib/cmdx/validators/custom.rb +0 -102
@@ -1,47 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # ANSI color formatting module for result states and statuses.
4
+ # ANSI color formatting utilities for result states and statuses.
5
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
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
- # 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.
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] The state or status string to colorize
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
- # @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)
34
+ # @return [String] the input string with ANSI color codes applied
67
35
  #
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)
36
+ # @example Colorize a success status
37
+ # ResultAnsi.call("success") #=> "\e[0;32;49msuccess\e[0m" (green)
72
38
  #
73
- # @example Unknown value
74
- # ResultAnsi.call("unknown") # => "\e[39munknown\e[0m" (default color)
39
+ # @example Colorize a failed status
40
+ # ResultAnsi.call("failed") #=> "\e[0;31;49mfailed\e[0m" (red)
75
41
  #
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)
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 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.
48
+ # Determines the appropriate color for a result state or status.
92
49
  #
93
- # @param s [String] The state or status string to determine color for
94
- # @return [Symbol] The color symbol for the state or status
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
- # @example Result state color mapping
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
- # @example Result status color mapping
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 Unknown state or status
108
- # color("unknown") # => :default
109
- # color("pending") # => :default
58
+ # @example Get color for success status
59
+ # ResultAnsi.color("success") #=> :green
110
60
  #
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
61
+ # @example Get color for unknown value
62
+ # ResultAnsi.color("unknown") #=> :default
114
63
  #
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
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 utility for generating human-readable result descriptions.
4
+ # Result inspection and formatting utilities for readable result representation.
5
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
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
- # 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.
19
+ # Formats a result hash into a human-readable string representation.
63
20
  #
64
- # @param result [Hash] The result hash to inspect
65
- # @return [String] Formatted result description
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
- # @example Simple result inspection
68
- # ResultInspector.call(result_hash)
69
- # # => "ProcessOrderTask: type=Task index=0 id=018c2b95... state=complete status=success"
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
- # @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}"
42
+ # @return [String] a formatted string representation of the result with key information
75
43
  #
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>"
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 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"
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)
@@ -1,47 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Result-specific logging module for task execution outcomes.
4
+ # Logger utilities for task execution results.
5
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
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 at the appropriate severity level.
21
+ # Logs a task execution result with the appropriate severity level.
54
22
  #
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.
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 Logging a successful result
63
- # task = ProcessOrderTask.call(order_id: 123)
33
+ # @example Log a successful task result
34
+ # task = ProcessDataTask.call(data: "input")
64
35
  # ResultLogger.call(task.result)
65
- # # Logs at INFO level with result details
36
+ # # Logs at :info level: "Result: ProcessDataTask completed successfully"
66
37
  #
67
- # @example Logging a failed result
68
- # task = ProcessOrderTask.new
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 ERROR level with failure details
41
+ # # Logs at :error level: "Result: ValidateDataTask failed with error"
72
42
  #
73
- # @example Logging a skipped result
74
- # task = ProcessOrderTask.new
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 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.
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 utility for converting Result objects to hash representations.
4
+ # Result serialization module for converting result objects to hash format.
5
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
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 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.
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
- # Converts a Result object to a hash representation.
25
+ # Serializes a result object into a hash representation.
92
26
  #
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.
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] The result object to serialize
98
- # @return [Hash] Structured hash representation of the result
32
+ # @param result [CMDx::Result] the result object to serialize
99
33
  #
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
- # # }
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
- # @example Failed result with metadata
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
- # # 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
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 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",
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
- # # 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
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"