cmdx 1.1.2 → 1.5.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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +55 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/CHANGELOG.md +11 -132
  11. data/LLM.md +3317 -0
  12. data/README.md +68 -44
  13. data/docs/attributes/coercions.md +162 -0
  14. data/docs/attributes/defaults.md +90 -0
  15. data/docs/attributes/definitions.md +281 -0
  16. data/docs/attributes/naming.md +78 -0
  17. data/docs/attributes/validations.md +309 -0
  18. data/docs/basics/chain.md +56 -249
  19. data/docs/basics/context.md +56 -289
  20. data/docs/basics/execution.md +114 -0
  21. data/docs/basics/setup.md +37 -334
  22. data/docs/callbacks.md +89 -467
  23. data/docs/deprecation.md +91 -174
  24. data/docs/getting_started.md +212 -202
  25. data/docs/internationalization.md +11 -647
  26. data/docs/interruptions/exceptions.md +23 -198
  27. data/docs/interruptions/faults.md +71 -151
  28. data/docs/interruptions/halt.md +109 -186
  29. data/docs/logging.md +44 -256
  30. data/docs/middlewares.md +113 -426
  31. data/docs/outcomes/result.md +81 -228
  32. data/docs/outcomes/states.md +33 -221
  33. data/docs/outcomes/statuses.md +21 -311
  34. data/docs/tips_and_tricks.md +120 -70
  35. data/docs/workflows.md +99 -283
  36. data/lib/cmdx/.DS_Store +0 -0
  37. data/lib/cmdx/attribute.rb +229 -0
  38. data/lib/cmdx/attribute_registry.rb +94 -0
  39. data/lib/cmdx/attribute_value.rb +193 -0
  40. data/lib/cmdx/callback_registry.rb +69 -77
  41. data/lib/cmdx/chain.rb +56 -73
  42. data/lib/cmdx/coercion_registry.rb +52 -68
  43. data/lib/cmdx/coercions/array.rb +19 -18
  44. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  45. data/lib/cmdx/coercions/boolean.rb +26 -25
  46. data/lib/cmdx/coercions/complex.rb +21 -22
  47. data/lib/cmdx/coercions/date.rb +25 -23
  48. data/lib/cmdx/coercions/date_time.rb +24 -25
  49. data/lib/cmdx/coercions/float.rb +25 -22
  50. data/lib/cmdx/coercions/hash.rb +31 -32
  51. data/lib/cmdx/coercions/integer.rb +30 -24
  52. data/lib/cmdx/coercions/rational.rb +29 -24
  53. data/lib/cmdx/coercions/string.rb +19 -22
  54. data/lib/cmdx/coercions/symbol.rb +37 -0
  55. data/lib/cmdx/coercions/time.rb +26 -25
  56. data/lib/cmdx/configuration.rb +49 -108
  57. data/lib/cmdx/context.rb +222 -44
  58. data/lib/cmdx/deprecator.rb +61 -0
  59. data/lib/cmdx/errors.rb +42 -252
  60. data/lib/cmdx/exceptions.rb +39 -0
  61. data/lib/cmdx/faults.rb +78 -39
  62. data/lib/cmdx/freezer.rb +51 -0
  63. data/lib/cmdx/identifier.rb +30 -0
  64. data/lib/cmdx/locale.rb +52 -0
  65. data/lib/cmdx/log_formatters/json.rb +21 -22
  66. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  67. data/lib/cmdx/log_formatters/line.rb +15 -22
  68. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  69. data/lib/cmdx/log_formatters/raw.rb +16 -22
  70. data/lib/cmdx/middleware_registry.rb +70 -74
  71. data/lib/cmdx/middlewares/correlate.rb +90 -54
  72. data/lib/cmdx/middlewares/runtime.rb +58 -0
  73. data/lib/cmdx/middlewares/timeout.rb +48 -68
  74. data/lib/cmdx/railtie.rb +12 -45
  75. data/lib/cmdx/result.rb +229 -314
  76. data/lib/cmdx/task.rb +194 -366
  77. data/lib/cmdx/utils/call.rb +49 -0
  78. data/lib/cmdx/utils/condition.rb +71 -0
  79. data/lib/cmdx/utils/format.rb +61 -0
  80. data/lib/cmdx/validator_registry.rb +63 -72
  81. data/lib/cmdx/validators/exclusion.rb +38 -67
  82. data/lib/cmdx/validators/format.rb +48 -49
  83. data/lib/cmdx/validators/inclusion.rb +43 -74
  84. data/lib/cmdx/validators/length.rb +101 -162
  85. data/lib/cmdx/validators/numeric.rb +95 -170
  86. data/lib/cmdx/validators/presence.rb +37 -50
  87. data/lib/cmdx/version.rb +1 -1
  88. data/lib/cmdx/worker.rb +178 -0
  89. data/lib/cmdx/workflow.rb +85 -81
  90. data/lib/cmdx.rb +19 -13
  91. data/lib/generators/cmdx/install_generator.rb +14 -13
  92. data/lib/generators/cmdx/task_generator.rb +25 -50
  93. data/lib/generators/cmdx/templates/install.rb +11 -46
  94. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  95. data/lib/locales/en.yml +18 -4
  96. data/src/cmdx-logo.png +0 -0
  97. metadata +32 -116
  98. data/docs/ai_prompts.md +0 -393
  99. data/docs/basics/call.md +0 -317
  100. data/docs/configuration.md +0 -344
  101. data/docs/parameters/coercions.md +0 -396
  102. data/docs/parameters/defaults.md +0 -335
  103. data/docs/parameters/definitions.md +0 -446
  104. data/docs/parameters/namespacing.md +0 -378
  105. data/docs/parameters/validations.md +0 -405
  106. data/docs/testing.md +0 -553
  107. data/lib/cmdx/callback.rb +0 -53
  108. data/lib/cmdx/chain_inspector.rb +0 -56
  109. data/lib/cmdx/chain_serializer.rb +0 -63
  110. data/lib/cmdx/coercion.rb +0 -57
  111. data/lib/cmdx/coercions/virtual.rb +0 -29
  112. data/lib/cmdx/core_ext/hash.rb +0 -83
  113. data/lib/cmdx/core_ext/module.rb +0 -98
  114. data/lib/cmdx/core_ext/object.rb +0 -125
  115. data/lib/cmdx/correlator.rb +0 -122
  116. data/lib/cmdx/error.rb +0 -67
  117. data/lib/cmdx/fault.rb +0 -140
  118. data/lib/cmdx/immutator.rb +0 -52
  119. data/lib/cmdx/lazy_struct.rb +0 -246
  120. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  121. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  122. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  123. data/lib/cmdx/logger.rb +0 -49
  124. data/lib/cmdx/logger_ansi.rb +0 -68
  125. data/lib/cmdx/logger_serializer.rb +0 -116
  126. data/lib/cmdx/middleware.rb +0 -70
  127. data/lib/cmdx/parameter.rb +0 -312
  128. data/lib/cmdx/parameter_evaluator.rb +0 -231
  129. data/lib/cmdx/parameter_inspector.rb +0 -66
  130. data/lib/cmdx/parameter_registry.rb +0 -106
  131. data/lib/cmdx/parameter_serializer.rb +0 -59
  132. data/lib/cmdx/result_ansi.rb +0 -71
  133. data/lib/cmdx/result_inspector.rb +0 -71
  134. data/lib/cmdx/result_logger.rb +0 -59
  135. data/lib/cmdx/result_serializer.rb +0 -104
  136. data/lib/cmdx/rspec/matchers.rb +0 -28
  137. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  138. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  139. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  141. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  142. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  143. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  144. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  145. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  146. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  147. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  148. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  149. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  150. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  151. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  152. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  153. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  154. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  155. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  156. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  157. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  158. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  159. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  160. data/lib/cmdx/task_deprecator.rb +0 -58
  161. data/lib/cmdx/task_processor.rb +0 -246
  162. data/lib/cmdx/task_serializer.rb +0 -57
  163. data/lib/cmdx/utils/ansi_color.rb +0 -73
  164. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  165. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  166. data/lib/cmdx/utils/name_affix.rb +0 -52
  167. data/lib/cmdx/validator.rb +0 -57
  168. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  169. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  170. data/lib/locales/ar.yml +0 -35
  171. data/lib/locales/cs.yml +0 -35
  172. data/lib/locales/da.yml +0 -35
  173. data/lib/locales/de.yml +0 -35
  174. data/lib/locales/el.yml +0 -35
  175. data/lib/locales/es.yml +0 -35
  176. data/lib/locales/fi.yml +0 -35
  177. data/lib/locales/fr.yml +0 -35
  178. data/lib/locales/he.yml +0 -35
  179. data/lib/locales/hi.yml +0 -35
  180. data/lib/locales/it.yml +0 -35
  181. data/lib/locales/ja.yml +0 -35
  182. data/lib/locales/ko.yml +0 -35
  183. data/lib/locales/nl.yml +0 -35
  184. data/lib/locales/no.yml +0 -35
  185. data/lib/locales/pl.yml +0 -35
  186. data/lib/locales/pt.yml +0 -35
  187. data/lib/locales/ru.yml +0 -35
  188. data/lib/locales/sv.yml +0 -35
  189. data/lib/locales/th.yml +0 -35
  190. data/lib/locales/tr.yml +0 -35
  191. data/lib/locales/vi.yml +0 -35
  192. data/lib/locales/zh.yml +0 -35
