cmdx 1.8.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/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +3 -3
- data/.cursor/prompts/llms.md +1 -3
- data/.cursor/prompts/yardoc.md +1 -0
- data/.irbrc +14 -2
- data/CHANGELOG.md +64 -45
- data/LLM.md +159 -53
- data/README.md +26 -83
- data/docs/.DS_Store +0 -0
- data/docs/assets/favicon.ico +0 -0
- data/docs/assets/favicon.svg +1 -0
- data/docs/attributes/coercions.md +12 -24
- data/docs/attributes/defaults.md +3 -16
- data/docs/attributes/definitions.md +16 -30
- data/docs/attributes/naming.md +3 -13
- data/docs/attributes/transformations.md +63 -0
- data/docs/attributes/validations.md +14 -33
- data/docs/basics/chain.md +14 -23
- data/docs/basics/context.md +13 -22
- data/docs/basics/execution.md +8 -26
- data/docs/basics/setup.md +8 -19
- data/docs/callbacks.md +19 -32
- data/docs/deprecation.md +8 -25
- data/docs/getting_started.md +109 -76
- data/docs/index.md +132 -0
- data/docs/internationalization.md +6 -18
- data/docs/interruptions/exceptions.md +10 -16
- data/docs/interruptions/faults.md +8 -25
- data/docs/interruptions/halt.md +12 -27
- data/docs/logging.md +7 -17
- data/docs/middlewares.md +13 -29
- data/docs/outcomes/result.md +21 -38
- data/docs/outcomes/states.md +8 -22
- data/docs/outcomes/statuses.md +10 -21
- data/docs/stylesheets/extra.css +42 -0
- data/docs/tips_and_tricks.md +7 -46
- data/docs/workflows.md +23 -38
- data/examples/active_record_query_tagging.md +46 -0
- data/examples/paper_trail_whatdunnit.md +39 -0
- data/lib/cmdx/attribute.rb +88 -6
- data/lib/cmdx/attribute_registry.rb +20 -0
- data/lib/cmdx/attribute_value.rb +56 -10
- data/lib/cmdx/callback_registry.rb +31 -2
- 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 +4 -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 +119 -3
- data/lib/cmdx/context.rb +36 -0
- data/lib/cmdx/deprecator.rb +6 -3
- data/lib/cmdx/errors.rb +22 -0
- data/lib/cmdx/executor.rb +136 -7
- 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 +24 -5
- data/lib/cmdx/railtie.rb +13 -0
- data/lib/cmdx/result.rb +133 -2
- data/lib/cmdx/task.rb +103 -8
- 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 +9 -0
- data/lib/generators/cmdx/locale_generator.rb +0 -1
- data/lib/generators/cmdx/templates/install.rb +9 -0
- data/mkdocs.yml +122 -0
- data/src/cmdx-dark-logo.png +0 -0
- data/src/cmdx-favicon.svg +1 -0
- data/src/cmdx-light-logo.png +0 -0
- data/src/cmdx-logo.svg +1 -0
- metadata +14 -3
- data/lib/cmdx/freezer.rb +0 -51
- data/src/cmdx-logo.png +0 -0
    
        data/lib/cmdx/result.rb
    CHANGED
    
    | @@ -11,18 +11,22 @@ module CMDx | |
| 11 11 |  | 
| 12 12 | 
             
                extend Forwardable
         | 
| 13 13 |  | 
| 14 | 
            +
                # @rbs STATES: Array[String]
         | 
| 14 15 | 
             
                STATES = [
         | 
| 15 16 | 
             
                  INITIALIZED = "initialized",  # Initial state before execution
         | 
| 16 17 | 
             
                  EXECUTING = "executing",      # Currently executing task logic
         | 
| 17 18 | 
             
                  COMPLETE = "complete",        # Successfully completed execution
         | 
| 18 19 | 
             
                  INTERRUPTED = "interrupted"   # Execution was halted due to failure
         | 
| 19 20 | 
             
                ].freeze
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # @rbs STATUSES: Array[String]
         | 
| 20 23 | 
             
                STATUSES = [
         | 
| 21 24 | 
             
                  SUCCESS = "success",  # Task completed successfully
         | 
| 22 25 | 
             
                  SKIPPED = "skipped",  # Task was skipped intentionally
         | 
| 23 26 | 
             
                  FAILED = "failed"     # Task failed due to error or validation
         | 
| 24 27 | 
             
                ].freeze
         | 
| 25 28 |  | 
| 29 | 
            +
                # @rbs STRIP_FAILURE: Proc
         | 
| 26 30 | 
             
                STRIP_FAILURE = proc do |hash, result, key|
         | 
| 27 31 | 
             
                  unless result.send(:"#{key}?")
         | 
| 28 32 | 
             
                    # Strip caused/threw failures since its the same info as the log line
         | 
| @@ -31,7 +35,65 @@ module CMDx | |
| 31 35 | 
             
                end.freeze
         | 
| 32 36 | 
             
                private_constant :STRIP_FAILURE
         | 
| 33 37 |  | 
| 34 | 
            -
                 | 
| 38 | 
            +
                # Returns the task instance associated with this result.
         | 
| 39 | 
            +
                #
         | 
| 40 | 
            +
                # @return [CMDx::Task] The task instance
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                # @example
         | 
| 43 | 
            +
                #   result.task.id # => "users/create"
         | 
| 44 | 
            +
                #
         | 
| 45 | 
            +
                # @rbs @task: Task
         | 
| 46 | 
            +
                attr_reader :task
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # Returns the current execution state of the result.
         | 
| 49 | 
            +
                #
         | 
| 50 | 
            +
                # @return [String] One of: "initialized", "executing", "complete", "interrupted"
         | 
