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/README.md
    CHANGED
    
    | @@ -1,68 +1,49 @@ | |
| 1 | 
            -
            < | 
| 2 | 
            -
              <img src="./src/cmdx-logo.png" width="200" alt="CMDx Logo">
         | 
| 3 | 
            -
             | 
| 1 | 
            +
            <div align="center">
         | 
| 2 | 
            +
              <img src="./src/cmdx-light-logo.png#gh-light-mode-only" width="200" alt="CMDx Logo">
         | 
| 3 | 
            +
              <img src="./src/cmdx-dark-logo.png#gh-dark-mode-only" width="200" alt="CMDx Logo">
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              ---
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              Build business logic that’s powerful, predictable, and maintainable.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              [Documentation](https://drexed.github.io/cmdx) · [Changelog](./CHANGELOG.md) · [Report Bug](https://github.com/drexed/cmdx/issues) · [Request Feature](https://github.com/drexed/cmdx/issues)
         | 
| 4 10 |  | 
| 5 | 
            -
            <p align="center">
         | 
| 6 11 | 
             
              <img alt="Version" src="https://img.shields.io/gem/v/cmdx">
         | 
| 7 12 | 
             
              <img alt="Build" src="https://github.com/drexed/cmdx/actions/workflows/ci.yml/badge.svg">
         | 
| 8 13 | 
             
              <img alt="License" src="https://img.shields.io/github/license/drexed/cmdx">
         | 
| 9 | 
            -
            </ | 
| 14 | 
            +
            </div>
         | 
| 10 15 |  | 
| 11 | 
            -
            #  | 
| 16 | 
            +
            # CMDx
         | 
| 12 17 |  | 
| 13 | 
            -
             | 
| 18 | 
            +
            Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.
         | 
| 14 19 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
            -  | 
| 17 | 
            -
            - Get built-in flow control, logging, validations, and more...
         | 
| 20 | 
            +
            > [!NOTE]
         | 
| 21 | 
            +
            > Documentation reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.
         | 
| 18 22 |  | 
| 19 | 
            -
             | 
| 23 | 
            +
            ## Requirements
         | 
| 20 24 |  | 
| 21 | 
            -
             | 
| 25 | 
            +
            - Ruby: MRI 3.1+ or JRuby 9.4+.
         | 
| 22 26 |  | 
| 23 | 
            -
            CMDx  | 
| 24 | 
            -
             | 
| 25 | 
            -
            - **Compose** → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
         | 
| 26 | 
            -
            - **Execute** → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
         | 
| 27 | 
            -
            - **React** → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
         | 
| 28 | 
            -
            - **Observe** → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
         | 
| 27 | 
            +
            CMDx works with any Ruby framework. Rails support is built-in, but it's framework-agnostic at its core.
         | 
| 29 28 |  | 
| 30 29 | 
             
            ## Installation
         | 
| 31 30 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 31 | 
            +
            ```sh
         | 
| 32 | 
            +
            gem install cmdx
         | 
| 33 | 
            +
            # - or -
         | 
| 34 | 
            +
            bundle add cmdx
         | 
| 36 35 | 
             
            ```
         | 
| 37 36 |  | 
| 38 | 
            -
            And then execute:
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                $ bundle
         | 
| 41 | 
            -
             | 
| 42 | 
            -
            Or install it yourself as:
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                $ gem install cmdx
         | 
| 45 | 
            -
             | 
| 46 37 | 
             
            ## Quick Example
         | 
| 47 38 |  | 
| 48 | 
            -
             | 
| 39 | 
            +
            Build powerful business logic in four simple steps:
         | 
| 49 40 |  | 
| 50 41 | 
             
            ### 1. Compose
         | 
| 51 42 |  | 
| 52 | 
            -
            #### Minimum Viable Task
         | 
| 53 | 
            -
             | 
| 54 43 | 
             
            ```ruby
         | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
                user = User.find(context.user_id)
         | 
| 58 | 
            -
                MetricsMailer.analyzed(user).deliver_now
         | 
| 59 | 
            -
              end
         | 
| 60 | 
            -
            end
         | 
| 61 | 
            -
            ```
         | 
| 62 | 
            -
             | 
| 63 | 
            -
            #### Fully Featured Task
         | 
| 44 | 
            +
            # Full-featured task example
         | 
| 45 | 
            +
            # See docs for minimum viable task examples
         | 
| 64 46 |  | 
| 65 | 
            -
            ```ruby
         | 
| 66 47 | 
             
            class AnalyzeMetrics < CMDx::Task
         | 
| 67 48 | 
             
              register :middleware, CMDx::Middlewares::Correlate, id: -> { Current.request_id }
         | 
| 68 49 |  | 
| @@ -127,35 +108,7 @@ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx: | |
| 127 108 | 
             
            index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
         | 
| 128 109 | 
             
            ```
         | 
| 129 110 |  | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
            - [Getting Started](docs/getting_started.md)
         | 
| 133 | 
            -
            - Basics
         | 
| 134 | 
            -
              - [Setup](docs/basics/setup.md)
         | 
| 135 | 
            -
              - [Execution](docs/basics/execution.md)
         | 
| 136 | 
            -
              - [Context](docs/basics/context.md)
         | 
| 137 | 
            -
              - [Chain](docs/basics/chain.md)
         | 
| 138 | 
            -
            - Interruptions
         | 
| 139 | 
            -
              - [Halt](docs/interruptions/halt.md)
         | 
| 140 | 
            -
              - [Faults](docs/interruptions/faults.md)
         | 
| 141 | 
            -
              - [Exceptions](docs/interruptions/exceptions.md)
         | 
| 142 | 
            -
            - Outcomes
         | 
| 143 | 
            -
              - [Result](docs/outcomes/result.md)
         | 
| 144 | 
            -
              - [States](docs/outcomes/states.md)
         | 
| 145 | 
            -
              - [Statuses](docs/outcomes/statuses.md)
         | 
| 146 | 
            -
            - Attributes
         | 
| 147 | 
            -
              - [Definitions](docs/attributes/definitions.md)
         | 
| 148 | 
            -
              - [Naming](docs/attributes/naming.md)
         | 
| 149 | 
            -
              - [Coercions](docs/attributes/coercions.md)
         | 
| 150 | 
            -
              - [Validations](docs/attributes/validations.md)
         | 
| 151 | 
            -
              - [Defaults](docs/attributes/defaults.md)
         | 
| 152 | 
            -
            - [Callbacks](docs/callbacks.md)
         | 
| 153 | 
            -
            - [Middlewares](docs/middlewares.md)
         | 
| 154 | 
            -
            - [Logging](docs/logging.md)
         | 
| 155 | 
            -
            - [Internationalization (i18n)](docs/internationalization.md)
         | 
| 156 | 
            -
            - [Deprecation](docs/deprecation.md)
         | 
| 157 | 
            -
            - [Workflows](docs/workflows.md)
         | 
| 158 | 
            -
            - [Tips and Tricks](docs/tips_and_tricks.md)
         | 
| 111 | 
            +
            Ready to dive in? Check out the [Getting Started](https://drexed.github.io/cmdx/getting_started/) guide to learn more.
         | 
| 159 112 |  | 
| 160 113 | 
             
            ## Ecosystem
         | 
| 161 114 |  | 
| @@ -166,20 +119,10 @@ For backwards compatibility of certain functionality: | |
| 166 119 | 
             
            - [cmdx-i18n](https://github.com/drexed/cmdx-i18n) - 85+ translations, `v1.5.0` - `v1.6.2`
         | 
| 167 120 | 
             
            - [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks, `v1.6.1` - `v1.6.2`
         | 
| 168 121 |  | 
| 169 | 
            -
            ## Development
         | 
| 170 | 
            -
             | 
| 171 | 
            -
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         | 
| 172 | 
            -
             | 
| 173 | 
            -
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         | 
| 174 | 
            -
             | 
| 175 122 | 
             
            ## Contributing
         | 
| 176 123 |  | 
| 177 | 
            -
            Bug reports and pull requests are welcome  | 
| 124 | 
            +
            Bug reports and pull requests are welcome at <https://github.com/drexed/cmdx>. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
         | 
| 178 125 |  | 
| 179 126 | 
             
            ## License
         | 
| 180 127 |  | 
| 181 128 | 
             
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         | 
| 182 | 
            -
             | 
| 183 | 
            -
            ## Code of Conduct
         | 
| 184 | 
            -
             | 
| 185 | 
            -
            Everyone interacting in the CMDx project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
         | 
    
        data/docs/.DS_Store
    ADDED
    
    | Binary file | 
| Binary file | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1038)"><rect width="1000" height="1000" fill="#ffffff"></rect><g transform="matrix(6.235191420376605,0,0,6.235191420376605,175.46452176081812,150)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="104.098" height="112.266"><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"></path></svg></svg></g></g><defs><clipPath id="SvgjsClipPath1038"><rect width="1000" height="1000" x="0" y="0" rx="150" ry="150"></rect></clipPath></defs></svg>
         | 
| @@ -1,18 +1,8 @@ | |
| 1 1 | 
             
            # Attributes - Coercions
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            Automatically convert inputs to expected types. Coercions handle everything from simple string-to-integer conversions to JSON parsing.
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            ## Table of Contents
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            - [Usage](#usage)
         | 
| 10 | 
            -
            - [Built-in Coercions](#built-in-coercions)
         | 
| 11 | 
            -
            - [Declarations](#declarations)
         | 
| 12 | 
            -
              - [Proc or Lambda](#proc-or-lambda)
         | 
| 13 | 
            -
              - [Class or Module](#class-or-module)
         | 
| 14 | 
            -
            - [Removals](#removals)
         | 
| 15 | 
            -
            - [Error Handling](#error-handling)
         | 
| 5 | 
            +
            See [Global Configuration](../getting_started.md#coercions) for custom coercion setup.
         | 
| 16 6 |  | 
| 17 7 | 
             
            ## Usage
         | 
| 18 8 |  | 
| @@ -43,8 +33,9 @@ ParseMetrics.execute( | |
| 43 33 | 
             
            )
         | 
| 44 34 | 
             
            ```
         | 
| 45 35 |  | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 36 | 
            +
            !!! tip
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                Specify multiple coercion types for attributes that could be a variety of value formats. CMDx attempts each type in order until one succeeds.
         | 
| 48 39 |  | 
| 49 40 | 
             
            ## Built-in Coercions
         | 
| 50 41 |  | 
| @@ -66,8 +57,9 @@ ParseMetrics.execute( | |
| 66 57 |  | 
| 67 58 | 
             
            ## Declarations
         | 
| 68 59 |  | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 60 | 
            +
            !!! warning "Important"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                Custom coercions must raise `CMDx::CoercionError` with a descriptive message.
         | 
| 71 63 |  | 
| 72 64 | 
             
            ### Proc or Lambda
         | 
| 73 65 |  | 
| @@ -117,10 +109,11 @@ end | |
| 117 109 |  | 
| 118 110 | 
             
            ## Removals
         | 
| 119 111 |  | 
| 120 | 
            -
            Remove  | 
| 112 | 
            +
            Remove unwanted coercions:
         | 
| 121 113 |  | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 114 | 
            +
            !!! warning
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                Each `deregister` call removes one coercion. Use multiple calls for batch removals.
         | 
| 124 117 |  | 
| 125 118 | 
             
            ```ruby
         | 
| 126 119 | 
             
            class TransformCoordinates < CMDx::Task
         | 
| @@ -160,8 +153,3 @@ result.metadata #=> { | |
| 160 153 | 
             
                            #     }
         | 
| 161 154 | 
             
                            #   }
         | 
| 162 155 | 
             
            ```
         | 
| 163 | 
            -
             | 
| 164 | 
            -
            ---
         | 
| 165 | 
            -
             | 
| 166 | 
            -
            - **Prev:** [Attributes - Naming](naming.md)
         | 
| 167 | 
            -
            - **Next:** [Attributes - Validations](validations.md)
         | 
    
        data/docs/attributes/defaults.md
    CHANGED
    
    | @@ -1,18 +1,10 @@ | |
| 1 1 | 
             
            # Attributes - Defaults
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
            ## Table of Contents
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            - [Declarations](#declarations)
         | 
| 8 | 
            -
              - [Static Values](#static-values)
         | 
| 9 | 
            -
              - [Symbol References](#symbol-references)
         | 
| 10 | 
            -
              - [Proc or Lambda](#proc-or-lambda)
         | 
| 11 | 
            -
            - [Coercions and Validations](#coercions-and-validations)
         | 
| 3 | 
            +
            Provide fallback values for optional attributes. Defaults kick in when values aren't provided or are `nil`.
         | 
| 12 4 |  | 
| 13 5 | 
             
            ## Declarations
         | 
| 14 6 |  | 
| 15 | 
            -
            Defaults  | 
| 7 | 
            +
            Defaults work seamlessly with coercions, validations, and nested attributes:
         | 
| 16 8 |  | 
| 17 9 | 
             
            ### Static Values
         | 
| 18 10 |  | 
| @@ -72,7 +64,7 @@ end | |
| 72 64 |  | 
| 73 65 | 
             
            ## Coercions and Validations
         | 
| 74 66 |  | 
| 75 | 
            -
            Defaults  | 
| 67 | 
            +
            Defaults follow the same coercion and validation rules as provided values:
         | 
| 76 68 |  | 
| 77 69 | 
             
            ```ruby
         | 
| 78 70 | 
             
            class ScheduleBackup < CMDx::Task
         | 
| @@ -83,8 +75,3 @@ class ScheduleBackup < CMDx::Task | |
| 83 75 | 
             
              optional :frequency, default: "daily", inclusion: { in: %w[hourly daily weekly monthly] }
         | 
| 84 76 | 
             
            end
         | 
| 85 77 | 
             
            ```
         | 
| 86 | 
            -
             | 
| 87 | 
            -
            ---
         | 
| 88 | 
            -
             | 
| 89 | 
            -
            - **Prev:** [Attributes - Validations](validations.md)
         | 
| 90 | 
            -
            - **Next:** [Callbacks](../callbacks.md)
         | 
| @@ -1,24 +1,12 @@ | |
| 1 1 | 
             
            # Attributes - Definitions
         | 
| 2 2 |  | 
| 3 | 
            -
            Attributes define  | 
| 4 | 
            -
             | 
| 5 | 
            -
            ## Table of Contents
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            - [Declarations](#declarations)
         | 
| 8 | 
            -
              - [Optional](#optional)
         | 
| 9 | 
            -
              - [Required](#required)
         | 
| 10 | 
            -
            - [Sources](#sources)
         | 
| 11 | 
            -
              - [Context](#context)
         | 
| 12 | 
            -
              - [Symbol References](#symbol-references)
         | 
| 13 | 
            -
              - [Proc or Lambda](#proc-or-lambda)
         | 
| 14 | 
            -
              - [Class or Module](#class-or-module)
         | 
| 15 | 
            -
            - [Nesting](#nesting)
         | 
| 16 | 
            -
            - [Error Handling](#error-handling)
         | 
| 3 | 
            +
            Attributes define your task's interface with automatic validation, type coercion, and accessor generation. They're the contract between callers and your business logic.
         | 
| 17 4 |  | 
| 18 5 | 
             
            ## Declarations
         | 
| 19 6 |  | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 7 | 
            +
            !!! tip
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                Prefer using the `required` and `optional` alias for `attributes` for brevity and to clearly signal intent.
         | 
| 22 10 |  | 
| 23 11 | 
             
            ### Optional
         | 
| 24 12 |  | 
| @@ -87,7 +75,7 @@ PublishArticle.execute( | |
| 87 75 |  | 
| 88 76 | 
             
            ## Sources
         | 
| 89 77 |  | 
| 90 | 
            -
            Attributes  | 
| 78 | 
            +
            Attributes read from any accessible object—not just context. Use sources to pull data from models, services, or any callable:
         | 
| 91 79 |  | 
| 92 80 | 
             
            ### Context
         | 
| 93 81 |  | 
| @@ -167,10 +155,11 @@ end | |
| 167 155 |  | 
| 168 156 | 
             
            ## Nesting
         | 
| 169 157 |  | 
| 170 | 
            -
             | 
| 158 | 
            +
            Build complex structures with nested attributes. Children inherit their parent as source and support all attribute options:
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            !!! note
         | 
| 171 161 |  | 
| 172 | 
            -
             | 
| 173 | 
            -
            > All options available to top-level attributes are available to nested attributes, eg: naming, coercions, and validations
         | 
| 162 | 
            +
                Nested attributes support all features: naming, coercions, validations, defaults, and more.
         | 
| 174 163 |  | 
| 175 164 | 
             
            ```ruby
         | 
| 176 165 | 
             
            class ConfigureServer < CMDx::Task
         | 
| @@ -223,15 +212,17 @@ ConfigureServer.execute( | |
| 223 212 | 
             
            )
         | 
| 224 213 | 
             
            ```
         | 
| 225 214 |  | 
| 226 | 
            -
             | 
| 227 | 
            -
             | 
| 215 | 
            +
            !!! warning "Important"
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                Child requirements only apply when the parent is provided—perfect for optional structures.
         | 
| 228 218 |  | 
| 229 219 | 
             
            ## Error Handling
         | 
| 230 220 |  | 
| 231 | 
            -
             | 
| 221 | 
            +
            Validation failures provide detailed, structured error messages:
         | 
| 222 | 
            +
             | 
| 223 | 
            +
            !!! note
         | 
| 232 224 |  | 
| 233 | 
            -
             | 
| 234 | 
            -
            > Nested attributes are only ever evaluated when the parent attribute is available and valid.
         | 
| 225 | 
            +
                Nested attributes are only validated when their parent is present and valid.
         | 
| 235 226 |  | 
| 236 227 | 
             
            ```ruby
         | 
| 237 228 | 
             
            class ConfigureServer < CMDx::Task
         | 
| @@ -280,8 +271,3 @@ result.metadata #=> { | |
| 280 271 | 
             
                            #     }
         | 
| 281 272 | 
             
                            #   }
         | 
| 282 273 | 
             
            ```
         | 
| 283 | 
            -
             | 
| 284 | 
            -
            ---
         | 
| 285 | 
            -
             | 
| 286 | 
            -
            - **Prev:** [Outcomes - States](../outcomes/states.md)
         | 
| 287 | 
            -
            - **Next:** [Attributes - Naming](naming.md)
         | 
    
        data/docs/attributes/naming.md
    CHANGED
    
    | @@ -1,15 +1,10 @@ | |
| 1 1 | 
             
            # Attributes - Naming
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            Customize accessor method names to avoid conflicts and improve clarity. Affixing changes only the generated methods—not the original attribute names.
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
            > Affixing modifies only the generated accessor method names within tasks.
         | 
| 5 | 
            +
            !!! note
         | 
| 7 6 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            - [Prefix](#prefix)
         | 
| 11 | 
            -
            - [Suffix](#suffix)
         | 
| 12 | 
            -
            - [As](#as)
         | 
| 7 | 
            +
                Use naming when attributes conflict with existing methods or need better clarity in your code.
         | 
| 13 8 |  | 
| 14 9 | 
             
            ## Prefix
         | 
| 15 10 |  | 
| @@ -71,8 +66,3 @@ end | |
| 71 66 | 
             
            # Attributes passed as original attribute names
         | 
| 72 67 | 
             
            ScheduleMaintenance.execute(scheduled_at: DateTime.new(2024, 12, 15, 2, 0, 0))
         | 
| 73 68 | 
             
            ```
         | 
| 74 | 
            -
             | 
| 75 | 
            -
            ---
         | 
| 76 | 
            -
             | 
| 77 | 
            -
            - **Prev:** [Attributes - Definitions](definitions.md)
         | 
| 78 | 
            -
            - **Next:** [Attributes - Coercions](coercions.md)
         | 
| @@ -0,0 +1,63 @@ | |
| 1 | 
            +
            # Attributes - Transformations
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Modify attribute values after coercion but before validation. Perfect for normalization, formatting, and data cleanup.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Declarations
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### Symbol References
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Reference instance methods by symbol for dynamic value transformations:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ```ruby
         | 
| 12 | 
            +
            class ProcessAnalytics < CMDx::Task
         | 
| 13 | 
            +
              attribute :options, transform: :compact_blank
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
            ```
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ### Proc or Lambda
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            Use anonymous functions for dynamic value transformations:
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ```ruby
         | 
| 22 | 
            +
            class CacheContent < CMDx::Task
         | 
| 23 | 
            +
              # Proc
         | 
| 24 | 
            +
              attribute :expire_hours, transform: proc { |v| v * 2 }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              # Lambda
         | 
| 27 | 
            +
              attribute :compression, transform: ->(v) { v.to_s.upcase.strip[0..2]  }
         | 
| 28 | 
            +
            end
         | 
| 29 | 
            +
            ```
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            ### Class or Module
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            Use any object that responds to `call` for reusable transformation logic:
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            ```ruby
         | 
| 36 | 
            +
            class EmailNormalizer
         | 
| 37 | 
            +
              def call(value)
         | 
| 38 | 
            +
                value.to_s.downcase.strip
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            class ProcessContacts < CMDx::Task
         | 
| 43 | 
            +
              # Class or Module
         | 
| 44 | 
            +
              attribute :email, transform: EmailNormalizer
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              # Instance
         | 
| 47 | 
            +
              attribute :email, transform: EmailNormalizer.new
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
            ```
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ## Validations
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            Validations run on transformed values, ensuring data consistency:
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            ```ruby
         | 
| 56 | 
            +
            class ScheduleBackup < CMDx::Task
         | 
| 57 | 
            +
              # Coercions
         | 
| 58 | 
            +
              attribute :retention_days, type: :integer, transform: proc { |v| v.clamp(1, 5) }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              # Validations
         | 
| 61 | 
            +
              optional :frequency, transform: :downcase, inclusion: { in: %w[hourly daily weekly monthly] }
         | 
| 62 | 
            +
            end
         | 
| 63 | 
            +
            ```
         | 
| @@ -1,25 +1,8 @@ | |
| 1 1 | 
             
            # Attributes - Validations
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            ## Table of Contents
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            - [Usage](#usage)
         | 
| 10 | 
            -
            - [Built-in Validators](#built-in-validators)
         | 
| 11 | 
            -
              - [Common Options](#common-options)
         | 
| 12 | 
            -
              - [Exclusion](#exclusion)
         | 
| 13 | 
            -
              - [Format](#format)
         | 
| 14 | 
            -
              - [Inclusion](#inclusion)
         | 
| 15 | 
            -
              - [Length](#length)
         | 
| 16 | 
            -
              - [Numeric](#numeric)
         | 
| 17 | 
            -
              - [Presence](#presence)
         | 
| 18 | 
            -
            - [Declarations](#declarations)
         | 
| 19 | 
            -
              - [Proc or Lambda](#proc-or-lambda)
         | 
| 20 | 
            -
              - [Class or Module](#class-or-module)
         | 
| 21 | 
            -
            - [Removals](#removals)
         | 
| 22 | 
            -
            - [Error Handling](#error-handling)
         | 
| 3 | 
            +
            Ensure inputs meet requirements before execution. Validations run after coercions, giving you declarative data integrity checks.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            See [Global Configuration](../getting_started.md#validators) for custom validator setup.
         | 
| 23 6 |  | 
| 24 7 | 
             
            ## Usage
         | 
| 25 8 |  | 
| @@ -55,8 +38,9 @@ ProcessSubscription.execute( | |
| 55 38 | 
             
            )
         | 
| 56 39 | 
             
            ```
         | 
| 57 40 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 41 | 
            +
            !!! tip
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                Validations run after coercions, so you can validate the final coerced values rather than raw input.
         | 
| 60 44 |  | 
| 61 45 | 
             
            ## Built-in Validators
         | 
| 62 46 |  | 
| @@ -211,8 +195,9 @@ end | |
| 211 195 |  | 
| 212 196 | 
             
            ## Declarations
         | 
| 213 197 |  | 
| 214 | 
            -
             | 
| 215 | 
            -
             | 
| 198 | 
            +
            !!! warning "Important"
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                Custom validators must raise `CMDx::ValidationError` with a descriptive message.
         | 
| 216 201 |  | 
| 217 202 | 
             
            ### Proc or Lambda
         | 
| 218 203 |  | 
| @@ -258,10 +243,11 @@ end | |
| 258 243 |  | 
| 259 244 | 
             
            ## Removals
         | 
| 260 245 |  | 
| 261 | 
            -
            Remove  | 
| 246 | 
            +
            Remove unwanted validators:
         | 
| 262 247 |  | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 248 | 
            +
            !!! warning
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                Each `deregister` call removes one validator. Use multiple calls for batch removals.
         | 
| 265 251 |  | 
| 266 252 | 
             
            ```ruby
         | 
| 267 253 | 
             
            class SetupApplication < CMDx::Task
         | 
| @@ -271,7 +257,7 @@ end | |
| 271 257 |  | 
| 272 258 | 
             
            ## Error Handling
         | 
| 273 259 |  | 
| 274 | 
            -
            Validation failures provide detailed | 
| 260 | 
            +
            Validation failures provide detailed, structured error messages:
         | 
| 275 261 |  | 
| 276 262 | 
             
            ```ruby
         | 
| 277 263 | 
             
            class CreateProject < CMDx::Task
         | 
| @@ -307,8 +293,3 @@ result.metadata #=> { | |
| 307 293 | 
             
                            #     }
         | 
| 308 294 | 
             
                            #   }
         | 
| 309 295 | 
             
            ```
         | 
| 310 | 
            -
             | 
| 311 | 
            -
            ---
         | 
| 312 | 
            -
             | 
| 313 | 
            -
            - **Prev:** [Attributes - Coercions](coercions.md)
         | 
| 314 | 
            -
            - **Next:** [Attributes - Defaults](defaults.md)
         | 
    
        data/docs/basics/chain.md
    CHANGED
    
    | @@ -1,20 +1,14 @@ | |
| 1 1 | 
             
            # Basics - Chain
         | 
| 2 2 |  | 
| 3 | 
            -
            Chains automatically  | 
| 4 | 
            -
             | 
| 5 | 
            -
            ## Table of Contents
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            - [Management](#management)
         | 
| 8 | 
            -
            - [Links](#links)
         | 
| 9 | 
            -
            - [Inheritance](#inheritance)
         | 
| 10 | 
            -
            - [Structure](#structure)
         | 
| 3 | 
            +
            Chains automatically track related task executions within a thread. Think of them as execution traces that help you understand what happened and in what order.
         | 
| 11 4 |  | 
| 12 5 | 
             
            ## Management
         | 
| 13 6 |  | 
| 14 | 
            -
            Each thread maintains its own chain  | 
| 7 | 
            +
            Each thread maintains its own isolated chain using thread-local storage.
         | 
| 15 8 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 9 | 
            +
            !!! warning
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                Chains are thread-local. Don't share chain references across threads—it causes race conditions.
         | 
| 18 12 |  | 
| 19 13 | 
             
            ```ruby
         | 
| 20 14 | 
             
            # Thread A
         | 
| @@ -36,10 +30,11 @@ CMDx::Chain.clear    #=> Clears current thread's chain | |
| 36 30 |  | 
| 37 31 | 
             
            ## Links
         | 
| 38 32 |  | 
| 39 | 
            -
             | 
| 33 | 
            +
            Tasks automatically create or join the current thread's chain:
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            !!! warning "Important"
         | 
| 40 36 |  | 
| 41 | 
            -
             | 
| 42 | 
            -
            > Chain creation is automatic and transparent. You don't need to manually manage chain lifecycle.
         | 
| 37 | 
            +
                Chain management is automatic—no manual lifecycle handling needed.
         | 
| 43 38 |  | 
| 44 39 | 
             
            ```ruby
         | 
| 45 40 | 
             
            class ImportDataset < CMDx::Task
         | 
| @@ -62,7 +57,7 @@ end | |
| 62 57 |  | 
| 63 58 | 
             
            ## Inheritance
         | 
| 64 59 |  | 
| 65 | 
            -
             | 
| 60 | 
            +
            Subtasks automatically inherit the current thread's chain, building a unified execution trail:
         | 
| 66 61 |  | 
| 67 62 | 
             
            ```ruby
         | 
| 68 63 | 
             
            class ImportDataset < CMDx::Task
         | 
| @@ -87,10 +82,11 @@ chain.results.map { |r| r.task.class } | |
| 87 82 |  | 
| 88 83 | 
             
            ## Structure
         | 
| 89 84 |  | 
| 90 | 
            -
            Chains  | 
| 85 | 
            +
            Chains expose comprehensive execution information:
         | 
| 91 86 |  | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 87 | 
            +
            !!! warning "Important"
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                Chain state reflects the first (outermost) task result. Subtasks maintain their own states.
         | 
| 94 90 |  | 
| 95 91 | 
             
            ```ruby
         | 
| 96 92 | 
             
            result = ImportDataset.execute(dataset_id: 456)
         | 
| @@ -110,8 +106,3 @@ chain.results.each_with_index do |result, index| | |
| 110 106 | 
             
              puts "#{index}: #{result.task.class} - #{result.status}"
         | 
| 111 107 | 
             
            end
         | 
| 112 108 | 
             
            ```
         | 
| 113 | 
            -
             | 
| 114 | 
            -
            ---
         | 
| 115 | 
            -
             | 
| 116 | 
            -
            - **Prev:** [Basics - Context](context.md)
         | 
| 117 | 
            -
            - **Next:** [Interruptions - Halt](../interruptions/halt.md)
         |