cmdx 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.cursor/prompts/yardoc.md +1 -0
- data/CHANGELOG.md +6 -0
- data/LLM.md +9 -0
- data/README.md +6 -1
- data/docs/getting_started.md +9 -0
- data/docs/index.md +13 -1
- data/lib/cmdx/attribute.rb +82 -1
- data/lib/cmdx/attribute_registry.rb +20 -0
- data/lib/cmdx/attribute_value.rb +25 -0
- data/lib/cmdx/callback_registry.rb +19 -0
- data/lib/cmdx/chain.rb +34 -1
- data/lib/cmdx/coercion_registry.rb +18 -0
- data/lib/cmdx/coercions/array.rb +2 -0
- data/lib/cmdx/coercions/big_decimal.rb +3 -0
- data/lib/cmdx/coercions/boolean.rb +5 -0
- data/lib/cmdx/coercions/complex.rb +2 -0
- data/lib/cmdx/coercions/date.rb +4 -0
- data/lib/cmdx/coercions/date_time.rb +5 -0
- data/lib/cmdx/coercions/float.rb +2 -0
- data/lib/cmdx/coercions/hash.rb +2 -0
- data/lib/cmdx/coercions/integer.rb +2 -0
- data/lib/cmdx/coercions/rational.rb +2 -0
- data/lib/cmdx/coercions/string.rb +2 -0
- data/lib/cmdx/coercions/symbol.rb +2 -0
- data/lib/cmdx/coercions/time.rb +5 -0
- data/lib/cmdx/configuration.rb +111 -3
- data/lib/cmdx/context.rb +36 -0
- data/lib/cmdx/deprecator.rb +3 -0
- data/lib/cmdx/errors.rb +22 -0
- data/lib/cmdx/executor.rb +43 -0
- data/lib/cmdx/faults.rb +14 -0
- data/lib/cmdx/identifier.rb +2 -0
- data/lib/cmdx/locale.rb +3 -0
- data/lib/cmdx/log_formatters/json.rb +2 -0
- data/lib/cmdx/log_formatters/key_value.rb +2 -0
- data/lib/cmdx/log_formatters/line.rb +2 -0
- data/lib/cmdx/log_formatters/logstash.rb +2 -0
- data/lib/cmdx/log_formatters/raw.rb +2 -0
- data/lib/cmdx/middleware_registry.rb +20 -0
- data/lib/cmdx/middlewares/correlate.rb +11 -0
- data/lib/cmdx/middlewares/runtime.rb +4 -0
- data/lib/cmdx/middlewares/timeout.rb +4 -0
- data/lib/cmdx/pipeline.rb +20 -1
- data/lib/cmdx/railtie.rb +4 -0
- data/lib/cmdx/result.rb +123 -1
- data/lib/cmdx/task.rb +91 -1
- data/lib/cmdx/utils/call.rb +2 -0
- data/lib/cmdx/utils/condition.rb +3 -0
- data/lib/cmdx/utils/format.rb +5 -0
- data/lib/cmdx/validator_registry.rb +18 -0
- data/lib/cmdx/validators/exclusion.rb +2 -0
- data/lib/cmdx/validators/format.rb +2 -0
- data/lib/cmdx/validators/inclusion.rb +2 -0
- data/lib/cmdx/validators/length.rb +14 -0
- data/lib/cmdx/validators/numeric.rb +14 -0
- data/lib/cmdx/validators/presence.rb +2 -0
- data/lib/cmdx/version.rb +4 -1
- data/lib/cmdx/workflow.rb +10 -0
- data/lib/cmdx.rb +8 -0
- data/lib/generators/cmdx/locale_generator.rb +0 -1
- metadata +1 -1
    
        data/lib/cmdx/executor.rb
    CHANGED
    
    | @@ -8,6 +8,14 @@ module CMDx | |
| 8 8 | 
             
              # and proper error handling for different types of failures.
         | 
| 9 9 | 
             
              class Executor
         | 
| 10 10 |  | 
| 11 | 
            +
                # Returns the task being executed.
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # @return [Task] The task instance
         | 
| 14 | 
            +
                #
         | 
| 15 | 
            +
                # @example
         | 
| 16 | 
            +
                #   executor.task.id # => "abc123"
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                # @rbs @task: Task
         | 
| 11 19 | 
             
                attr_reader :task
         | 
| 12 20 |  | 
| 13 21 | 
             
                # @param task [CMDx::Task] The task to execute
         | 
| @@ -16,6 +24,8 @@ module CMDx | |
| 16 24 | 
             
                #
         | 
| 17 25 | 
             
                # @example
         | 