| 51 | 
            +
                #
         | 
| 52 | 
            +
                # @example
         | 
| 53 | 
            +
                #   result.state # => "complete"
         | 
| 54 | 
            +
                #
         | 
| 55 | 
            +
                # @rbs @state: String
         | 
| 56 | 
            +
                attr_reader :state
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                # Returns the execution status of the result.
         | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # @return [String] One of: "success", "skipped", "failed"
         | 
| 61 | 
            +
                #
         | 
| 62 | 
            +
                # @example
         | 
| 63 | 
            +
                #   result.status # => "success"
         | 
| 64 | 
            +
                #
         | 
| 65 | 
            +
                # @rbs @status: String
         | 
| 66 | 
            +
                attr_reader :status
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                # Returns additional metadata about the result.
         | 
| 69 | 
            +
                #
         | 
| 70 | 
            +
                # @return [Hash{Symbol => Object}] Metadata hash
         | 
| 71 | 
            +
                #
         | 
| 72 | 
            +
                # @example
         | 
| 73 | 
            +
                #   result.metadata # => { duration: 1.5, retries: 2 }
         | 
| 74 | 
            +
                #
         | 
| 75 | 
            +
                # @rbs @metadata: Hash[Symbol, untyped]
         | 
| 76 | 
            +
                attr_reader :metadata
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                # Returns the reason for interruption (skip or failure).
         | 
| 79 | 
            +
                #
         | 
| 80 | 
            +
                # @return [String, nil] The reason message, or nil if not interrupted
         | 
| 81 | 
            +
                #
         | 
| 82 | 
            +
                # @example
         | 
| 83 | 
            +
                #   result.reason # => "Validation failed"
         | 
| 84 | 
            +
                #
         | 
| 85 | 
            +
                # @rbs @reason: (String | nil)
         | 
| 86 | 
            +
                attr_reader :reason
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                # Returns the exception that caused the interruption.
         | 
| 89 | 
            +
                #
         | 
| 90 | 
            +
                # @return [Exception, nil] The causing exception, or nil if not interrupted
         | 
| 91 | 
            +
                #
         | 
| 92 | 
            +
                # @example
         | 
| 93 | 
            +
                #   result.cause # => #<StandardError: Connection timeout>
         | 
| 94 | 
            +
                #
         | 
| 95 | 
            +
                # @rbs @cause: (Exception | nil)
         | 
| 96 | 
            +
                attr_reader :cause
         | 
| 35 97 |  | 
| 36 98 | 
             
                def_delegators :task, :context, :chain, :errors
         | 
| 37 99 | 
             
                alias ctx context
         | 
| @@ -45,6 +107,8 @@ module CMDx | |
| 45 107 | 
             
                # @example
         | 
| 46 108 | 
             
                #   result = CMDx::Result.new(my_task)
         | 
| 47 109 | 
             
                #   result.state # => "initialized"
         | 
| 110 | 
            +
                #
         | 
| 111 | 
            +
                # @rbs (Task) -> void
         | 
| 48 112 | 
             
                def initialize(task)
         | 
| 49 113 | 
             
                  raise TypeError, "must be a CMDx::Task" unless task.is_a?(CMDx::Task)
         | 
| 50 114 |  | 
| @@ -62,6 +126,8 @@ module CMDx | |
| 62 126 | 
             
                  # @example
         | 
| 63 127 | 
             
                  #   result.initialized? # => true
         | 
| 64 128 | 
             
                  #   result.executing?   # => false
         | 
| 129 | 
            +
                  #
         | 
| 130 | 
            +
                  # @rbs () -> bool
         | 
| 65 131 | 
             
                  define_method(:"#{s}?") { state == s }
         | 
| 66 132 |  | 
| 67 133 | 
             
                  # @param block [Proc] Block to execute conditionally
         | 
| @@ -75,6 +141,8 @@ module CMDx | |
| 75 141 | 
             
                  # @example
         | 
| 76 142 | 
             
                  #   result.handle_initialized { |r| puts "Starting execution" }
         | 
| 77 143 | 
             
                  #   result.handle_complete { |r| puts "Task completed" }
         | 
| 144 | 
            +
                  #
         | 
| 145 | 
            +
                  # @rbs () { (Result) -> void } -> self
         | 
| 78 146 | 
             
                  define_method(:"handle_#{s}") do |&block|
         | 
| 79 147 | 
             
                    raise ArgumentError, "block required" unless block
         | 
| 80 148 |  | 
| @@ -87,6 +155,8 @@ module CMDx | |
| 87 155 | 
             
                #
         | 
| 88 156 | 
             
                # @example
         | 
| 89 157 | 
             
                #   result.executed! # Transitions to complete or interrupted
         | 
| 158 | 
            +
                #
         | 
| 159 | 
            +
                # @rbs () -> self
         | 
| 90 160 | 
             
                def executed!
         | 
| 91 161 | 
             
                  success? ? complete! : interrupt!
         | 
| 92 162 | 
             
                end
         | 
| @@ -95,6 +165,8 @@ module CMDx | |
| 95 165 | 
             
                #
         | 
| 96 166 | 
             
                # @example
         | 
| 97 167 | 
             
                #   result.executed? # => true if complete? || interrupted?
         | 
| 168 | 
            +
                #
         | 
| 169 | 
            +
                # @rbs () -> bool
         | 
| 98 170 | 
             
                def executed?
         | 
| 99 171 | 
             
                  complete? || interrupted?
         | 
| 100 172 | 
             
                end
         | 
| @@ -109,6 +181,8 @@ module CMDx | |
| 109 181 | 
             
                #
         | 
| 110 182 | 
             
                # @example
         | 
