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
| @@ -7,6 +7,14 @@ module CMDx | |
| 7 7 |  | 
| 8 8 | 
             
                extend Forwardable
         | 
| 9 9 |  | 
| 10 | 
            +
                # Returns the internal registry mapping validator types to classes.
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # @return [Hash{Symbol => Class}] Hash of validator type names to validator classes
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # @example
         | 
| 15 | 
            +
                #   registry.registry # => { presence: Validators::Presence, format: Validators::Format }
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @rbs @registry: Hash[Symbol, Class]
         | 
| 10 18 | 
             
                attr_reader :registry
         | 
| 11 19 | 
             
                alias to_h registry
         | 
| 12 20 |  | 
| @@ -17,6 +25,8 @@ module CMDx | |
| 17 25 | 
             
                # @param registry [Hash, nil] Optional hash mapping validator names to validator classes
         | 
| 18 26 | 
             
                #
         | 
| 19 27 | 
             
                # @return [ValidatorRegistry] A new validator registry instance
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                # @rbs (?Hash[Symbol, Class]? registry) -> void
         | 
| 20 30 | 
             
                def initialize(registry = nil)
         | 
| 21 31 | 
             
                  @registry = registry || {
         | 
| 22 32 | 
             
                    exclusion: Validators::Exclusion,
         | 
| @@ -31,6 +41,8 @@ module CMDx | |
| 31 41 | 
             
                # Create a duplicate of the registry with copied internal state.
         | 
| 32 42 | 
             
                #
         | 
| 33 43 | 
             
                # @return [ValidatorRegistry] A new validator registry with duplicated registry hash
         | 
| 44 | 
            +
                #
         | 
| 45 | 
            +
                # @rbs () -> ValidatorRegistry
         | 
| 34 46 | 
             
                def dup
         | 
| 35 47 | 
             
                  self.class.new(registry.dup)
         | 
| 36 48 | 
             
                end
         | 
| @@ -45,6 +57,8 @@ module CMDx | |
| 45 57 | 
             
                # @example
         | 
| 46 58 | 
             
                #   registry.register(:custom, CustomValidator)
         | 
| 47 59 | 
             
                #   registry.register("email", EmailValidator)
         | 
| 60 | 
            +
                #
         | 
| 61 | 
            +
                # @rbs ((String | Symbol) name, Class validator) -> self
         | 
| 48 62 | 
             
                def register(name, validator)
         | 
| 49 63 | 
             
                  registry[name.to_sym] = validator
         | 
| 50 64 | 
             
                  self
         | 
| @@ -59,6 +73,8 @@ module CMDx | |
| 59 73 | 
             
                # @example
         | 
| 60 74 | 
             
                #   registry.deregister(:format)
         | 
| 61 75 | 
             
                #   registry.deregister("presence")
         | 
| 76 | 
            +
                #
         | 
| 77 | 
            +
                # @rbs ((String | Symbol) name) -> self
         | 
| 62 78 | 
             
                def deregister(name)
         | 
| 63 79 | 
             
                  registry.delete(name.to_sym)
         | 
| 64 80 | 
             
                  self
         | 
| @@ -77,6 +93,8 @@ module CMDx | |
| 77 93 | 
             
                # @example
         | 
| 78 94 | 
             
                #   registry.validate(:presence, task, user.name, presence: true)
         | 
| 79 95 | 
             
                #   registry.validate(:length, task, password, { min: 8, allow_nil: false })
         | 
| 96 | 
            +
                #
         | 
| 97 | 
            +
                # @rbs (Symbol type, Task task, untyped value, untyped options) -> untyped
         | 
| 80 98 | 
             
                def validate(type, task, value, options = {})
         | 
| 81 99 | 
             
                  raise TypeError, "unknown validator type #{type.inspect}" unless registry.key?(type)
         | 
| 82 100 |  | 
| @@ -32,6 +32,8 @@ module CMDx | |
| 32 32 | 
             
                  #   # => raises ValidationError if value is 5 (within 1..10)
         | 
| 33 33 | 
             
                  # @example Exclude with custom message
         | 
| 34 34 | 
             
                  #   Exclusion.call("test", in: ["test", "demo"], message: "value %{values} is forbidden")
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  # @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
         | 
| 35 37 | 
             
                  def call(value, options = {})
         | 
| 36 38 | 
             
                    values = options[:in] || options[:within]
         | 
| 37 39 |  | 
| @@ -38,6 +38,8 @@ module CMDx | |
| 38 38 | 
             
                  # @example Validate with custom message
         | 
| 39 39 | 
             
                  #   Format.call("invalid", with: /\A\d+\z/, message: "Must contain only digits")
         | 
| 40 40 | 
             
                  #   # => raises ValidationError with custom message
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # @rbs (untyped value, (Hash[Symbol, untyped] | Regexp) options) -> nil
         | 
| 41 43 | 
             
                  def call(value, options = {})
         | 
| 42 44 | 
             
                    match =
         | 
| 43 45 | 
             
                      if options.is_a?(Regexp)
         | 
| @@ -34,6 +34,8 @@ module CMDx | |
| 34 34 | 
             
                  #   # => nil (validation passes - 5 is within 1..10)
         | 
| 35 35 | 
             
                  # @example Include with custom message
         | 
| 36 36 | 
             
                  #   Inclusion.call("test", in: ["admin", "user"], message: "must be one of: %{values}")
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  # @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
         | 
| 37 39 | 
             
                  def call(value, options = {})
         | 
| 38 40 | 
             
                    values = options[:in] || options[:within]
         | 
| 39 41 |  | 
| @@ -51,6 +51,8 @@ module CMDx | |
| 51 51 | 
             
                  # @example Exclusion validation
         | 
| 52 52 | 
             
                  #   Length.call("short", not_in: 1..3)
         | 
| 53 53 | 
             
                  #   # => nil (validation passes - length 5 is not in excluded range)
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
         | 
| 54 56 | 
             
                  def call(value, options = {})
         | 
| 55 57 | 
             
                    length = value&.length
         | 
| 56 58 |  | 
| @@ -87,6 +89,8 @@ module CMDx | |
| 87 89 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 88 90 | 
             
                  #
         | 
| 89 91 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 92 | 
            +
                  #
         | 
| 93 | 
            +
                  # @rbs (Integer min, Integer max, Hash[Symbol, untyped] options) -> void
         | 
| 90 94 | 
             
                  def raise_within_validation_error!(min, max, options)
         | 
| 91 95 | 
             
                    message = options[:within_message] || options[:in_message] || options[:message]
         | 
| 92 96 | 
             
                    message %= { min:, max: } unless message.nil?
         | 
| @@ -101,6 +105,8 @@ module CMDx | |
| 101 105 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 102 106 | 
             
                  #
         | 
| 103 107 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 108 | 
            +
                  #
         | 
| 109 | 
            +
                  # @rbs (Integer min, Integer max, Hash[Symbol, untyped] options) -> void
         | 
| 104 110 | 
             
                  def raise_not_within_validation_error!(min, max, options)
         | 
| 105 111 | 
             
                    message = options[:not_within_message] || options[:not_in_message] || options[:message]
         | 
| 106 112 | 
             
                    message %= { min:, max: } unless message.nil?
         | 
| @@ -114,6 +120,8 @@ module CMDx | |
| 114 120 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 115 121 | 
             
                  #
         | 
| 116 122 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 123 | 
            +
                  #
         | 
| 124 | 
            +
                  # @rbs (Integer min, Hash[Symbol, untyped] options) -> void
         | 
| 117 125 | 
             
                  def raise_min_validation_error!(min, options)
         | 
| 118 126 | 
             
                    message = options[:min_message] || options[:message]
         | 
| 119 127 | 
             
                    message %= { min: } unless message.nil?
         | 
| @@ -127,6 +135,8 @@ module CMDx | |
| 127 135 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 128 136 | 
             
                  #
         | 
| 129 137 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 138 | 
            +
                  #
         | 
| 139 | 
            +
                  # @rbs (Integer max, Hash[Symbol, untyped] options) -> void
         | 
| 130 140 | 
             
                  def raise_max_validation_error!(max, options)
         | 
| 131 141 | 
             
                    message = options[:max_message] || options[:message]
         | 
| 132 142 | 
             
                    message %= { max: } unless message.nil?
         | 
| @@ -140,6 +150,8 @@ module CMDx | |
| 140 150 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 141 151 | 
             
                  #
         | 
| 142 152 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 153 | 
            +
                  #
         | 
| 154 | 
            +
                  # @rbs (Integer is, Hash[Symbol, untyped] options) -> void
         | 
| 143 155 | 
             
                  def raise_is_validation_error!(is, options)
         | 
| 144 156 | 
             
                    message = options[:is_message] || options[:message]
         | 
| 145 157 | 
             
                    message %= { is: } unless message.nil?
         | 
| @@ -153,6 +165,8 @@ module CMDx | |
| 153 165 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 154 166 | 
             
                  #
         | 
| 155 167 | 
             
                  # @raise [ValidationError] Always raised with appropriate message
         | 
| 168 | 
            +
                  #
         | 
| 169 | 
            +
                  # @rbs (Integer is_not, Hash[Symbol, untyped] options) -> void
         | 
| 156 170 | 
             
                  def raise_is_not_validation_error!(is_not, options)
         | 
| 157 171 | 
             
                    message = options[:is_not_message] || options[:message]
         | 
| 158 172 | 
             
                    message %= { is_not: } unless message.nil?
         | 
| @@ -48,6 +48,8 @@ module CMDx | |
| 48 48 | 
             
                  # @example Validate value exclusion
         | 
| 49 49 | 
             
                  #   Numeric.call(5, not_in: 1..10)
         | 
| 50 50 | 
             
                  #   # => nil (validation passes - 5 is not in 1..10)
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  # @rbs (Numeric value, Hash[Symbol, untyped] options) -> nil
         | 
| 51 53 | 
             
                  def call(value, options = {})
         | 
| 52 54 | 
             
                    case options
         | 
| 53 55 | 
             
                    in within:
         | 
| @@ -82,6 +84,8 @@ module CMDx | |
| 82 84 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 83 85 | 
             
                  #
         | 
| 84 86 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  # @rbs (Numeric min, Numeric max, Hash[Symbol, untyped] options) -> void
         | 
| 85 89 | 
             
                  def raise_within_validation_error!(min, max, options)
         | 
| 86 90 | 
             
                    message = options[:within_message] || options[:in_message] || options[:message]
         | 
| 87 91 | 
             
                    message %= { min:, max: } unless message.nil?
         | 
| @@ -96,6 +100,8 @@ module CMDx | |
| 96 100 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 97 101 | 
             
                  #
         | 
| 98 102 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  # @rbs (Numeric min, Numeric max, Hash[Symbol, untyped] options) -> void
         | 
| 99 105 | 
             
                  def raise_not_within_validation_error!(min, max, options)
         | 
| 100 106 | 
             
                    message = options[:not_within_message] || options[:not_in_message] || options[:message]
         | 
| 101 107 | 
             
                    message %= { min:, max: } unless message.nil?
         | 
| @@ -109,6 +115,8 @@ module CMDx | |
| 109 115 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 110 116 | 
             
                  #
         | 
| 111 117 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 118 | 
            +
                  #
         | 
| 119 | 
            +
                  # @rbs (Numeric min, Hash[Symbol, untyped] options) -> void
         | 
| 112 120 | 
             
                  def raise_min_validation_error!(min, options)
         | 
| 113 121 | 
             
                    message = options[:min_message] || options[:message]
         | 
| 114 122 | 
             
                    message %= { min: } unless message.nil?
         | 
| @@ -122,6 +130,8 @@ module CMDx | |
| 122 130 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 123 131 | 
             
                  #
         | 
| 124 132 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 133 | 
            +
                  #
         | 
| 134 | 
            +
                  # @rbs (Numeric max, Hash[Symbol, untyped] options) -> void
         | 
| 125 135 | 
             
                  def raise_max_validation_error!(max, options)
         | 
| 126 136 | 
             
                    message = options[:max_message] || options[:message]
         | 
| 127 137 | 
             
                    message %= { max: } unless message.nil?
         | 
| @@ -135,6 +145,8 @@ module CMDx | |
| 135 145 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 136 146 | 
             
                  #
         | 
| 137 147 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 148 | 
            +
                  #
         | 
| 149 | 
            +
                  # @rbs (Numeric is, Hash[Symbol, untyped] options) -> void
         | 
| 138 150 | 
             
                  def raise_is_validation_error!(is, options)
         | 
| 139 151 | 
             
                    message = options[:is_message] || options[:message]
         | 
| 140 152 | 
             
                    message %= { is: } unless message.nil?
         | 
| @@ -148,6 +160,8 @@ module CMDx | |
| 148 160 | 
             
                  # @param options [Hash] Validation options containing custom messages
         | 
| 149 161 | 
             
                  #
         | 
| 150 162 | 
             
                  # @raise [ValidationError] With appropriate error message
         | 
| 163 | 
            +
                  #
         | 
| 164 | 
            +
                  # @rbs (Numeric is_not, Hash[Symbol, untyped] options) -> void
         | 
| 151 165 | 
             
                  def raise_is_not_validation_error!(is_not, options)
         | 
| 152 166 | 
             
                    message = options[:is_not_message] || options[:message]
         | 
| 153 167 | 
             
                    message %= { is_not: } unless message.nil?
         | 
| @@ -38,6 +38,8 @@ module CMDx | |
| 38 38 | 
             
                  # @example Validate with custom message
         | 
| 39 39 | 
             
                  #   Presence.call(nil, message: "Value cannot be blank")
         | 
| 40 40 | 
             
                  #   # => raises ValidationError with custom message
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> nil
         | 
| 41 43 | 
             
                  def call(value, options = {})
         | 
| 42 44 | 
             
                    match =
         | 
| 43 45 | 
             
                      if value.is_a?(String)
         | 
    
        data/lib/cmdx/version.rb
    CHANGED
    
    
    
        data/lib/cmdx/workflow.rb
    CHANGED
    
    | @@ -20,6 +20,8 @@ module CMDx | |
| 20 20 | 
             
                  #     # This would raise an error:
         | 
| 21 21 | 
             
                  #     # def work; end
         | 
| 22 22 | 
             
                  #   end
         | 
| 23 | 
            +
                  #
         | 
| 24 | 
            +
                  # @rbs (Symbol method_name) -> void
         | 
| 23 25 | 
             
                  def method_added(method_name)
         | 
| 24 26 | 
             
                    raise "cannot redefine #{name}##{method_name} method" if method_name == :work
         | 
| 25 27 |  | 
| @@ -37,6 +39,8 @@ module CMDx | |
| 37 39 | 
             
                  #     task Task2
         | 
| 38 40 | 
             
                  #     puts pipeline.size # => 2
         | 
| 39 41 | 
             
                  #   end
         | 
| 42 | 
            +
                  #
         | 
| 43 | 
            +
                  # @rbs () -> Array[ExecutionGroup]
         | 
| 40 44 | 
             
                  def pipeline
         | 
| 41 45 | 
             
                    @pipeline ||= []
         | 
| 42 46 | 
             
                  end
         | 
| @@ -55,6 +59,8 @@ module CMDx | |
| 55 59 | 
             
                  #     include CMDx::Workflow
         | 
| 56 60 | 
             
                  #     tasks ValidateTask, ProcessTask, NotifyTask, breakpoints: [:failure, :halt]
         | 
| 57 61 | 
             
                  #   end
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # @rbs (*untyped tasks, **untyped options) -> void
         | 
| 58 64 | 
             
                  def tasks(*tasks, **options)
         | 
| 59 65 | 
             
                    pipeline << ExecutionGroup.new(
         | 
| 60 66 | 
             
                      tasks.map do |task|
         | 
| @@ -83,6 +89,8 @@ module CMDx | |
| 83 89 | 
             
                #     include CMDx::Workflow
         | 
| 84 90 | 
             
                #     # Now has access to task, tasks, and work methods
         | 
| 85 91 | 
             
                #   end
         | 
| 92 | 
            +
                #
         | 
| 93 | 
            +
                # @rbs (Class base) -> void
         | 
| 86 94 | 
             
                def self.included(base)
         | 
| 87 95 | 
             
                  base.extend(ClassMethods)
         | 
| 88 96 | 
             
                end
         | 
| @@ -100,6 +108,8 @@ module CMDx | |
| 100 108 | 
             
                #
         | 
| 101 109 | 
             
                #   workflow = MyWorkflow.new
         | 
| 102 110 | 
             
                #   result = workflow.work
         | 
| 111 | 
            +
                #
         | 
| 112 | 
            +
                # @rbs () -> void
         | 
| 103 113 | 
             
                def work
         | 
| 104 114 | 
             
                  Pipeline.execute(self)
         | 
| 105 115 | 
             
                end
         | 
    
        data/lib/cmdx.rb
    CHANGED
    
    | @@ -5,6 +5,7 @@ require "date" | |
| 5 5 | 
             
            require "forwardable"
         | 
| 6 6 | 
             
            require "json"
         | 
| 7 7 | 
             
            require "logger"
         | 
| 8 | 
            +
            require "pathname"
         | 
| 8 9 | 
             
            require "securerandom"
         | 
| 9 10 | 
             
            require "set"
         | 
| 10 11 | 
             
            require "time"
         | 
| @@ -16,6 +17,14 @@ module CMDx | |
| 16 17 |  | 
| 17 18 | 
             
              extend self
         | 
| 18 19 |  | 
| 20 | 
            +
              # Returns the path to the CMDx gem.
         | 
| 21 | 
            +
              #
         | 
| 22 | 
            +
              # @return [Pathname] the path to the CMDx gem
         | 
| 23 | 
            +
              #
         | 
| 24 | 
            +
              # @example
         | 
| 25 | 
            +
              #   CMDx.gem_path # => Pathname.new("/path/to/cmdx")
         | 
| 26 | 
            +
              #
         | 
| 27 | 
            +
              # @rbs return: Pathname
         | 
| 19 28 | 
             
              def gem_path
         | 
| 20 29 | 
             
                @gem_path ||= Pathname.new(__dir__).parent
         | 
| 21 30 | 
             
              end
         | 
| @@ -32,6 +32,15 @@ CMDx.configure do |config| | |
| 32 32 | 
             
                level: Logger::INFO
         | 
| 33 33 | 
             
              )
         | 
| 34 34 |  | 
| 35 | 
            +
              # Backtrace configuration - controls whether to log backtraces on faults and exceptions
         | 
| 36 | 
            +
              # https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#backtraces
         | 
| 37 | 
            +
              # config.backtrace = false
         | 
| 38 | 
            +
              # config.backtrace_cleaner = nil
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              # Exception handler configuration - called when non-fault exceptions are raised
         | 
| 41 | 
            +
              # https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#exception-handler
         | 
| 42 | 
            +
              # config.exception_handler = nil
         | 
| 43 | 
            +
             | 
| 35 44 | 
             
              # Additional global configurations - automatically applied to all tasks
         | 
| 36 45 | 
             
              #
         | 
| 37 46 | 
             
              # Middlewares - https://github.com/drexed/cmdx/blob/main/docs/middlewares.md
         | 
    
        data/mkdocs.yml
    ADDED
    
    | @@ -0,0 +1,122 @@ | |
| 1 | 
            +
            # yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            site_name: CMDx
         | 
| 4 | 
            +
            site_url: https://drexed.github.io/cmdx/
         | 
| 5 | 
            +
            site_description: Build business logic that's powerful, predictable, and chaos-free.
         | 
| 6 | 
            +
            site_author: drexed
         | 
| 7 | 
            +
            repo_name: drexed/cmdx
         | 
| 8 | 
            +
            repo_url: https://github.com/drexed/cmdx
         | 
| 9 | 
            +
            edit_uri: edit/main/docs/
         | 
| 10 | 
            +
            copyright: Drexed, Inc. © 2025 - EoT
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            theme:
         | 
| 13 | 
            +
              name: material
         | 
| 14 | 
            +
              logo: assets/favicon.svg
         | 
| 15 | 
            +
              favicon: assets/favicon.ico
         | 
| 16 | 
            +
              icon:
         | 
| 17 | 
            +
                repo: fontawesome/brands/github
         | 
| 18 | 
            +
              font:
         | 
| 19 | 
            +
                text: Inter
         | 
| 20 | 
            +
                code: IBM Plex Mono
         | 
| 21 | 
            +
              palette:
         | 
| 22 | 
            +
                # Palette toggle for automatic mode
         | 
| 23 | 
            +
                - media: "(prefers-color-scheme)"
         | 
| 24 | 
            +
                  primary: custom
         | 
| 25 | 
            +
                  accent: custom
         | 
| 26 | 
            +
                  toggle:
         | 
| 27 | 
            +
                    icon: material/brightness-auto
         | 
| 28 | 
            +
                    name: Switch to light mode
         | 
| 29 | 
            +
                # Palette toggle for light mode
         | 
| 30 | 
            +
                - media: "(prefers-color-scheme: light)"
         | 
| 31 | 
            +
                  scheme: default
         | 
| 32 | 
            +
                  primary: custom
         | 
| 33 | 
            +
                  accent: custom
         | 
| 34 | 
            +
                  toggle:
         | 
| 35 | 
            +
                    icon: material/brightness-7
         | 
| 36 | 
            +
                    name: Switch to dark mode
         | 
| 37 | 
            +
                # Palette toggle for dark mode
         | 
| 38 | 
            +
                - media: "(prefers-color-scheme: dark)"
         | 
| 39 | 
            +
                  scheme: slate
         | 
| 40 | 
            +
                  primary: custom
         | 
| 41 | 
            +
                  accent: custom
         | 
| 42 | 
            +
                  toggle:
         | 
| 43 | 
            +
                    icon: material/brightness-4
         | 
| 44 | 
            +
                    name: Switch to system preference
         | 
| 45 | 
            +
              features:
         | 
| 46 | 
            +
                - navigation.footer
         | 
| 47 | 
            +
                - navigation.instant
         | 
| 48 | 
            +
                - navigation.instant.prefetch
         | 
| 49 | 
            +
                - navigation.instant.progress
         | 
| 50 | 
            +
                - navigation.tracking
         | 
| 51 | 
            +
                - navigation.sections
         | 
| 52 | 
            +
                - navigation.expand
         | 
| 53 | 
            +
                - navigation.path
         | 
| 54 | 
            +
                - navigation.top
         | 
| 55 | 
            +
                - search.share
         | 
| 56 | 
            +
                - search.suggest
         | 
| 57 | 
            +
                - search.highlight
         | 
| 58 | 
            +
                - content.code.copy
         | 
| 59 | 
            +
                - content.code.annotate
         | 
| 60 | 
            +
                - content.tabs.link
         | 
| 61 | 
            +
                - content.tooltips
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            markdown_extensions:
         | 
| 64 | 
            +
              - admonition
         | 
| 65 | 
            +
              - pymdownx.details
         | 
| 66 | 
            +
              - pymdownx.superfences
         | 
| 67 | 
            +
              - pymdownx.highlight:
         | 
| 68 | 
            +
                  anchor_linenums: true
         | 
| 69 | 
            +
                  line_spans: __span
         | 
| 70 | 
            +
                  pygments_lang_class: true
         | 
| 71 | 
            +
              - pymdownx.inlinehilite
         | 
| 72 | 
            +
              - pymdownx.snippets
         | 
| 73 | 
            +
              - pymdownx.tabbed:
         | 
| 74 | 
            +
                  alternate_style: true
         | 
| 75 | 
            +
              - tables
         | 
| 76 | 
            +
              - attr_list
         | 
| 77 | 
            +
              - md_in_html
         | 
| 78 | 
            +
              - toc:
         | 
| 79 | 
            +
                  permalink: true
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            plugins:
         | 
| 82 | 
            +
              - search
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            nav:
         | 
| 85 | 
            +
              - Home: index.md
         | 
| 86 | 
            +
              - Getting Started: getting_started.md
         | 
| 87 | 
            +
              - Basics:
         | 
| 88 | 
            +
                - Setup: basics/setup.md
         | 
| 89 | 
            +
                - Execution: basics/execution.md
         | 
| 90 | 
            +
                - Context: basics/context.md
         | 
| 91 | 
            +
                - Chain: basics/chain.md
         | 
| 92 | 
            +
              - Interruptions:
         | 
| 93 | 
            +
                - Halt: interruptions/halt.md
         | 
| 94 | 
            +
                - Faults: interruptions/faults.md
         | 
| 95 | 
            +
                - Exceptions: interruptions/exceptions.md
         | 
| 96 | 
            +
              - Outcomes:
         | 
| 97 | 
            +
                - Result: outcomes/result.md
         | 
| 98 | 
            +
                - States: outcomes/states.md
         | 
| 99 | 
            +
                - Statuses: outcomes/statuses.md
         | 
| 100 | 
            +
              - Attributes:
         | 
| 101 | 
            +
                - Definitions: attributes/definitions.md
         | 
| 102 | 
            +
                - Naming: attributes/naming.md
         | 
| 103 | 
            +
                - Coercions: attributes/coercions.md
         | 
| 104 | 
            +
                - Validations: attributes/validations.md
         | 
| 105 | 
            +
                - Defaults: attributes/defaults.md
         | 
| 106 | 
            +
                - Transformations: attributes/transformations.md
         | 
| 107 | 
            +
              - Callbacks: callbacks.md
         | 
| 108 | 
            +
              - Middlewares: middlewares.md
         | 
| 109 | 
            +
              - Logging: logging.md
         | 
| 110 | 
            +
              - Internationalization: internationalization.md
         | 
| 111 | 
            +
              - Deprecation: deprecation.md
         | 
| 112 | 
            +
              - Workflows: workflows.md
         | 
| 113 | 
            +
              - Tips and Tricks: tips_and_tricks.md
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            extra:
         | 
| 116 | 
            +
              generator: false
         | 
| 117 | 
            +
              social:
         | 
| 118 | 
            +
                - icon: fontawesome/brands/github
         | 
| 119 | 
            +
                  link: https://github.com/drexed/cmdx
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            extra_css:
         | 
| 122 | 
            +
              - stylesheets/extra.css
         | 
| Binary file | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg width="104.098" height="112.266" viewBox="0 0 60 64.708" xmlns="http://www.w3.org/2000/svg"><path d="M29.907 17.723 26.4 23.17 13.384 3.323h3.507l9.508 14.77 1.938-3.139L18.737 0H7.291L26.4 29.262 45.045 0h-3.97L30 17.54zM9.23 61.293H6.091l18.646-29.447L4.43.093H.46l20.308 31.846L0 64.708l10.985-.092 39.138-61.2h3.323L31.57 37.754l13.662 21.415h3.97L35.445 37.754 59.537.093H48.37zm29.63-23.262 15.047 23.262H43.384l-13.662-20.77-15.508 24.093h3.97l11.63-18 11.723 18H60L41.17 35.354l-.278-.461z" fill="#000"/></svg>
         | 
| Binary file | 
    
        data/src/cmdx-logo.svg
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg width="352.2" height="112.266" viewBox="0 0 203 64.708" xmlns="http://www.w3.org/2000/svg" xmlSpace="preserve"><path d="M172.908 17.723 169.4 23.17 156.385 3.323h3.507l9.508 14.77 1.938-3.139L161.738 0h-11.446L169.4 29.262 188.046 0h-3.97L173 17.54zm-20.677 43.57h-3.139l18.646-29.447L147.431.093h-3.97l20.308 31.846L143 64.708l10.985-.092 39.138-61.2h3.323L174.57 37.754l13.662 21.415h3.969l-13.754-21.415L202.538.093H191.37zm29.63-23.262 15.047 23.262h-10.523l-13.662-20.77-15.508 24.093h3.97l11.63-18 11.723 18H203l-18.83-29.262-.278-.461z" fill="#fe1817"/><path d="M41.667 14v12.8h-23.42c-3.214.272-5.665 3.05-5.665 6.318s2.45 5.937 5.664 6.318H33.17v4.248H18.246a10.65 10.65 0 0 1-9.858-10.62c0-5.447 4.194-10.077 9.64-10.512h19.39v-4.303H18.246v.054A14.823 14.823 0 0 0 4.248 33.118c0 7.898 6.21 14.38 13.998 14.815h19.172v-8.497h4.249v12.745h-23.42A19.063 19.063 0 0 1 0 33.118a19.033 19.033 0 0 1 18.246-19.063zM75 35.623 87.2 14h13.508v38.181H87.963v-14.27l-8.116 14.27h-9.749L57.734 30.504v-8.007l14.87 25.436h4.792l14.815-25.436v25.436h4.249V18.249H89.65l-14.76 25.49-14.542-25.49H53.54v29.684h4.194v-8.007l4.249 7.299v4.956H49.292v-38.18H62.8zM108.333 14h23.42C141.94 14.436 150 22.824 150 33.064c0 10.294-8.061 18.681-18.246 19.117h-23.42V22.497h23.42a10.65 10.65 0 0 1 9.858 10.621c0 5.447-4.194 10.13-9.64 10.566H116.83V30.286h4.248v9.15l10.676-.054c3.213-.273 5.664-3.05 5.664-6.264 0-2.778-1.743-5.065-4.194-5.991-.926-.382-1.47-.382-2.94-.382h-17.702v21.188h19.172a14.84 14.84 0 0 0 13.998-14.87c0-7.897-6.21-14.379-13.998-14.814h-23.42z"/></svg>
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: cmdx
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.9.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Juan Gomez
         | 
| @@ -214,10 +214,14 @@ files: | |
| 214 214 | 
             
            - LLM.md
         | 
| 215 215 | 
             
            - README.md
         | 
| 216 216 | 
             
            - Rakefile
         | 
| 217 | 
            +
            - docs/.DS_Store
         | 
| 218 | 
            +
            - docs/assets/favicon.ico
         | 
| 219 | 
            +
            - docs/assets/favicon.svg
         | 
| 217 220 | 
             
            - docs/attributes/coercions.md
         | 
| 218 221 | 
             
            - docs/attributes/defaults.md
         | 
| 219 222 | 
             
            - docs/attributes/definitions.md
         | 
| 220 223 | 
             
            - docs/attributes/naming.md
         | 
| 224 | 
            +
            - docs/attributes/transformations.md
         | 
| 221 225 | 
             
            - docs/attributes/validations.md
         | 
| 222 226 | 
             
            - docs/basics/chain.md
         | 
| 223 227 | 
             
            - docs/basics/context.md
         | 
| @@ -226,6 +230,7 @@ files: | |
| 226 230 | 
             
            - docs/callbacks.md
         | 
| 227 231 | 
             
            - docs/deprecation.md
         | 
| 228 232 | 
             
            - docs/getting_started.md
         | 
| 233 | 
            +
            - docs/index.md
         | 
| 229 234 | 
             
            - docs/internationalization.md
         | 
| 230 235 | 
             
            - docs/interruptions/exceptions.md
         | 
| 231 236 | 
             
            - docs/interruptions/faults.md
         | 
| @@ -235,8 +240,11 @@ files: | |
| 235 240 | 
             
            - docs/outcomes/result.md
         | 
| 236 241 | 
             
            - docs/outcomes/states.md
         | 
| 237 242 | 
             
            - docs/outcomes/statuses.md
         | 
| 243 | 
            +
            - docs/stylesheets/extra.css
         | 
| 238 244 | 
             
            - docs/tips_and_tricks.md
         | 
| 239 245 | 
             
            - docs/workflows.md
         | 
| 246 | 
            +
            - examples/active_record_query_tagging.md
         | 
| 247 | 
            +
            - examples/paper_trail_whatdunnit.md
         | 
| 240 248 | 
             
            - lib/cmdx.rb
         | 
| 241 249 | 
             
            - lib/cmdx/.DS_Store
         | 
| 242 250 | 
             
            - lib/cmdx/attribute.rb
         | 
| @@ -265,7 +273,6 @@ files: | |
| 265 273 | 
             
            - lib/cmdx/exceptions.rb
         | 
| 266 274 | 
             
            - lib/cmdx/executor.rb
         | 
| 267 275 | 
             
            - lib/cmdx/faults.rb
         | 
| 268 | 
            -
            - lib/cmdx/freezer.rb
         | 
| 269 276 | 
             
            - lib/cmdx/identifier.rb
         | 
| 270 277 | 
             
            - lib/cmdx/locale.rb
         | 
| 271 278 | 
             
            - lib/cmdx/log_formatters/json.rb
         | 
| @@ -386,7 +393,11 @@ files: | |
| 386 393 | 
             
            - lib/locales/zh-HK.yml
         | 
| 387 394 | 
             
            - lib/locales/zh-TW.yml
         | 
| 388 395 | 
             
            - lib/locales/zh-YUE.yml
         | 
| 389 | 
            -
            -  | 
| 396 | 
            +
            - mkdocs.yml
         | 
| 397 | 
            +
            - src/cmdx-dark-logo.png
         | 
| 398 | 
            +
            - src/cmdx-favicon.svg
         | 
| 399 | 
            +
            - src/cmdx-light-logo.png
         | 
| 400 | 
            +
            - src/cmdx-logo.svg
         | 
| 390 401 | 
             
            homepage: https://github.com/drexed/cmdx
         | 
| 391 402 | 
             
            licenses:
         | 
| 392 403 | 
             
            - MIT
         | 
    
        data/lib/cmdx/freezer.rb
    DELETED
    
    | @@ -1,51 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module CMDx
         | 
| 4 | 
            -
              # Provides freezing functionality for CMDx tasks and their associated objects.
         | 
| 5 | 
            -
              #
         | 
| 6 | 
            -
              # The Freezer module is responsible for making task objects immutable after execution
         | 
| 7 | 
            -
              # to prevent accidental modifications and ensure data integrity. It can be disabled
         | 
| 8 | 
            -
              # via environment variable for testing or debugging purposes.
         | 
| 9 | 
            -
              module Freezer
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                extend self
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                # Freezes a task and its associated objects to prevent modifications.
         | 
| 14 | 
            -
                #
         | 
| 15 | 
            -
                # This method makes the task, result, context, and chain immutable after execution.
         | 
| 16 | 
            -
                # Freezing can be skipped by setting the SKIP_CMDX_FREEZING environment variable.
         | 
| 17 | 
            -
                #
         | 
| 18 | 
            -
                # @param task [Task] The task instance to freeze
         | 
| 19 | 
            -
                # @option ENV["SKIP_CMDX_FREEZING"] [String, Boolean] Set to "true" or true to skip freezing
         | 
| 20 | 
            -
                #
         | 
| 21 | 
            -
                # @raise [RuntimeError] If attempting to stub on frozen objects
         | 
| 22 | 
            -
                #
         | 
| 23 | 
            -
                # @example Freeze a completed task
         | 
| 24 | 
            -
                #   task = MyTask.new
         | 
| 25 | 
            -
                #   task.execute
         | 
| 26 | 
            -
                #   CMDx::Freezer.immute(task)
         | 
| 27 | 
            -
                #   # task, result, context, and chain are now frozen
         | 
| 28 | 
            -
                # @example Skip freezing for testing
         | 
| 29 | 
            -
                #   ENV["SKIP_CMDX_FREEZING"] = "true"
         | 
| 30 | 
            -
                #   CMDx::Freezer.immute(task)
         | 
| 31 | 
            -
                #   # No freezing occurs
         | 
| 32 | 
            -
                def immute(task)
         | 
| 33 | 
            -
                  # Stubbing on frozen objects is not allowed
         | 
| 34 | 
            -
                  skip_freezing = ENV.fetch("SKIP_CMDX_FREEZING", false)
         | 
| 35 | 
            -
                  return if Coercions::Boolean.call(skip_freezing)
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                  task.freeze
         | 
| 38 | 
            -
                  task.result.freeze
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                  # Freezing the context and chain can only be done
         | 
| 41 | 
            -
                  # once the outer-most task has completed.
         | 
| 42 | 
            -
                  return unless task.result.index.zero?
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                  task.context.freeze
         | 
| 45 | 
            -
                  task.chain.freeze
         | 
| 46 | 
            -
             | 
| 47 | 
            -
                  Chain.clear
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
              end
         | 
| 51 | 
            -
            end
         | 
    
        data/src/cmdx-logo.png
    DELETED
    
    | Binary file |