| 18 26 | 
             
                #   executor = CMDx::Executor.new(my_task)
         | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # @rbs (Task task) -> void
         | 
| 19 29 | 
             
                def initialize(task)
         | 
| 20 30 | 
             
                  @task = task
         | 
| 21 31 | 
             
                end
         | 
| @@ -32,6 +42,8 @@ module CMDx | |
| 32 42 | 
             
                # @example
         | 
| 33 43 | 
             
                #   CMDx::Executor.execute(my_task)
         | 
| 34 44 | 
             
                #   CMDx::Executor.execute(my_task, raise: true)
         | 
| 45 | 
            +
                #
         | 
| 46 | 
            +
                # @rbs (Task task, raise: bool) -> Result
         | 
| 35 47 | 
             
                def self.execute(task, raise: false)
         | 
| 36 48 | 
             
                  instance = new(task)
         | 
| 37 49 | 
             
                  raise ? instance.execute! : instance.execute
         | 
| @@ -44,6 +56,8 @@ module CMDx | |
| 44 56 | 
             
                # @example
         | 
| 45 57 | 
             
                #   executor = CMDx::Executor.new(my_task)
         | 
| 46 58 | 
             
                #   result = executor.execute
         | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # @rbs () -> Result
         | 
| 47 61 | 
             
                def execute
         | 
| 48 62 | 
             
                  task.class.settings[:middlewares].call!(task) do
         | 
| 49 63 | 
             
                    pre_execution! unless @pre_execution
         | 
| @@ -73,6 +87,8 @@ module CMDx | |
| 73 87 | 
             
                # @example
         | 
| 74 88 | 
             
                #   executor = CMDx::Executor.new(my_task)
         | 
| 75 89 | 
             
                #   result = executor.execute!
         | 
| 90 | 
            +
                #
         | 
| 91 | 
            +
                # @rbs () -> Result
         | 
| 76 92 | 
             
                def execute!
         | 
| 77 93 | 
             
                  task.class.settings[:middlewares].call!(task) do
         | 
| 78 94 | 
             
                    pre_execution! unless @pre_execution
         | 
| @@ -104,6 +120,8 @@ module CMDx | |
| 104 120 | 
             
                #
         | 
| 105 121 | 
             
                # @example
         | 
| 106 122 | 
             
                #   halt_execution?(fault_exception)
         | 
| 123 | 
            +
                #
         | 
| 124 | 
            +
                # @rbs (Exception exception) -> bool
         | 
| 107 125 | 
             
                def halt_execution?(exception)
         | 
| 108 126 | 
             
                  breakpoints = task.class.settings[:breakpoints] || task.class.settings[:task_breakpoints]
         | 
| 109 127 | 
             
                  breakpoints = Array(breakpoints).map(&:to_s).uniq
         | 
| @@ -119,6 +137,8 @@ module CMDx | |
| 119 137 | 
             
                #
         | 
| 120 138 | 
             
                # @example
         | 
| 121 139 | 
             
                #   retry_execution?(standard_error)
         | 
| 140 | 
            +
                #
         | 
| 141 | 
            +
                # @rbs (Exception exception) -> bool
         | 
| 122 142 | 
             
                def retry_execution?(exception)
         | 
| 123 143 | 
             
                  available_retries = (task.class.settings[:retries] || 0).to_i
         | 
| 124 144 | 
             
                  return false unless available_retries.positive?
         | 
| @@ -151,6 +171,8 @@ module CMDx | |
| 151 171 | 
             
                #
         | 
| 152 172 | 
             
                # @example
         | 
| 153 173 | 
             
                #   raise_exception(standard_error)
         | 
| 174 | 
            +
                #
         | 
| 175 | 
            +
                # @rbs (Exception exception) -> void
         | 
| 154 176 | 
             
                def raise_exception(exception)
         | 
| 155 177 | 
             
                  Chain.clear
         | 
| 156 178 |  | 
| @@ -165,6 +187,8 @@ module CMDx | |
| 165 187 | 
             
                #
         | 
| 166 188 | 
             
                # @example
         | 
| 167 189 | 
             
                #   invoke_callbacks(:before_execution)
         | 
| 190 | 
            +
                #
         | 
| 191 | 
            +
                # @rbs (Symbol type) -> void
         | 
| 168 192 | 
             
                def invoke_callbacks(type)
         | 
| 169 193 | 
             
                  task.class.settings[:callbacks].invoke(type, task)
         | 
| 170 194 | 
             
                end
         | 
| @@ -172,11 +196,15 @@ module CMDx | |
| 172 196 | 
             
                private
         | 