| 111 183 | 
             
                #   result.handle_executed { |r| puts "Task finished: #{r.outcome}" }
         | 
| 184 | 
            +
                #
         | 
| 185 | 
            +
                # @rbs () { (Result) -> void } -> self
         | 
| 112 186 | 
             
                def handle_executed(&)
         | 
| 113 187 | 
             
                  raise ArgumentError, "block required" unless block_given?
         | 
| 114 188 |  | 
| @@ -120,6 +194,8 @@ module CMDx | |
| 120 194 | 
             
                #
         | 
| 121 195 | 
             
                # @example
         | 
| 122 196 | 
             
                #   result.executing! # Transitions from initialized to executing
         | 
| 197 | 
            +
                #
         | 
| 198 | 
            +
                # @rbs () -> void
         | 
| 123 199 | 
             
                def executing!
         | 
| 124 200 | 
             
                  return if executing?
         | 
| 125 201 |  | 
| @@ -132,6 +208,8 @@ module CMDx | |
| 132 208 | 
             
                #
         | 
| 133 209 | 
             
                # @example
         | 
| 134 210 | 
             
                #   result.complete! # Transitions from executing to complete
         | 
| 211 | 
            +
                #
         | 
| 212 | 
            +
                # @rbs () -> void
         | 
| 135 213 | 
             
                def complete!
         | 
| 136 214 | 
             
                  return if complete?
         | 
| 137 215 |  | 
| @@ -144,6 +222,8 @@ module CMDx | |
| 144 222 | 
             
                #
         | 
| 145 223 | 
             
                # @example
         | 
| 146 224 | 
             
                #   result.interrupt! # Transitions from executing to interrupted
         | 
| 225 | 
            +
                #
         | 
| 226 | 
            +
                # @rbs () -> void
         | 
| 147 227 | 
             
                def interrupt!
         | 
| 148 228 | 
             
                  return if interrupted?
         | 
| 149 229 |  | 
| @@ -158,6 +238,8 @@ module CMDx | |
| 158 238 | 
             
                  # @example
         | 
| 159 239 | 
             
                  #   result.success? # => true
         | 
| 160 240 | 
             
                  #   result.failed?  # => false
         | 
| 241 | 
            +
                  #
         | 
| 242 | 
            +
                  # @rbs () -> bool
         | 
| 161 243 | 
             
                  define_method(:"#{s}?") { status == s }
         | 
| 162 244 |  | 
| 163 245 | 
             
                  # @param block [Proc] Block to execute conditionally
         | 
| @@ -171,6 +253,8 @@ module CMDx | |
| 171 253 | 
             
                  # @example
         | 
| 172 254 | 
             
                  #   result.handle_success { |r| puts "Task succeeded" }
         | 
| 173 255 | 
             
                  #   result.handle_failed { |r| puts "Task failed: #{r.reason}" }
         | 
| 256 | 
            +
                  #
         | 
| 257 | 
            +
                  # @rbs () { (Result) -> void } -> self
         | 
| 174 258 | 
             
                  define_method(:"handle_#{s}") do |&block|
         | 
| 175 259 | 
             
                    raise ArgumentError, "block required" unless block
         | 
| 176 260 |  | 
| @@ -183,6 +267,8 @@ module CMDx | |
| 183 267 | 
             
                #
         | 
| 184 268 | 
             
                # @example
         | 
| 185 269 | 
             
                #   result.good? # => true if !failed?
         | 
| 270 | 
            +
                #
         | 
| 271 | 
            +
                # @rbs () -> bool
         | 
| 186 272 | 
             
                def good?
         | 
| 187 273 | 
             
                  !failed?
         | 
| 188 274 | 
             
                end
         | 
| @@ -198,6 +284,8 @@ module CMDx | |
| 198 284 | 
             
                #
         | 
| 199 285 | 
             
                # @example
         | 
| 200 286 | 
             
                #   result.handle_good { |r| puts "Task completed successfully" }
         | 
| 287 | 
            +
                #
         | 
| 288 | 
            +
                # @rbs () { (Result) -> void } -> self
         | 
| 201 289 | 
             
                def handle_good(&)
         | 
| 202 290 | 
             
                  raise ArgumentError, "block required" unless block_given?
         | 
| 203 291 |  | 
| @@ -209,6 +297,8 @@ module CMDx | |
| 209 297 | 
             
                #
         | 
| 210 298 | 
             
                # @example
         | 
| 211 299 | 
             
                #   result.bad? # => true if !success?
         | 
| 300 | 
            +
                #
         | 
| 301 | 
            +
                # @rbs () -> bool
         | 
| 212 302 | 
             
                def bad?
         | 
| 213 303 | 
             
                  !success?
         | 
| 214 304 | 
             
                end
         | 
| @@ -223,6 +313,8 @@ module CMDx | |
| 223 313 | 
             
                #
         | 
| 224 314 | 
             
                # @example
         | 
| 225 315 | 
             
                #   result.handle_bad { |r| puts "Task had issues: #{r.reason}" }
         | 
| 316 | 
            +
                #
         | 
| 317 | 
            +
                # @rbs () { (Result) -> void } -> self
         | 
| 226 318 | 
             
                def handle_bad(&)
         | 
| 227 319 | 
             
                  raise ArgumentError, "block required" unless block_given?
         | 
| 228 320 |  | 
| @@ -240,6 +332,8 @@ module CMDx | |
| 240 332 | 
             
                # @example
         | 
| 241 333 | 
             
                #   result.skip!("Dependencies not met", cause: dependency_error)
         | 
| 242 334 | 
             
                #   result.skip!("Already processed", halt: false)
         | 
| 335 | 
            +
                #
         | 
