Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [UNRELEASED]

## [2.0.0] - 2026-04-09

### Changed

- **Breaking:** Rebuilt the runtime around immutable `Definition`, per-run `Session`, `Outcome`, and `ExecutionResult`.
- **Breaking:** Removed `Settings`, separate registry classes, `Result`/`Resolver`, `Chain`, `Attribute`/`AttributeValue`, `Parallelizer`, and built-in log formatter types; added `ExtensionSet`, `Trace`, `WorkflowRunner`, `Parallel::Threads`, and `Telemetry`.
- **Breaking:** Middleware uses `call(env, **options) { ... }` with `MiddlewareEnv`.
- **Breaking:** Removed `CMDx::Exception` alias; use `CMDx::Error`.
- **Breaking:** `Definition.for` renamed to `Definition.fetch` for Ruby keyword compatibility.

### Added

- `CMDx.reset_configuration!` for test isolation.
- `docs/v2/V1_AUDIT.md` and `UPGRADING.md`.

## [1.21.0] - 2026-04-09

### Added
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
cmdx (1.21.0)
cmdx (2.0.0)
bigdecimal
logger
zeitwerk
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Say goodbye to messy service objects. CMDx helps you design business logic with
> [!NOTE]
> [Documentation](https://drexed.github.io/cmdx/getting_started/) reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.

> [!IMPORTANT]
> **2.0** rewrote the framework (see [CHANGELOG.md](./CHANGELOG.md) and [UPGRADING.md](./UPGRADING.md)). The published MkDocs site may still describe 1.x until it is regenerated.

## Requirements

- Ruby: MRI 3.1+ or JRuby 9.4+
Expand Down
40 changes: 40 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Upgrading to CMDx 2.0

Version 2.0 is a **breaking** rewrite: same goals (composable command objects, workflows, outcomes), different internals and several API changes. This guide maps concepts, not line-for-line APIs.

## Mental model

- **Definition** replaces `Settings` plus multiple registries. Coercions, validators, and middleware are merged through `ExtensionSet` on each task class.
- **Session** carries `context`, `errors`, `outcome`, and **Trace** (explicit correlation). Thread-local `Chain.current` is gone; pass `trace:` into `execute` when needed.
- **ExecutionResult** is what `execute` returns (not `Result`). Use `success?`, `failed?`, `context`, `outcome`, `trace`.
- **Outcome** holds state/status; `success!` / `skip!` / `fail!` still exist on the task and delegate to the session outcome.

## Middleware

Use Rack-style signatures:

```ruby
module MyMw
def self.call(env, **options)
yield
end
end

register :middleware, MyMw, if: :some_predicate?
```

`env` is `CMDx::MiddlewareEnv` (`session`, `handler`).

## Removed or renamed pieces

- `CMDx::Result`, `CMDx::Resolver`, `CMDx::Chain`, `CMDx::Settings`, registry classes, `Attribute`, `AttributeValue`, `Parallelizer`, built-in log formatters.
- `CMDx::Exception` alias removed; use `CMDx::Error`.
- `Definition.for` was renamed to `Definition.fetch` (Ruby 3.4+ reserves `for` syntax in some positions).

## Configuration

`CMDx.configure` still works. Registries are replaced by `config.extensions` (`ExtensionSet`) and per-class `register`. Use `CMDx.reset_configuration!` in tests to isolate state.

## Further reading

- [docs/v2/V1_AUDIT.md](docs/v2/V1_AUDIT.md) — what changed conceptually.
20 changes: 20 additions & 0 deletions benchmark/cmdx_benchmark.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

require "bundler/setup"
require_relative "../lib/cmdx"

CMDx.configuration.logger = Logger.new(nil)

klass = Class.new(CMDx::Task) do
required :n, type: :integer

def work
context[:out] = n + 1
end
end

n = 20_000
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
n.times { klass.execute(n: 1) }
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
puts format("Task.execute x%<n>d: %<sec>.3fs (%<rps>.1f runs/s)", n: n, sec: elapsed, rps: n / elapsed)
32 changes: 32 additions & 0 deletions docs/v2/V1_AUDIT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# V1 → V2 audit (concepts retained vs dropped)

This document satisfies the v2 plan audit: what v1 provided and what v2 replaces or removes. It is **not** a parity checklist.

## Retained concepts (CERO)

- **Compose**: typed/command objects with declared inputs, optional workflows composing steps.
- **Execute**: single entry (`execute` / `execute!`) returning a rich outcome.
- **React**: predicate API on outcome (`success?`, `failed?`, `skipped?`) and context access.
- **Observe**: trace identifiers, structured telemetry hooks, optional logging.

## Dropped or replaced

| V1 | V2 |
|----|-----|
| `Settings` + five CoW registries | `Definition` + `ExtensionSet` (one merge model) |
| `Result` + `Resolver` + `instance_variable_set` | `Outcome` with explicit transitions |
| `Attribute#task` + dup-per-run | Frozen `AttributeSpec` + `AttributePipeline` |
| `Chain.current` thread/fiber default | `Trace` carried on `Session` (explicit correlation) |
| `CMDx::Exception = Error` | `CMDx::Error` only (no `Exception` alias) |
| Middleware `call(task, **opts, &)` | `call(env, &next)` with `MiddlewareEnv` |
| `Context#method_missing` as default hot path | Hash-backed `Context`; optional accessors |
| `AttributeValue` class | Pipeline stages on `AttributeSpec` |
| Many log formatter classes as core | `Telemetry` / `LogSink` adapter pattern |
| 80+ locale files in core | English defaults in `CMDx::V2::Locale`; optional full locales later |
| `throw(:cmdx_halt)` | `throw(:cmdx_v2_halt)` (internal); documented |

## Optional / deferred

- Full I18n parity for all validator strings (start with English + `I18n.t` when gem present).
- Rack/Rails-specific middleware ports beyond a correlate + runtime example.
- Non-thread parallel backends (protocol only: `Parallel::Backend`).
Loading
Loading