| 173 197 |  | 
| 174 198 | 
             
                # Lazy loaded repeator instance to handle retries.
         | 
| 199 | 
            +
                #
         | 
| 200 | 
            +
                # @rbs () -> untyped
         | 
| 175 201 | 
             
                def repeator
         | 
| 176 202 | 
             
                  @repeator ||= Repeator.new(task)
         | 
| 177 203 | 
             
                end
         | 
| 178 204 |  | 
| 179 205 | 
             
                # Performs pre-execution tasks including validation and attribute verification.
         | 
| 206 | 
            +
                #
         | 
| 207 | 
            +
                # @rbs () -> void
         | 
| 180 208 | 
             
                def pre_execution!
         | 
| 181 209 | 
             
                  @pre_execution = true
         | 
| 182 210 |  | 
| @@ -195,6 +223,8 @@ module CMDx | |
| 195 223 | 
             
                end
         | 
| 196 224 |  | 
| 197 225 | 
             
                # Executes the main task logic.
         | 
| 226 | 
            +
                #
         | 
| 227 | 
            +
                # @rbs () -> void
         | 
| 198 228 | 
             
                def execution!
         | 
| 199 229 | 
             
                  invoke_callbacks(:before_execution)
         | 
| 200 230 |  | 
| @@ -203,6 +233,8 @@ module CMDx | |
| 203 233 | 
             
                end
         | 
| 204 234 |  | 
| 205 235 | 
             
                # Performs post-execution tasks including callback invocation.
         | 
| 236 | 
            +
                #
         | 
| 237 | 
            +
                # @rbs () -> void
         | 
| 206 238 | 
             
                def post_execution!
         | 
| 207 239 | 
             
                  invoke_callbacks(:"on_#{task.result.state}")
         | 
| 208 240 | 
             
                  invoke_callbacks(:on_executed) if task.result.executed?
         | 
| @@ -213,6 +245,8 @@ module CMDx | |
| 213 245 | 
             
                end
         | 
| 214 246 |  | 
| 215 247 | 
             
                # Finalizes execution by freezing the task and logging results.
         | 
| 248 | 
            +
                #
         | 
| 249 | 
            +
                # @rbs () -> Result
         | 
| 216 250 | 
             
                def finalize_execution!
         | 
| 217 251 | 
             
                  log_execution!
         | 
| 218 252 | 
             
                  log_backtrace! if task.class.settings[:backtrace]
         | 
| @@ -222,11 +256,15 @@ module CMDx | |
| 222 256 | 
             
                end
         | 
| 223 257 |  | 
| 224 258 | 
             
                # Logs the execution result at the configured log level.
         | 
| 259 | 
            +
                #
         | 
| 260 | 
            +
                # @rbs () -> void
         | 
| 225 261 | 
             
                def log_execution!
         | 
| 226 262 | 
             
                  task.logger.info { task.result.to_h }
         | 
| 227 263 | 
             
                end
         | 
| 228 264 |  | 
| 229 265 | 
             
                # Logs the backtrace of the exception if the task failed.
         | 
| 266 | 
            +
                #
         | 
| 267 | 
            +
                # @rbs () -> void
         | 
| 230 268 | 
             
                def log_backtrace!
         | 
| 231 269 | 
             
                  return unless task.result.failed?
         | 
| 232 270 |  | 
| @@ -244,6 +282,8 @@ module CMDx | |
| 244 282 | 
             
                end
         | 
| 245 283 |  | 
| 246 284 | 
             
                # Freezes the task and its associated objects to prevent modifications.
         | 
| 285 | 
            +
                #
         | 
| 286 | 
            +
                # @rbs () -> void
         | 
| 247 287 | 
             
                def freeze_execution!
         | 
| 248 288 | 
             
                  # Stubbing on frozen objects is not allowed in most test environments.
         | 
| 249 289 | 
             
                  skip_freezing = ENV.fetch("SKIP_CMDX_FREEZING", false)
         | 
| @@ -260,6 +300,9 @@ module CMDx | |
| 260 300 | 
             
                  task.chain.freeze
         | 
| 261 301 | 
             
                end
         | 
| 262 302 |  | 
| 303 | 
            +
                # Clears the chain if the task is the outermost (top-level) task.
         | 
| 304 | 
            +
                #
         | 
| 305 | 
            +
                # @rbs () -> void
         | 
| 263 306 | 
             
                def clear_chain!
         | 
| 264 307 | 
             
                  return unless task.result.index.zero?
         | 