| 336 | 
            +
                # @rbs (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void
         | 
| 243 337 | 
             
                def skip!(reason = nil, halt: true, cause: nil, **metadata)
         | 
| 244 338 | 
             
                  return if skipped?
         | 
| 245 339 |  | 
| @@ -264,6 +358,8 @@ module CMDx | |
| 264 358 | 
             
                # @example
         | 
| 265 359 | 
             
                #   result.fail!("Validation failed", cause: validation_error)
         | 
| 266 360 | 
             
                #   result.fail!("Network timeout", halt: false, timeout: 30)
         | 
| 361 | 
            +
                #
         | 
| 362 | 
            +
                # @rbs (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void
         | 
| 267 363 | 
             
                def fail!(reason = nil, halt: true, cause: nil, **metadata)
         | 
| 268 364 | 
             
                  return if failed?
         | 
| 269 365 |  | 
| @@ -283,6 +379,8 @@ module CMDx | |
| 283 379 | 
             
                #
         | 
| 284 380 | 
             
                # @example
         | 
| 285 381 | 
             
                #   result.halt! # Raises appropriate fault based on status
         | 
| 382 | 
            +
                #
         | 
| 383 | 
            +
                # @rbs () -> void
         | 
| 286 384 | 
             
                def halt!
         | 
| 287 385 | 
             
                  return if success?
         | 
| 288 386 |  | 
| @@ -291,7 +389,16 @@ module CMDx | |
| 291 389 |  | 
| 292 390 | 
             
                  # Strip the first two frames (this method and the delegator)
         | 
| 293 391 | 
             
                  frames = caller_locations(3..-1)
         | 
| 294 | 
            -
             | 
| 392 | 
            +
             | 
| 393 | 
            +
                  unless frames.empty?
         | 
| 394 | 
            +
                    frames = frames.map(&:to_s)
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                    if (cleaner = task.class.settings[:backtrace_cleaner])
         | 
| 397 | 
            +
                      cleaner.call(frames)
         | 
| 398 | 
            +
                    end
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                    fault.set_backtrace(frames)
         | 
| 401 | 
            +
                  end
         | 
| 295 402 |  | 
| 296 403 | 
             
                  raise(fault)
         | 
| 297 404 | 
             
                end
         | 
| @@ -306,6 +413,8 @@ module CMDx | |
| 306 413 | 
             
                # @example
         | 
| 307 414 | 
             
                #   other_result = OtherTask.execute
         | 
| 308 415 | 
             
                #   result.throw!(other_result, cause: upstream_error)
         | 
| 416 | 
            +
                #
         | 
| 417 | 
            +
                # @rbs (Result result, halt: bool, cause: Exception?, **untyped metadata) -> void
         | 
| 309 418 | 
             
                def throw!(result, halt: true, cause: nil, **metadata)
         | 
| 310 419 | 
             
                  raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)
         | 
| 311 420 |  | 
| @@ -323,6 +432,8 @@ module CMDx | |
| 323 432 | 
             
                # @example
         | 
| 324 433 | 
             
                #   cause = result.caused_failure
         | 
| 325 434 | 
             
                #   puts "Caused by: #{cause.task.id}" if cause
         | 
| 435 | 
            +
                #
         | 
| 436 | 
            +
                # @rbs () -> Result?
         | 
| 326 437 | 
             
                def caused_failure
         | 
| 327 438 | 
             
                  return unless failed?
         | 
| 328 439 |  | 
| @@ -335,6 +446,8 @@ module CMDx | |
| 335 446 | 
             
                #   if result.caused_failure?
         | 
| 336 447 | 
             
                #     puts "This task caused the failure"
         | 
| 337 448 | 
             
                #   end
         | 
| 449 | 
            +
                #
         | 
| 450 | 
            +
                # @rbs () -> bool
         | 
| 338 451 | 
             
                def caused_failure?
         | 
| 339 452 | 
             
                  return false unless failed?
         | 
| 340 453 |  | 
| @@ -346,6 +459,8 @@ module CMDx | |
| 346 459 | 
             
                # @example
         | 
| 347 460 | 
             
                #   thrown = result.threw_failure
         | 
| 348 461 | 
             
                #   puts "Thrown by: #{thrown.task.id}" if thrown
         | 
| 462 | 
            +
                #
         | 
| 463 | 
            +
                # @rbs () -> Result?
         | 
| 349 464 | 
             
                def threw_failure
         | 
| 350 465 | 
             
                  return unless failed?
         | 
| 351 466 |  | 
| @@ -360,6 +475,8 @@ module CMDx | |
| 360 475 | 
             
                #   if result.threw_failure?
         | 
| 361 476 | 
             
                #     puts "This task threw the failure"
         | 
| 362 477 | 
             
                #   end
         | 
| 478 | 
            +
                #
         | 
| 479 | 
            +
                # @rbs () -> bool
         | 
| 363 480 | 
             
                def threw_failure?
         | 
| 364 481 | 
             
                  return false unless failed?
         | 
| 365 482 |  | 
| @@ -372,6 +489,8 @@ module CMDx | |
| 372 489 | 
             
                #   if result.thrown_failure?
         | 
| 373 490 | 
             
                #     puts "This failure was thrown from another task"
         | 
| 374 491 | 
             
                #   end
         | 
| 492 | 
            +
                #
         | 
| 493 | 
            +
                # @rbs () -> bool
         | 
| 375 494 | 
             
                def thrown_failure?
         | 
| 376 495 | 
             
                  failed? && !caused_failure?
         | 
| 377 496 | 
             
                end
         | 
| @@ -381,6 +500,8 @@ module CMDx | |
| 381 500 | 
             
                # @example
         | 