@@ -2,78 +2,114 @@
2
2
 
3
3
  module CMDx
4
4
  module Middlewares
5
- # Middleware that manages correlation IDs for task execution tracing.
6
- # Automatically generates or uses provided correlation IDs to track task execution
7
- # across complex workflows, enabling better debugging and monitoring.
8
- class Correlate < CMDx::Middleware
5
+ # Middleware for correlating task executions with unique identifiers.
6
+ #
7
+ # The Correlate middleware provides thread-safe correlation ID management
8
+ # for tracking task execution flows across different operations.
9
+ # It automatically generates correlation IDs when none are provided and
10
+ # stores them in task result metadata for traceability.
11
+ module Correlate
9
12
 
10
- # @return [String, Symbol, Proc, nil] The explicit correlation ID to use, or callable that generates one
11
- attr_reader :id
13
+ extend self
12
14
 
13
- # @return [Hash] The conditional options for correlation application
14
- attr_reader :conditional
15
+ THREAD_KEY = :cmdx_correlate
15
16
 
16
- # Initializes the correlation middleware with optional configuration.
17
+ # Retrieves the current correlation ID from thread-local storage.
17
18
  #
18
- # @param options [Hash] configuration options for the middleware
19
- # @option options [String, Symbol, Proc] :id explicit correlation ID or callable to generate one
20
- # @option options [Symbol, Proc] :if condition that must be truthy to apply correlation
21
- # @option options [Symbol, Proc] :unless condition that must be falsy to apply correlation
19
+ # @return [String, nil] The current correlation ID or nil if not set
22
20
  #