| 265 308 |  | 
    
        data/lib/cmdx/faults.rb
    CHANGED
    
    | @@ -11,6 +11,14 @@ module CMDx | |
| 11 11 |  | 
| 12 12 | 
             
                extend Forwardable
         | 
| 13 13 |  | 
| 14 | 
            +
                # Returns the result that caused this fault.
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @return [Result] The result instance
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                # @example
         | 
| 19 | 
            +
                #   fault.result.reason # => "Validation failed"
         | 
| 20 | 
            +
                #
         | 
| 21 | 
            +
                # @rbs @result: Result
         | 
| 14 22 | 
             
                attr_reader :result
         | 
| 15 23 |  | 
| 16 24 | 
             
                def_delegators :result, :task, :context, :chain
         | 
| @@ -24,6 +32,8 @@ module CMDx | |
| 24 32 | 
             
                # @example
         | 
| 25 33 | 
             
                #   fault = Fault.new(task_result)
         | 
| 26 34 | 
             
                #   fault.result.reason # => "Task validation failed"
         | 
| 35 | 
            +
                #
         | 
| 36 | 
            +
                # @rbs (Result result) -> void
         | 
| 27 37 | 
             
                def initialize(result)
         | 
| 28 38 | 
             
                  @result = result
         | 
| 29 39 |  | 
| @@ -41,6 +51,8 @@ module CMDx | |
| 41 51 | 
             
                  # @example
         | 
| 42 52 | 
             
                  #   Fault.for?(UserTask, AdminUserTask)
         | 
| 43 53 | 
             
                  #   # => true if fault.task is a UserTask or AdminUserTask
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # @rbs (*Class tasks) -> Class
         | 
| 44 56 | 
             
                  def for?(*tasks)
         | 
| 45 57 | 
             
                    temp_fault = Class.new(self) do
         | 
| 46 58 | 
             
                      def self.===(other)
         | 
| @@ -62,6 +74,8 @@ module CMDx | |
| 62 74 | 
             
                  # @example
         | 
| 63 75 | 
             
                  #   Fault.matches? { |fault| fault.result.metadata[:critical] }
         | 
| 64 76 | 
             
                  #   # => true if fault has critical metadata
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  # @rbs () { (Fault) -> bool } -> Class
         | 
| 65 79 | 
             
                  def matches?(&block)
         | 
| 66 80 | 
             
                    raise ArgumentError, "block required" unless block_given?
         | 
| 67 81 |  | 
    
        data/lib/cmdx/identifier.rb
    CHANGED
    
    
    
        data/lib/cmdx/locale.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ module CMDx | |
| 8 8 |  | 
| 9 9 | 
             
                extend self
         | 
| 10 10 |  | 
| 11 | 
            +
                # @rbs EN: Hash[String, untyped]
         | 
| 11 12 | 
             
                EN = YAML.load_file(CMDx.gem_path.join("lib/locales/en.yml")).freeze
         | 
| 12 13 | 
             
                private_constant :EN
         | 
| 13 14 |  | 
| @@ -34,6 +35,8 @@ module CMDx | |
| 34 35 | 
             
                # @example With fallback
         | 
| 35 36 | 
             
                #   Locale.translate("missing.key", default: "Custom fallback message")
         | 
| 36 37 | 
             
                #   # => "Custom fallback message"
         | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # @rbs ((String | Symbol) key, **untyped options) -> String
         | 
| 37 40 | 
             
                def translate(key, **options)
         | 
| 38 41 | 
             
                  options[:default] ||= EN.dig("en", *key.to_s.split("."))
         | 
| 39 42 | 
             
                  return ::I18n.t(key, **options) if defined?(::I18n)
         | 
| @@ -21,6 +21,8 @@ module CMDx | |
| 21 21 | 
             
                  # @example Basic usage
         | 
| 22 22 | 
             
                  #   logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
         | 
| 23 23 | 
             
                  #   # => '{"severity":"INFO","timestamp":"2024-01-15T10:30:45.123456Z","progname":"MyApp","pid":12345,"message":"User logged in"}\n'
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @rbs (String severity, Time time, String? progname, String message) -> String
         | 
| 24 26 | 
             
                  def call(severity, time, progname, message)
         | 