| 382 501 | 
             
                #   position = result.index
         | 
| 383 502 | 
             
                #   puts "Task #{position + 1} of #{chain.results.count}"
         | 
| 503 | 
            +
                #
         | 
| 504 | 
            +
                # @rbs () -> Integer
         | 
| 384 505 | 
             
                def index
         | 
| 385 506 | 
             
                  chain.index(self)
         | 
| 386 507 | 
             
                end
         | 
| @@ -389,6 +510,8 @@ module CMDx | |
| 389 510 | 
             
                #
         | 
| 390 511 | 
             
                # @example
         | 
| 391 512 | 
             
                #   result.outcome # => "success" or "interrupted"
         | 
| 513 | 
            +
                #
         | 
| 514 | 
            +
                # @rbs () -> String
         | 
| 392 515 | 
             
                def outcome
         | 
| 393 516 | 
             
                  initialized? || thrown_failure? ? state : status
         | 
| 394 517 | 
             
                end
         | 
| @@ -398,6 +521,8 @@ module CMDx | |
| 398 521 | 
             
                # @example
         | 
| 399 522 | 
             
                #   result.to_h
         | 
| 400 523 | 
             
                #   # => {state: "complete", status: "success", outcome: "success", metadata: {}}
         | 
| 524 | 
            +
                #
         | 
| 525 | 
            +
                # @rbs () -> Hash[Symbol, untyped]
         | 
| 401 526 | 
             
                def to_h
         | 