23
- # @return [Correlate] new instance of the middleware
21
+ # @example Get current correlation ID
22
+ # Correlate.id # => "550e8400-e29b-41d4-a716-446655440000"
23
+ def id
24
+ Thread.current[THREAD_KEY]
25
+ end
26
+
27
+ # Sets the correlation ID in thread-local storage.
24
28
  #
25
- # @example Register with a middleware instance
26
- # use :middleware, CMDx::Middlewares::Correlate.new(id: "request-123")
29
+ # @param id [String] The correlation ID to set
30
+ # @return [String] The set correlation ID
27
31
  #
28
- # @example Register with explicit ID
29
- # use :middleware, CMDx::Middlewares::Correlate, id: "request-123"
32
+ # @example Set correlation ID
33
+ # Correlate.id = "abc-123-def"
34
+ def id=(id)
35
+ Thread.current[THREAD_KEY] = id
36
+ end
37
+
38
+ # Clears the current correlation ID from thread-local storage.
30
39
  #
31
- # @example Register with dynamic ID generation
32
- # use :middleware, CMDx::Middlewares::Correlate, id: -> { SecureRandom.uuid }
40
+ # @return [nil] Always returns nil
33
41
  #
34
- # @example Register with conditions
35
- # use :middleware, CMDx::Middlewares::Correlate, if: :production?, unless: :testing?
36
- def initialize(options = {})
37
- @id = options[:id]
38
- @conditional = options.slice(:if, :unless)
42
+ # @example Clear correlation ID
43
+ # Correlate.clear
44
+ def clear
45
+ Thread.current[THREAD_KEY] = nil
39
46
  end