| 25 27 | 
             
                    hash = {
         | 
| 26 28 | 
             
                      severity:,
         | 
| @@ -21,6 +21,8 @@ module CMDx | |
| 21 21 | 
             
                  # @example Basic usage
         | 
| 22 22 | 
             
                  #   logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
         | 
| 23 23 | 
             
                  #   # => "severity=INFO timestamp=2024-01-15T10:30:45.123456Z progname=MyApp pid=12345 message=User logged in\n"
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @rbs (String severity, Time time, String? progname, String message) -> String
         | 
| 24 26 | 
             
                  def call(severity, time, progname, message)
         | 
| 25 27 | 
             
                    hash = {
         | 
| 26 28 | 
             
                      severity:,
         | 
| @@ -21,6 +21,8 @@ module CMDx | |
| 21 21 | 
             
                  # @example Basic usage
         | 
| 22 22 | 
             
                  #   logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
         | 
| 23 23 | 
             
                  #   # => "I, [2024-01-15T10:30:45.123456Z #12345] INFO -- MyApp: User logged in\n"
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @rbs (String severity, Time time, String? progname, String message) -> String
         | 
| 24 26 | 
             
                  def call(severity, time, progname, message)
         | 
| 25 27 | 
             
                    "#{severity[0]}, [#{time.utc.iso8601(6)} ##{Process.pid}] #{severity} -- #{progname}: #{message}\n"
         | 
| 26 28 | 
             
                  end
         | 
| @@ -22,6 +22,8 @@ module CMDx | |
| 22 22 | 
             
                  # @example Basic usage
         | 
| 23 23 | 
             
                  #   logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
         | 
| 24 24 | 
             
                  #   # => '{"severity":"INFO","progname":"MyApp","pid":12345,"message":"User logged in","@version":"1","@timestamp":"2024-01-15T10:30:45.123456Z"}\n'
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # @rbs (String severity, Time time, String? progname, String message) -> String
         | 
| 25 27 | 
             
                  def call(severity, time, progname, message)
         | 
| 26 28 | 
             
                    hash = {
         | 
| 27 29 | 
             
                      severity:,
         | 
| @@ -22,6 +22,8 @@ module CMDx | |
| 22 22 | 
             
                  # @example Basic usage
         | 
| 23 23 | 
             
                  #   logger_formatter.call("INFO", Time.now, "MyApp", "User logged in")
         | 
| 24 24 | 
             
                  #   # => "User logged in\n"
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # @rbs (String severity, Time time, String? progname, String message) -> String
         | 
| 25 27 | 
             
                  def call(severity, time, progname, message)
         | 
| 26 28 | 
             
                    "#{message}\n"
         | 
| 27 29 | 
             
                  end
         | 
| @@ -9,6 +9,14 @@ module CMDx | |
| 9 9 | 
             
              # they were registered.
         | 
| 10 10 | 
             
              class MiddlewareRegistry
         | 
| 11 11 |  | 
| 12 | 
            +
                # Returns the ordered collection of middleware entries.
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # @return [Array<Array>] Array of middleware-options pairs
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @example
         | 
| 17 | 
            +
                #   registry.registry # => [[LoggingMiddleware, {level: :debug}], [AuthMiddleware, {}]]
         | 
| 18 | 
            +
                #
         | 
| 19 | 
            +
                # @rbs @registry: Array[Array[untyped]]
         | 
| 12 20 | 
             
                attr_reader :registry
         | 
| 13 21 | 
             
                alias to_a registry
         | 
| 14 22 |  | 
| @@ -19,6 +27,8 @@ module CMDx | |
| 19 27 | 
             
                # @example
         | 
| 20 28 | 
             
                #   registry = MiddlewareRegistry.new
         | 
| 21 29 | 
             
                #   registry = MiddlewareRegistry.new([[MyMiddleware, {option: 'value'}]])
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                # @rbs (?Array[Array[untyped]] registry) -> void
         | 
| 22 32 | 
             
                def initialize(registry = [])
         | 
| 23 33 | 
             
                  @registry = registry
         | 
| 24 34 | 
             
                end
         | 
| @@ -29,6 +39,8 @@ module CMDx | |
| 29 39 | 
             
                #
         | 
| 30 40 | 
             
                # @example
         | 
| 31 41 | 
             
                #   new_registry = registry.dup
         | 
| 42 | 
            +
                #
         | 
| 43 | 
            +
                # @rbs () -> MiddlewareRegistry
         | 
| 32 44 | 
             
                def dup
         | 
| 33 45 | 
             
                  self.class.new(registry.map(&:dup))
         | 
| 34 46 | 
             
                end
         | 
| @@ -46,6 +58,8 @@ module CMDx | |
| 46 58 | 
             
                # @example
         | 
| 47 59 | 
             
                #   registry.register(LoggingMiddleware, at: 0, log_level: :debug)
         | 
| 48 60 | 
             
                #   registry.register(AuthMiddleware, at: -1, timeout: 30)
         | 
| 61 | 
            +
                #
         | 
| 62 | 
            +
                # @rbs (untyped middleware, ?at: Integer, **untyped options) -> self
         | 
| 49 63 | 
             
                def register(middleware, at: -1, **options)
         | 
| 50 64 | 
             
                  registry.insert(at, [middleware, options])
         | 
| 51 65 | 
             
                  self
         | 
| @@ -59,6 +73,8 @@ module CMDx | |
| 59 73 | 
             
                #
         | 
| 60 74 | 
             
                # @example
         | 
| 61 75 | 
             
                #   registry.deregister(LoggingMiddleware)
         | 
| 76 | 
            +
                #
         | 
| 77 | 
            +
                # @rbs (untyped middleware) -> self
         | 
| 62 78 | 
             
                def deregister(middleware)
         | 
| 63 79 | 
             
                  registry.reject! { |mw, _opts| mw == middleware }
         | 
| 64 80 | 
             
                  self
         | 
| @@ -79,6 +95,8 @@ module CMDx | |
| 79 95 | 
             
                #   result = registry.call!(my_task) do |processed_task|
         | 
| 80 96 | 
             
                #     processed_task.execute
         | 
| 81 97 | 
             
                #   end
         | 
| 98 | 
            +
                #
         | 
| 99 | 
            +
                # @rbs (untyped task) { (untyped) -> untyped } -> untyped
         | 
| 82 100 | 
             
                def call!(task, &)
         | 
| 83 101 | 
             
                  raise ArgumentError, "block required" unless block_given?
         | 
| 84 102 |  | 
| @@ -96,6 +114,8 @@ module CMDx | |
| 96 114 | 
             
                # @yieldparam task [Object] The processed task object
         | 
| 97 115 | 
             
                #
         | 
| 98 116 | 
             
                # @return [Object] Result of the block execution or next middleware call
         | 
| 117 | 
            +
                #
         | 
| 118 | 
            +
                # @rbs (Integer index, untyped task) { (untyped) -> untyped } -> untyped
         | 
| 99 119 | 
             
                def recursively_call_middleware(index, task, &block)
         | 
| 100 120 | 
             
                  return yield(task) if index >= registry.size
         | 
| 101 121 |  | 
| @@ -12,6 +12,7 @@ module CMDx | |
| 12 12 |  | 
| 13 13 | 
             
                  extend self
         | 
| 14 14 |  | 
| 15 | 
            +
                  # @rbs THREAD_KEY: Symbol
         | 
| 15 16 | 
             
                  THREAD_KEY = :cmdx_correlate
         | 
| 16 17 |  | 
| 17 18 | 
             
                  # Retrieves the current correlation ID from thread-local storage.
         | 
| @@ -20,6 +21,8 @@ module CMDx | |
| 20 21 | 
             
                  #
         | 
| 21 22 | 
             
                  # @example Get current correlation ID
         | 
| 22 23 | 
             
                  #   Correlate.id # => "550e8400-e29b-41d4-a716-446655440000"
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @rbs () -> String?
         | 
| 23 26 | 
             
                  def id
         | 
| 24 27 | 
             
                    Thread.current[THREAD_KEY]
         | 
| 25 28 | 
             
                  end
         | 
| @@ -31,6 +34,8 @@ module CMDx | |
| 31 34 | 
             
                  #
         | 
| 32 35 | 
             
                  # @example Set correlation ID
         | 
| 33 36 | 
             
                  #   Correlate.id = "abc-123-def"
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  # @rbs (String id) -> String
         | 
| 34 39 | 
             
                  def id=(id)
         | 
| 35 40 | 
             
                    Thread.current[THREAD_KEY] = id
         | 
| 36 41 | 
             
                  end
         | 
| @@ -41,6 +46,8 @@ module CMDx | |
| 41 46 | 
             
                  #
         | 
| 42 47 | 
             
                  # @example Clear correlation ID
         | 
| 43 48 | 
             
                  #   Correlate.clear
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @rbs () -> nil
         | 
| 44 51 | 
             
                  def clear
         | 
| 45 52 | 
             
                    Thread.current[THREAD_KEY] = nil
         | 
| 46 53 | 
             
                  end
         | 
| @@ -58,6 +65,8 @@ module CMDx | |
| 58 65 | 
             
                  #     perform_operation
         | 
| 59 66 | 
             
                  #   end
         | 
| 60 67 | 
             
                  #   # Previous ID is restored
         | 
| 68 | 
            +
                  #
         | 
| 69 | 
            +
                  # @rbs (String new_id) { () -> untyped } -> untyped
         | 
| 61 70 | 
             
                  def use(new_id)
         | 
| 62 71 | 
             
                    old_id = id
         | 
| 63 72 | 
             
                    self.id = new_id
         | 
| @@ -92,6 +101,8 @@ module CMDx | |
| 92 101 | 
             
                  #   Correlate.call(task, id: -> { "dynamic-#{Time.now.to_i}" }, &block)
         | 
| 93 102 | 
             
                  # @example Conditional correlation
         | 
| 94 103 | 
             
                  #   Correlate.call(task, if: :enable_correlation, &block)
         | 
| 104 | 
            +
                  #
         | 
| 105 | 
            +
                  # @rbs (Task task, **untyped options) { () -> untyped } -> untyped
         | 
| 95 106 | 
             
                  def call(task, **options, &)
         | 
| 96 107 | 
             
                    return yield unless Utils::Condition.evaluate(task, options)
         | 
| 97 108 |  | 
| @@ -32,6 +32,8 @@ module CMDx | |
| 32 32 | 
             
                  #   Runtime.call(task, if: :enable_profiling, &block)
         | 
| 33 33 | 
             
                  # @example Disable runtime measurement
         | 
| 34 34 | 
             
                  #   Runtime.call(task, unless: :skip_profiling, &block)
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  # @rbs (Task task, **untyped options) { () -> untyped } -> untyped
         | 
| 35 37 | 
             
                  def call(task, **options)
         | 
| 36 38 | 
             
                    return yield unless Utils::Condition.evaluate(task, options)
         | 
| 37 39 |  | 
| @@ -49,6 +51,8 @@ module CMDx | |
| 49 51 | 
             
                  # timing measurements that are not affected by system clock changes.
         | 
| 50 52 | 
             
                  #
         | 
| 51 53 | 
             
                  # @return [Integer] Current monotonic time in milliseconds
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # @rbs () -> Integer
         | 
| 52 56 | 
             
                  def monotonic_time
         | 
| 53 57 | 
             
                    Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
         | 
| 54 58 | 
             
                  end
         | 
| @@ -21,6 +21,8 @@ module CMDx | |
| 21 21 | 
             
                  extend self
         | 
| 22 22 |  | 
| 23 23 | 
             
                  # Default timeout limit in seconds when none is specified.
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @rbs DEFAULT_LIMIT: Integer
         | 
| 24 26 | 
             
                  DEFAULT_LIMIT = 3
         | 
| 25 27 |  | 
| 26 28 | 
             
                  # Middleware entry point that enforces execution time limits.
         | 
| @@ -51,6 +53,8 @@ module CMDx | |
| 51 53 | 
             
                  #   Timeout.call(task, seconds: -> { calculate_timeout }, &block)
         | 
| 52 54 | 
             
                  # @example Conditional timeout control
         | 
| 53 55 | 
             
                  #   Timeout.call(task, if: :enable_timeout, &block)
         | 
| 56 | 
            +
                  #
         | 
| 57 | 
            +
                  # @rbs (Task task, **untyped options) { () -> untyped } -> untyped
         | 
| 54 58 | 
             
                  def call(task, **options, &)
         | 
| 55 59 | 
             
                    return yield unless Utils::Condition.evaluate(task, options)
         | 
| 56 60 |  | 
    
        data/lib/cmdx/pipeline.rb
    CHANGED
    
    | @@ -6,7 +6,14 @@ module CMDx | |
| 6 6 | 
             
              # and handling breakpoints that can interrupt execution at specific task statuses.
         | 
| 7 7 | 
             
              class Pipeline
         | 
| 8 8 |  | 
| 9 | 
            -
                #  | 
| 9 | 
            +
                # Returns the workflow being executed by this pipeline.
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # @return [Workflow] The workflow instance
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # @example
         | 
| 14 | 
            +
                #   pipeline.workflow.context[:status] # => "processing"
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @rbs @workflow: Workflow
         | 
| 10 17 | 
             
                attr_reader :workflow
         | 
| 11 18 |  | 
| 12 19 | 
             
                # @param workflow [Workflow] The workflow to execute
         | 
| @@ -15,6 +22,8 @@ module CMDx | |
| 15 22 | 
             
                #
         | 
| 16 23 | 
             
                # @example
         | 
| 17 24 | 
             
                #   pipeline = Pipeline.new(my_workflow)
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                # @rbs (Workflow workflow) -> void
         | 
| 18 27 | 
             
                def initialize(workflow)
         | 
| 19 28 | 
             
                  @workflow = workflow
         | 
| 20 29 | 
             
                end
         | 
| @@ -27,6 +36,8 @@ module CMDx | |
| 27 36 | 
             
                #
         | 
| 28 37 | 
             
                # @example
         | 
| 29 38 | 
             
                #   Pipeline.execute(my_workflow)
         | 
| 39 | 
            +
                #
         | 
| 40 | 
            +
                # @rbs (Workflow workflow) -> void
         | 
| 30 41 | 
             
                def self.execute(workflow)
         | 
| 31 42 | 
             
                  new(workflow).execute
         | 
| 32 43 | 
             
                end
         | 
| @@ -40,6 +51,8 @@ module CMDx | |
| 40 51 | 
             
                # @example
         | 
| 41 52 | 
             
                #   pipeline = Pipeline.new(my_workflow)
         | 
| 42 53 | 
             
                #   pipeline.execute
         | 
| 54 | 
            +
                #
         | 
| 55 | 
            +
                # @rbs () -> void
         | 
| 43 56 | 
             
                def execute
         | 
| 44 57 | 
             
                  workflow.class.pipeline.each do |group|
         | 
| 45 58 | 
             
                    next unless Utils::Condition.evaluate(workflow, group.options)
         | 
| @@ -65,6 +78,8 @@ module CMDx | |
| 65 78 | 
             
                #
         | 
| 66 79 | 
             
                # @example
         | 
| 67 80 | 
             
                #   execute_group_tasks(group, ["failed", "skipped"])
         | 
| 81 | 
            +
                #
         | 
| 82 | 
            +
                # @rbs (untyped group, Array[String] breakpoints) -> void
         | 
| 68 83 | 
             
                def execute_group_tasks(group, breakpoints)
         | 
| 69 84 | 
             
                  case strategy = group.options[:strategy]
         | 
| 70 85 | 
             
                  when NilClass, /sequential/ then execute_tasks_in_sequence(group, breakpoints)
         | 
| @@ -85,6 +100,8 @@ module CMDx | |
| 85 100 | 
             
                #
         | 
| 86 101 | 
             
                # @example
         | 
| 87 102 | 
             
                #   execute_tasks_in_sequence(group, ["failed", "skipped"])
         | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # @rbs (untyped group, Array[String] breakpoints) -> void
         | 
| 88 105 | 
             
                def execute_tasks_in_sequence(group, breakpoints)
         | 
| 89 106 | 
             
                  group.tasks.each do |task|
         | 
| 90 107 | 
             
                    task_result = task.execute(workflow.context)
         | 
| @@ -107,6 +124,8 @@ module CMDx | |
| 107 124 | 
             
                #
         | 
| 108 125 | 
             
                # @example
         | 
| 109 126 | 
             
                #   execute_tasks_in_parallel(group, ["failed"])
         | 
| 127 | 
            +
                #
         | 
| 128 | 
            +
                # @rbs (untyped group, Array[String] breakpoints) -> void
         | 
| 110 129 | 
             
                def execute_tasks_in_parallel(group, breakpoints)
         | 
| 111 130 | 
             
                  raise "install the `parallel` gem to use this feature" unless defined?(Parallel)
         | 
| 112 131 |  | 
    
        data/lib/cmdx/railtie.rb
    CHANGED
    
    | @@ -21,6 +21,8 @@ module CMDx | |
| 21 21 | 
             
                #   # This initializer runs automatically when Rails starts
         | 
| 22 22 | 
             
                #   # It will load locales like en.yml, es.yml, fr.yml if they exist
         | 
| 23 23 | 
             
                #   # in the CMDx gem's locales directory
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # @rbs (untyped app) -> void
         | 
| 24 26 | 
             
                initializer("cmdx.configure_locales") do |app|
         | 
| 25 27 | 
             
                  Array(app.config.i18n.available_locales).each do |locale|
         | 
| 26 28 | 
             
                    path = CMDx.gem_path.join("lib/locales/#{locale}.yml")
         | 
| @@ -35,6 +37,8 @@ module CMDx | |
| 35 37 | 
             
                # Configures the backtrace cleaner for CMDx in a Rails environment.
         | 
| 36 38 | 
             
                #
         | 
| 37 39 | 
             
                # Sets the backtrace cleaner to the Rails backtrace cleaner.
         | 
| 40 | 
            +
                #
         | 
| 41 | 
            +
                # @rbs () -> void
         | 
| 38 42 | 
             
                initializer("cmdx.backtrace_cleaner") do
         | 
| 39 43 | 
             
                  CMDx.configuration.backtrace_cleaner = lambda do |backtrace|
         | 
| 40 44 | 
             
                    Rails.backtrace_cleaner.clean(backtrace)
         |