| 402 527 | 
             
                  task.to_h.merge!(
         | 
| 403 528 | 
             
                    state:,
         | 
| @@ -421,6 +546,8 @@ module CMDx | |
| 421 546 | 
             
                #
         | 
| 422 547 | 
             
                # @example
         | 
| 423 548 | 
             
                #   result.to_s # => "task_id=my_task state=complete status=success"
         | 
| 549 | 
            +
                #
         | 
| 550 | 
            +
                # @rbs () -> String
         | 
| 424 551 | 
             
                def to_s
         | 
| 425 552 | 
             
                  Utils::Format.to_str(to_h) do |key, value|
         | 
| 426 553 | 
             
                    case key
         | 
| @@ -437,6 +564,8 @@ module CMDx | |
| 437 564 | 
             
                # @example
         | 
| 438 565 | 
             
                #   state, status = result.deconstruct
         | 
| 439 566 | 
             
                #   puts "State: #{state}, Status: #{status}"
         | 
| 567 | 
            +
                #
         | 
| 568 | 
            +
                # @rbs (*untyped) -> Array[untyped]
         | 
| 440 569 | 
             
                def deconstruct(*)
         | 
| 441 570 | 
             
                  [state, status, reason, cause, metadata]
         | 
| 442 571 | 
             
                end
         | 
| @@ -452,6 +581,8 @@ module CMDx | |
| 452 581 | 
             
                #   in {bad: true}
         | 
| 453 582 | 
             
                #     puts "Task had issues"
         | 
| 454 583 | 
             
                #   end
         | 
| 584 | 
            +
                #
         | 
| 585 | 
            +
                # @rbs (*untyped) -> Hash[Symbol, untyped]
         | 
| 455 586 | 
             
                def deconstruct_keys(*)
         | 
| 456 587 | 
             
                  {
         | 
| 457 588 | 
             
                    state: state,
         | 
    
        data/lib/cmdx/task.rb
    CHANGED
    
    | @@ -8,10 +8,68 @@ module CMDx | |
| 8 8 |  | 
| 9 9 | 
             
                extend Forwardable
         | 
| 10 10 |  | 
| 11 | 
            -
                 | 
| 11 | 
            +
                # Returns the hash of processed attribute values for this task.
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # @return [Hash{Symbol => Object}] Hash of attribute names to their values
         | 
| 14 | 
            +
                #
         | 
| 15 | 
            +
                # @example
         | 
| 16 | 
            +
                #   task.attributes # => { user_id: 42, user_name: "John" }
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                # @rbs @attributes: Hash[Symbol, untyped]
         | 
| 19 | 
            +
                attr_reader :attributes
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # Returns the collection of validation and execution errors.
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                # @return [Errors] The errors collection
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # @example
         | 
| 26 | 
            +
                #   task.errors.to_h # => { email: ["must be valid"] }
         | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # @rbs @errors: Errors
         | 
| 29 | 
            +
                attr_reader :errors
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                # Returns the unique identifier for this task instance.
         | 
| 32 | 
            +
                #
         | 
| 33 | 
            +
                # @return [String] The task identifier
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # @example
         | 
| 36 | 
            +
                #   task.id # => "abc123xyz"
         | 
| 37 | 
            +
                #
         | 
| 38 | 
            +
                # @rbs @id: String
         | 
| 39 | 
            +
                attr_reader :id
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                # Returns the execution context for this task.
         | 
| 42 | 
            +
                #
         | 
| 43 | 
            +
                # @return [Context] The context instance
         | 
| 44 | 
            +
                #
         | 
| 45 | 
            +
                # @example
         | 
| 46 | 
            +
                #   task.context[:user_id] # => 42
         | 
| 47 | 
            +
                #
         | 
| 48 | 
            +
                # @rbs @context: Context
         | 
| 49 | 
            +
                attr_reader :context
         | 
| 12 50 | 
             
                alias ctx context
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                # Returns the execution result for this task.
         | 
| 53 | 
            +
                #
         | 
| 54 | 
            +
                # @return [Result] The result instance
         | 
| 55 | 
            +
                #
         | 
| 56 | 
            +
                # @example
         | 
| 57 | 
            +
                #   task.result.status # => "success"
         | 
| 58 | 
            +
                #
         | 
| 59 | 
            +
                # @rbs @result: Result
         | 
| 60 | 
            +
                attr_reader :result
         | 
| 13 61 | 
             
                alias res result
         | 
| 14 62 |  | 
| 63 | 
            +
                # Returns the execution chain containing all task results.
         | 
| 64 | 
            +
                #
         | 
| 65 | 
            +
                # @return [Chain] The chain instance
         | 
| 66 | 
            +
                #
         | 
| 67 | 
            +
                # @example
         | 
| 68 | 
            +
                #   task.chain.results.size # => 3
         | 
| 69 | 
            +
                #
         | 
| 70 | 
            +
                # @rbs @chain: Chain
         | 
| 71 | 
            +
                attr_reader :chain
         | 
| 72 | 
            +
             | 
| 15 73 | 
             
                def_delegators :result, :skip!, :fail!, :throw!
         | 
| 16 74 |  | 
| 17 75 | 
             
                # @param context [Hash, Context] The initial context for the task
         | 
| @@ -25,6 +83,8 @@ module CMDx | |
| 25 83 | 
             
                # @example
         | 
| 26 84 | 
             
                #   task = MyTask.new(name: "example", priority: :high)
         | 
| 27 85 | 
             
                #   task = MyTask.new(Context.build(name: "example"))
         | 
| 86 | 
            +
                #
         | 
| 87 | 
            +
                # @rbs (untyped context) -> void
         | 
| 28 88 | 
             
                def initialize(context = {})
         | 
| 29 89 | 
             
                  Deprecator.restrict(self)
         | 
| 30 90 |  | 
| @@ -40,9 +100,6 @@ module CMDx | |
| 40 100 | 
             
                class << self
         | 
| 41 101 |  | 
| 42 102 | 
             
                  # @param options [Hash] Configuration options to merge with existing settings
         | 
| 43 | 
            -
                  # @option options [AttributeRegistry] :attributes Registry for task attributes
         | 
| 44 | 
            -
                  # @option options [Boolean] :deprecate Whether the task is deprecated
         | 
| 45 | 
            -
                  # @option options [Array<Symbol>] :tags Tags associated with the task
         | 
| 46 103 | 
             
                  #
         | 
| 47 104 | 
             
                  # @return [Hash] The merged settings hash
         | 
| 48 105 | 
             
                  #
         | 
| @@ -50,17 +107,27 @@ module CMDx | |
| 50 107 | 
             
                  #   class MyTask < Task
         | 
| 51 108 | 
             
                  #     settings deprecate: true, tags: [:experimental]
         | 
| 52 109 | 
             
                  #   end
         | 
| 110 | 
            +
                  #
         | 
| 111 | 
            +
                  # @rbs (**untyped options) -> Hash[Symbol, untyped]
         | 
| 53 112 | 
             
                  def settings(**options)
         | 
| 54 113 | 
             
                    @settings ||= begin
         | 
| 55 114 | 
             
                      hash =
         | 
| 56 115 | 
             
                        if superclass.respond_to?(:settings)
         | 
| 57 | 
            -
                          superclass.settings
         | 
| 116 | 
            +
                          parent = superclass.settings
         | 
| 117 | 
            +
                          parent
         | 
| 118 | 
            +
                            .except(:backtrace_cleaner, :exception_handler, :logger, :deprecate)
         | 
| 119 | 
            +
                            .transform_values!(&:dup)
         | 
| 120 | 
            +
                            .merge!(
         | 
| 121 | 
            +
                              backtrace_cleaner: parent[:backtrace_cleaner] || CMDx.configuration.backtrace_cleaner,
         | 
| 122 | 
            +
                              exception_handler: parent[:exception_handler] || CMDx.configuration.exception_handler,
         | 
| 123 | 
            +
                              logger: parent[:logger] || CMDx.configuration.logger,
         | 
| 124 | 
            +
                              deprecate: parent[:deprecate]
         | 
| 125 | 
            +
                            )
         | 
| 58 126 | 
             
                        else
         | 
| 59 | 
            -
                          CMDx.configuration.to_h | 
| 60 | 
            -
                        end | 
| 127 | 
            +
                          CMDx.configuration.to_h
         | 
| 128 | 
            +
                        end
         | 
| 61 129 |  | 
| 62 130 | 
             
                      hash[:attributes] ||= AttributeRegistry.new
         | 
| 63 | 
            -
                      hash[:deprecate] ||= false
         | 
| 64 131 | 
             
                      hash[:tags] ||= []
         | 
| 65 132 |  | 
| 66 133 | 
             
                      hash.merge!(options)
         | 
| @@ -76,6 +143,8 @@ module CMDx | |
| 76 143 | 
             
                  # @example
         | 
| 77 144 | 
             
                  #   register(:attribute, MyAttribute.new)
         | 
| 78 145 | 
             
                  #   register(:callback, :before, -> { puts "before" })
         | 
| 146 | 
            +
                  #
         | 
| 147 | 
            +
                  # @rbs (Symbol type, untyped object, *untyped) -> void
         | 
| 79 148 | 
             
                  def register(type, object, ...)
         | 
| 80 149 | 
             
                    case type
         | 
| 81 150 | 
             
                    when :attribute then settings[:attributes].register(object, ...)
         | 
| @@ -96,6 +165,8 @@ module CMDx | |
| 96 165 | 
             
                  # @example
         | 
| 97 166 | 
             
                  #   deregister(:attribute, :name)
         | 
| 98 167 | 
             
                  #   deregister(:callback, :before, MyCallback)
         | 
| 168 | 
            +
                  #
         | 
| 169 | 
            +
                  # @rbs (Symbol type, untyped object, *untyped) -> void
         | 
| 99 170 | 
             
                  def deregister(type, object, ...)
         | 
| 100 171 | 
             
                    case type
         | 
| 101 172 | 
             
                    when :attribute then settings[:attributes].deregister(object, ...)
         | 
| @@ -112,6 +183,8 @@ module CMDx | |
| 112 183 | 
             
                  # @example
         | 
| 113 184 | 
             
                  #   attributes :name, :email
         | 
| 114 185 | 
             
                  #   attributes :age, type: Integer, default: 18
         | 
| 186 | 
            +
                  #
         | 
| 187 | 
            +
                  # @rbs (*untyped) -> void
         | 
| 115 188 | 
             
                  def attributes(...)
         | 
| 116 189 | 
             
                    register(:attribute, Attribute.build(...))
         | 
| 117 190 | 
             
                  end
         | 
| @@ -122,6 +195,8 @@ module CMDx | |
| 122 195 | 
             
                  # @example
         | 
| 123 196 | 
             
                  #   optional :description, :notes
         | 
| 124 197 | 
             
                  #   optional :priority, type: Symbol, default: :normal
         | 
| 198 | 
            +
                  #
         | 
| 199 | 
            +
                  # @rbs (*untyped) -> void
         | 
| 125 200 | 
             
                  def optional(...)
         | 
| 126 201 | 
             
                    register(:attribute, Attribute.optional(...))
         | 
| 127 202 | 
             
                  end
         | 
| @@ -131,6 +206,8 @@ module CMDx | |
| 131 206 | 
             
                  # @example
         | 
| 132 207 | 
             
                  #   required :name, :email
         | 
| 133 208 | 
             
                  #   required :age, type: Integer, min: 0
         | 
| 209 | 
            +
                  #
         | 
| 210 | 
            +
                  # @rbs (*untyped) -> void
         | 
| 134 211 | 
             
                  def required(...)
         | 
| 135 212 | 
             
                    register(:attribute, Attribute.required(...))
         | 
| 136 213 | 
             
                  end
         | 
| @@ -139,6 +216,8 @@ module CMDx | |
| 139 216 | 
             
                  #
         | 
| 140 217 | 
             
                  # @example
         | 
| 141 218 | 
             
                  #   remove_attributes :old_field, :deprecated_field
         | 
| 219 | 
            +
                  #
         | 
| 220 | 
            +
                  # @rbs (*Symbol names) -> void
         | 
| 142 221 | 
             
                  def remove_attributes(*names)
         | 
| 143 222 | 
             
                    deregister(:attribute, names)
         | 
| 144 223 | 
             
                  end
         | 
| @@ -155,6 +234,8 @@ module CMDx | |
| 155 234 | 
             
                    #   before { puts "before execution" }
         | 
| 156 235 | 
             
                    #   after :cleanup, priority: :high
         | 
| 157 236 | 
             
                    #   around ->(task) { task.logger.info("starting") }
         | 
| 237 | 
            +
                    #
         | 
| 238 | 
            +
                    # @rbs (*untyped callables, **untyped options) ?{ () -> void } -> void
         | 
| 158 239 | 
             
                    define_method(callback) do |*callables, **options, &block|
         | 
| 159 240 | 
             
                      register(:callback, callback, *callables, **options, &block)
         | 
| 160 241 | 
             
                    end
         | 
| @@ -169,6 +250,8 @@ module CMDx | |
| 169 250 | 
             
                  #   if result.success?
         | 
| 170 251 | 
             
                  #     puts "Task completed successfully"
         | 
| 171 252 | 
             
                  #   end
         | 
| 253 | 
            +
                  #
         | 
| 254 | 
            +
                  # @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
         | 
| 172 255 | 
             
                  def execute(*args, **kwargs)
         | 
| 173 256 | 
             
                    task = new(*args, **kwargs)
         | 
| 174 257 | 
             
                    task.execute(raise: false)
         | 
| @@ -184,6 +267,8 @@ module CMDx | |
| 184 267 | 
             
                  # @example
         | 
| 185 268 | 
             
                  #   result = MyTask.execute!(name: "example")
         | 
| 186 269 | 
             
                  #   # Will raise an exception if execution fails
         | 
| 270 | 
            +
                  #
         | 
| 271 | 
            +
                  # @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
         | 
| 187 272 | 
             
                  def execute!(*args, **kwargs)
         | 
| 188 273 | 
             
                    task = new(*args, **kwargs)
         | 
| 189 274 | 
             
                    task.execute(raise: true)
         | 
| @@ -199,6 +284,8 @@ module CMDx | |
| 199 284 | 
             
                # @example
         | 
| 200 285 | 
             
                #   result = task.execute
         | 
| 201 286 | 
             
                #   result = task.execute(raise: true)
         | 
| 287 | 
            +
                #
         | 
| 288 | 
            +
                # @rbs (raise: bool) ?{ (Result) -> void } -> Result
         | 
| 202 289 | 
             
                def execute(raise: false)
         | 
| 203 290 | 
             
                  Executor.execute(self, raise:)
         | 
| 204 291 | 
             
                  block_given? ? yield(result) : result
         | 
| @@ -213,6 +300,8 @@ module CMDx | |
| 213 300 | 
             
                #       puts "Performing work..."
         | 
| 214 301 | 
             
                #     end
         | 
| 215 302 | 
             
                #   end
         | 
| 303 | 
            +
                #
         | 
| 304 | 
            +
                # @rbs () -> void
         | 
| 216 305 | 
             
                def work
         | 
| 217 306 | 
             
                  raise UndefinedMethodError, "undefined method #{self.class.name}#work"
         | 
| 218 307 | 
             
                end
         | 
| @@ -222,6 +311,8 @@ module CMDx | |
| 222 311 | 
             
                # @example
         | 
| 223 312 | 
             
                #   logger.info "Starting task execution"
         | 
| 224 313 | 
             
                #   logger.error "Task failed", error: exception
         | 
| 314 | 
            +
                #
         | 
| 315 | 
            +
                # @rbs () -> Logger
         | 
| 225 316 | 
             
                def logger
         | 
| 226 317 | 
             
                  @logger ||= begin
         | 
| 227 318 | 
             
                    logger = self.class.settings[:logger] || CMDx.configuration.logger
         | 
| @@ -244,6 +335,8 @@ module CMDx | |
| 244 335 | 
             
                #   task_hash = task.to_h
         | 
| 245 336 | 
             
                #   puts "Task type: #{task_hash[:type]}"
         | 
| 246 337 | 
             
                #   puts "Task tags: #{task_hash[:tags].join(', ')}"
         | 
| 338 | 
            +
                #
         | 
| 339 | 
            +
                # @rbs () -> Hash[Symbol, untyped]
         | 
| 247 340 | 
             
                def to_h
         | 
| 248 341 | 
             
                  {
         | 
| 249 342 | 
             
                    index: result.index,
         | 
| @@ -260,6 +353,8 @@ module CMDx | |
| 260 353 | 
             
                # @example
         | 
| 261 354 | 
             
                #   puts task.to_s
         | 
| 262 355 | 
             
                #   # Output: "Task[MyTask] tags: [:important] id: abc123"
         | 
| 356 | 
            +
                #
         | 
| 357 | 
            +
                # @rbs () -> String
         | 
| 263 358 | 
             
                def to_s
         | 
| 264 359 | 
             
                  Utils::Format.to_str(to_h)
         | 
| 265 360 | 
             
                end
         | 
    
        data/lib/cmdx/utils/call.rb
    CHANGED
    
    | @@ -32,6 +32,8 @@ module CMDx | |
| 32 32 | 
             
                  # @example Invoking a callable object
         | 
| 33 33 | 
             
                  #   callable = MyCallable.new
         | 
| 34 34 | 
             
                  #   Call.invoke(user, callable, 'data')
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  # @rbs (untyped target, (Symbol | Proc | untyped) callable, *untyped args, **untyped kwargs) ?{ () -> untyped } -> untyped
         | 
| 35 37 | 
             
                  def invoke(target, callable, *args, **kwargs, &)
         | 
| 36 38 | 
             
                    if callable.is_a?(Symbol)
         | 
| 37 39 | 
             
                      target.send(callable, *args, **kwargs, &)
         | 
    
        data/lib/cmdx/utils/condition.rb
    CHANGED
    
    | @@ -11,6 +11,7 @@ module CMDx | |
| 11 11 |  | 
| 12 12 | 
             
                  extend self
         | 
| 13 13 |  | 
| 14 | 
            +
                  # @rbs EVAL: Proc
         | 
| 14 15 | 
             
                  EVAL = proc do |target, callable, *args, **kwargs, &block|
         | 
| 15 16 | 
             
                    case callable
         | 
| 16 17 | 
             
                    when NilClass, FalseClass, TrueClass then !!callable
         | 
| @@ -53,6 +54,8 @@ module CMDx | |
| 53 54 | 
             
                  # @example With arguments and block
         | 
| 54 55 | 
             
                  #   Condition.evaluate(user, if: ->(u) { u.has_permission?(:admin) }, :admin)
         | 
| 55 56 | 
             
                  #   # => true if the proc returns true when called with user and :admin
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  # @rbs (untyped target, Hash[Symbol, untyped] options, *untyped) ?{ () -> untyped } -> bool
         | 
| 56 59 | 
             
                  def evaluate(target, options, ...)
         | 
| 57 60 | 
             
                    case options
         | 
| 58 61 | 
             
                    in if: if_cond, unless: unless_cond
         | 
    
        data/lib/cmdx/utils/format.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ module CMDx | |
| 8 8 |  | 
| 9 9 | 
             
                  extend self
         | 
| 10 10 |  | 
| 11 | 
            +
                  # @rbs FORMATTER: Proc
         | 
| 11 12 | 
             
                  FORMATTER = proc do |key, value|
         | 
| 12 13 | 
             
                    "#{key}=#{value.inspect}"
         | 
| 13 14 | 
             
                  end.freeze
         | 
| @@ -28,6 +29,8 @@ module CMDx | |
| 28 29 | 
             
                  # @example CMDx object
         | 
| 29 30 | 
             
                  #   Format.to_log(CMDx::Task.new(name: "task1"))
         | 
| 30 31 | 
             
                  #   # => {name: "task1"}
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  # @rbs (untyped message) -> untyped
         | 
| 31 34 | 
             
                  def to_log(message)
         | 
| 32 35 | 
             
                    if message.respond_to?(:to_h) && message.class.ancestors.any? { |a| a.to_s.start_with?("CMDx") }
         | 
| 33 36 | 
             
                      message.to_h
         | 
| @@ -51,6 +54,8 @@ module CMDx | |
| 51 54 | 
             
                  # @example Custom formatter
         | 
| 52 55 | 
             
                  #   Format.to_str({count: 5, total: 100}) { |k, v| "#{k}:#{v}" }
         | 
| 53 56 | 
             
                  #   # => "count:5 total:100"
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  # @rbs (Hash[untyped, untyped] hash) ?{ (untyped, untyped) -> String } -> String
         | 
| 54 59 | 
             
                  def to_str(hash, &block)
         | 
| 55 60 | 
             
                    block ||= FORMATTER
         | 
| 56 61 | 
             
                    hash.map(&block).join(" ")
         |