40
47
 
41
- # Executes the middleware, wrapping task execution with correlation context.
42
- # Evaluates conditions, determines correlation ID, and executes the task within
43
- # the correlation context for tracing purposes.
48
+ # Temporarily uses a new correlation ID for the duration of a block.
49
+ # Restores the previous ID after the block completes, even if an error occurs.
44
50
  #
45
- # @param task [CMDx::Task] the task being executed
46
- # @param callable [Proc] the callable that executes the task
51
+ # @param new_id [String] The correlation ID to use temporarily
52
+ # @yield The block to execute with the new correlation ID
53
+ # @return [Object] The result of the yielded block
47
54
  #
48
- # @return [Object] the result of the task execution
55
+ # @example Use temporary correlation ID
56
+ # Correlate.use("temp-id") do
57
+ # # Operations here use "temp-id"
58
+ # perform_operation
59
+ # end
60
+ # # Previous ID is restored
61
+ def use(new_id)
62
+ old_id = id
63
+ self.id = new_id
64
+ yield
65
+ ensure
66
+ self.id = old_id
67
+ end
68
+
69
+ # Middleware entry point that applies correlation ID logic to task execution.
49
70
  #
50
- # @example Task using correlation middleware
51
- # class ProcessOrderTask < CMDx::Task
52
- # use :middleware, CMDx::Middlewares::Correlate, id: "trace-123"
71
+ # Evaluates the condition from options and applies correlation ID handling
72
+ # if enabled. Generates or retrieves correlation IDs based on the :id option
73
+ # and stores them in task result metadata.
53
74
  #
54
- # def call
55
- # # Task execution is automatically wrapped with correlation
56
- # end
57
- # end
75
+ # @param task [Task] The task being executed
76
+ # @param options [Hash] Configuration options for correlation
77
+ # @option options [Symbol, Proc, Object, nil] :id The correlation ID source
78
+ # @option options [Symbol, Proc, Object, nil] :if Condition to enable correlation
79
+ # @option options [Symbol, Proc, Object, nil] :unless Condition to disable correlation
58
80
  #
59
- # @example Global configuration with conditional tracing
60
- # CMDx.configure do |config|
61
- # config.middlewares.register CMDx::Middlewares::Correlate, if: :should_trace?
62
- # end
63
- def call(task, callable)
64
- # Check if correlation should be applied based on conditions
65
- return callable.call(task) unless task.cmdx_eval(conditional)
81
+ # @yield The task execution block
82
+ #
83
+ # @return [Object] The result of task execution
84
+ #
85
+ # @example Basic usage with automatic ID generation
86
+ # Correlate.call(task, &block)
87
+ # @example Use custom correlation ID
88
+ # Correlate.call(task, id: "custom-123", &block)
89
+ # @example Use task method for ID
90
+ # Correlate.call(task, id: :correlation_id, &block)
91
+ # @example Use proc for dynamic ID generation
92
+ # Correlate.call(task, id: -> { "dynamic-#{Time.now.to_i}" }, &block)
93
+ # @example Conditional correlation
94
+ # Correlate.call(task, if: :enable_correlation, &block)
95
+ def call(task, **options, &)
96
+ return yield unless Utils::Condition.evaluate(task, options)
66
97
 
67
- # Get correlation ID using yield for dynamic generation
68
- correlation_id = task.cmdx_yield(id) ||
69
- CMDx::Correlator.id ||
70
- task.chain.id ||
71
- CMDx::Correlator.generate
98
+ correlation_id =
99
+ case callable = options[:id]
100
+ when Symbol then task.send(callable)
101
+ when Proc then task.instance_eval(&callable)
102
+ else
103
+ if callable.respond_to?(:call)
104
+ callable.call(task)
105
+ else
106
+ callable || id || Identifier.generate
107
+ end
108
+ end
72
109
 
73
- # Execute task with correlation context
74
- CMDx::Correlator.use(correlation_id) do
75
- callable.call(task)
76
- end
110
+ result = use(correlation_id, &)
111
+ task.result.metadata[:correlation_id] = correlation_id
112
+ result
77
113
  end
78
114
 
79
115
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMDx
4
+ module Middlewares
5
+ # Middleware for measuring task execution runtime.
6
+ #
7
+ # The Runtime middleware provides performance monitoring by measuring
8
+ # the execution time of tasks using monotonic clock for accuracy.
9
+ # It stores runtime measurements in task result metadata for analysis.
10
+ module Runtime
11
+
12
+ extend self
13
+
14
+ # Middleware entry point that measures task execution runtime.
15
+ #
16
+ # Evaluates the condition from options and measures execution time
17
+ # if enabled. Uses monotonic clock for precise timing measurements
18
+ # and stores the result in task metadata.
19
+ #
20
+ # @param task [Task] The task being executed
21
+ # @param options [Hash] Configuration options for runtime measurement
22
+ # @option options [Symbol, Proc, Object, nil] :if Condition to enable runtime measurement
23
+ # @option options [Symbol, Proc, Object, nil] :unless Condition to disable runtime measurement
24
+ #
25
+ # @yield The task execution block
26
+ #
27
+ # @return [Object] The result of task execution
28
+ #
29
+ # @example Basic usage with automatic runtime measurement
30
+ # Runtime.call(task, &block)
31
+ # @example Conditional runtime measurement
32
+ # Runtime.call(task, if: :enable_profiling, &block)
33
+ # @example Disable runtime measurement
34
+ # Runtime.call(task, unless: :skip_profiling, &block)
35
+ def call(task, **options)
36
+ return yield unless Utils::Condition.evaluate(task, options)
37
+
38
+ now = monotonic_time
39
+ result = yield
40
+ task.result.metadata[:runtime] = monotonic_time - now
41
+ result
42
+ end
43
+
44
+ private
45
+
46
+ # Gets the current monotonic time in milliseconds.
47
+ #
48
+ # Uses Process.clock_gettime with CLOCK_MONOTONIC for consistent
49
+ # timing measurements that are not affected by system clock changes.
50
+ #
51
+ # @return [Integer] Current monotonic time in milliseconds
52
+ def monotonic_time
53
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -2,89 +2,69 @@
2
2
 
3
3
  module CMDx
4
4
 
5
- # Custom exception raised when task execution exceeds the configured timeout limit.
6
- # Inherits from Interrupt to provide consistent error handling for timeout scenarios
7
- # and allow proper interruption of long-running tasks.
5
+ # Error raised when task execution exceeds the configured timeout limit.
6
+ #
7
+ # This error occurs when a task takes longer to execute than the specified
8
+ # time limit. Timeout errors are raised by Ruby's Timeout module and are
9
+ # caught by the middleware to properly fail the task with timeout information.
8
10
  TimeoutError = Class.new(Interrupt)
9
11
 
10
12
  module Middlewares
11
- # Middleware that provides execution timeout protection for tasks.
12
- # Automatically interrupts task execution if it exceeds the specified time limit,
13
- # preventing runaway processes and ensuring system responsiveness.
14
- class Timeout < CMDx::Middleware
13
+ # Middleware for enforcing execution time limits on tasks.
14
+ #
15
+ # The Timeout middleware provides execution time control by wrapping
16
+ # task execution with Ruby's Timeout module. It automatically fails
17
+ # tasks that exceed the configured time limit and provides detailed
18
+ # error information including the exceeded limit.
19
+ module Timeout
15
20
 
16
- # @return [Integer, Float, Symbol, Proc] The timeout value in seconds
17
- attr_reader :seconds
21
+ extend self
18
22
 
19
- # @return [Hash] The conditional options for timeout application
20
- attr_reader :conditional
23
+ # Default timeout limit in seconds when none is specified.
24
+ DEFAULT_LIMIT = 3
21
25
 
22
- # Initializes the timeout middleware with optional configuration.
26
+ # Middleware entry point that enforces execution time limits.
23
27
  #
24
- # @param options [Hash] configuration options for the middleware
25
- # @option options [Integer, Float, Symbol, Proc] :seconds timeout duration in seconds (default: 3)
26
- # @option options [Symbol, Proc] :if condition that must be truthy to apply timeout
27
- # @option options [Symbol, Proc] :unless condition that must be falsy to apply timeout
28
+ # Evaluates the condition from options and applies timeout control
29
+ # if enabled. Supports various timeout limit configurations including
30
+ # numeric values, task method calls, and dynamic proc evaluation.
28
31
  #
29
- # @return [Timeout] new instance of the middleware
32
+ # @param task [Task] The task being executed
33
+ # @param options [Hash] Configuration options for timeout control
34
+ # @option options [Numeric, Symbol, Proc, Object] :seconds The timeout limit source
35
+ # @option options [Symbol, Proc, Object, nil] :if Condition to enable timeout control
36
+ # @option options [Symbol, Proc, Object, nil] :unless Condition to disable timeout control
30
37
  #
31
- # @example Register with a middleware instance
32
- # use :middleware, CMDx::Middlewares::Timeout.new(seconds: 30)
38
+ # @yield The task execution block
33
39
  #
34
- # @example Register with fixed timeout
35
- # use :middleware, CMDx::Middlewares::Timeout, seconds: 30
40
+ # @return [Object] The result of task execution
36
41
  #
37
- # @example Register with dynamic timeout
38
- # use :middleware, CMDx::Middlewares::Timeout, seconds: -> { Rails.env.test? ? 1 : 10 }
42
+ # @raise [TimeoutError] When execution exceeds the configured limit
39
43
  #
40
- # @example Register with conditions
41
- # use :middleware, CMDx::Middlewares::Timeout, seconds: 5, if: :long_running?, unless: :skip_timeout?
42
- def initialize(options = {})
43
- @seconds = options[:seconds] || 3
44
- @conditional = options.slice(:if, :unless)
45
- end
46
-
47
- # Executes the middleware, wrapping task execution with timeout protection.
48
- # Evaluates conditions, determines timeout duration, and executes the task within
49
- # the timeout boundary to prevent runaway execution.
50
- #
51
- # @param task [CMDx::Task] the task being executed
52
- # @param callable [Proc] the callable that executes the task
53
- #
54
- # @return [Object] the result of the task execution
55
- #
56
- # @raise [TimeoutError] when task execution exceeds the timeout limit
57
- #
58
- # @example Task using timeout middleware
59
- # class ProcessFileTask < CMDx::Task
60
- # use :middleware, CMDx::Middlewares::Timeout, seconds: 10
61
- #
62
- # def call
63
- # # Task execution is automatically wrapped with timeout protection
64
- # end
65
- # end
66
- #
67
- # @example Global configuration with conditional timeout
68
- # CMDx.configure do |config|
69
- # config.middlewares.register CMDx::Middlewares::Timeout, seconds: 30, if: :large_dataset?
70
- # end
71
- def call(task, callable)
72
- # Check if timeout should be applied based on conditions
73
- return callable.call(task) unless task.cmdx_eval(conditional)
74
-
75
- # Get seconds using yield for dynamic generation
76
- limit = task.cmdx_yield(seconds) || 3
44
+ # @example Basic usage with default 3 second timeout
45
+ # Timeout.call(task, &block)
46
+ # @example Custom timeout limit in seconds
47
+ # Timeout.call(task, seconds: 10, &block)
48
+ # @example Use task method for timeout limit
49
+ # Timeout.call(task, seconds: :timeout_limit, &block)
50
+ # @example Use proc for dynamic timeout calculation
51
+ # Timeout.call(task, seconds: -> { calculate_timeout }, &block)
52
+ # @example Conditional timeout control
53
+ # Timeout.call(task, if: :enable_timeout, &block)
54
+ def call(task, **options, &)
55
+ return yield unless Utils::Condition.evaluate(task, options)
77
56
 
78
- # Ensure limit is numeric, fallback to default if not
79
- limit = 3 unless limit.is_a?(Numeric)
57
+ limit =
58
+ case callable = options[:seconds]
59
+ when Numeric then callable
60
+ when Symbol then task.send(callable)
61
+ when Proc then task.instance_eval(&callable)
62
+ else callable.respond_to?(:call) ? callable.call(task) : DEFAULT_LIMIT
63
+ end
80
64
 
81
- # Apply timeout protection
82
- ::Timeout.timeout(limit, TimeoutError, "execution exceeded #{limit} seconds") do
83
- callable.call(task)
84
- end
65
+ ::Timeout.timeout(limit, TimeoutError, "execution exceeded #{limit} seconds", &)
85
66
  rescue TimeoutError => e
86
- task.fail!(reason: "[#{e.class}] #{e.message}", original_exception: e, seconds: limit)
87
- task.result
67
+ task.result.tap { |r| r.fail!("[#{e.class}] #{e.message}", cause: e, limit:) }
88
68
  end
89
69
 
90
70
  end
data/lib/cmdx/railtie.rb CHANGED
@@ -1,32 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CMDx
4
- # Rails integration for CMDx framework.
5
- #
6
- # Provides Rails-specific configuration including internationalization
7
- # locale loading and autoload path configuration for CMDx workflows and tasks.
4
+ # Rails integration class that automatically configures CMDx when the Rails
5
+ # application initializes. Handles locale configuration and I18n setup.
8
6
  class Railtie < Rails::Railtie
9
7
 
10
8
  railtie_name :cmdx
11
9
 
12
- # Configure internationalization locales for CMDx.
10
+ # Configures CMDx locales during Rails application initialization.
13
11
  #
14
- # Loads available locale files from the CMDx locales directory
15
- # and adds them to the I18n load path. Only loads locales that
16
- # are configured as available in the Rails application.
12
+ # Iterates through available locales from the Rails configuration and loads
13
+ # corresponding CMDx locale files. Reloads the I18n system to ensure
14
+ # all locales are properly registered.
17
15
  #
18
16
  # @param app [Rails::Application] the Rails application instance
19
17
  #
20
- # @return [void]
18
+ # @raise [LoadError] if locale files cannot be loaded
21
19
  #
22
- # @raise [StandardError] if I18n reload fails
23
- #
24
- # @example Configure locales during Rails initialization
25
- # # This initializer runs automatically during Rails boot
26
- # # when CMDx is included in a Rails application
20
+ # @example
21
+ # # This initializer runs automatically when Rails starts
22
+ # # It will load locales like en.yml, es.yml, fr.yml if they exist
23
+ # # in the CMDx gem's locales directory
27
24
  initializer("cmdx.configure_locales") do |app|
28
25
  Array(app.config.i18n.available_locales).each do |locale|
29
- path = File.expand_path("../../../lib/locales/#{locale}.yml", __FILE__)
26
+ path = CMDx.gem_path.join("lib/locales/#{locale}.yml")
30
27
  next unless File.file?(path)
31
28
 
32
29
  I18n.load_path << path
@@ -35,35 +32,5 @@ module CMDx
35
32
  I18n.reload!
36
33
  end
37
34
 
38
- # Configure Rails autoload paths for CMDx components.
39
- #
40
- # Adds the app/cmds directory to Rails autoload paths and configures
41
- # autoloaders to collapse the workflows and tasks subdirectories.
42
- # This enables Rails to automatically load CMDx workflows and tasks
43
- # from the conventional directory structure.
44
- #
45
- # @param app [Rails::Application] the Rails application instance
46
- #
47
- # @return [void]
48
- #
49
- # @raise [StandardError] if autoloader configuration fails
50
- #
51
- # @example Configure autoload paths during Rails initialization
52
- # # This initializer runs automatically during Rails boot
53
- # # Enables loading of:
54
- # # - app/cmds/workflows/my_workflow.rb
55
- # # - app/cmds/tasks/my_task.rb
56
- initializer("cmdx.configure_rails_auto_load_paths") do |app|
57
- app.config.autoload_paths += %w[app/cmds]
58
-
59
- types = %w[workflows tasks]
60
- app.autoloaders.each do |autoloader|
61
- types.each do |concept|
62
- dir = app.root.join("app/cmds/#{concept}")
63
- autoloader.collapse(dir)
64
- end
65
- end
66
- end
67
-
68
35
  end
69
36
  end