Project

cmdx

0.0
No release in over a year
CMDx is a framework for building maintainable business processes.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

Runtime

 Project Readme
CMDx Light Logo CMDx Dark Logo

Build business logic that’s powerful, predictable, and maintainable.

Home · Documentation · Blog · Changelog · Report Bug · Request Feature · AI Skills · llms.txt · llms-full.txt

Version Build License

CMDx

Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.

Note

Documentation reflects the latest code on main. For version-specific documentation, refer to the docs/ directory within that version's tag.

What you get

  • Standardized task contract — typed inputs, declared outputs, explicit halts
  • Type system — 13 coercers, 7 validators, all pluggable
  • Built-in flow controlskip! / fail! / throw! with structured metadata
  • Retries and faults — declarative retry_on with configurable jitter
  • Middleware and callbacks — wrap the lifecycle without touching work
  • Observability — structured logs and telemetry, no extra instrumentation
  • Composable workflows — chain tasks into larger processes

See the feature comparison for how CMDx stacks up against other service-object gems.

Requirements

  • Ruby: MRI 3.3+ or a compatible JRuby/TruffleRuby release
  • Runtime dependencies: bigdecimal and logger (stdlib only — no ActiveSupport required)

Rails support is built-in, but CMDx is framework-agnostic at its core.

Installation

gem install cmdx
# - or -
bundle add cmdx

Quick Example

CMDx organizes business logic around the CERO pattern (pronounced "zero"): Compose, Execute, React, Observe.

1. Compose

Declare inputs, outputs, retries, and callbacks, then implement work.

class AnalyzeMetrics < CMDx::Task
  retry_on Net::ReadTimeout, limit: 3, jitter: :exponential

  on_success :track_analysis_completion!

  required :dataset_id, coerce: :integer, numeric: { min: 1 }

  optional :analysis_type, default: "standard"

  output :result, :analyzed_at

  def work
    if dataset.nil?
      fail!("Dataset not found", code: 404)
    elsif dataset.unprocessed?
      skip!("Dataset not ready for analysis")
    else
      context.result = PValueAnalyzer.execute(dataset:, analysis_type:)
      context.analyzed_at = Time.now

      SendAnalyzedEmail.execute(user_id: Current.account.manager_id)
    end
  end

  private

  def dataset
    @dataset ||= Dataset.find_by(id: dataset_id)
  end

  def track_analysis_completion!
    dataset.update!(analysis_result_id: context.result.id)
  end
end

2. Execute

Every invocation returns a Result. Inputs are coerced and validated, exceptions are captured, outputs are verified, and the outcome is logged — automatically.

result = AnalyzeMetrics.execute(
  dataset_id: 123,
  "analysis_type" => "advanced"
)

Use execute! instead when you want failures to raise a Fault.

3. React

Branch on the result's status and read values, reasons, or metadata from it.

if result.success?
  puts "Metrics analyzed at #{result.context.analyzed_at}"
elsif result.skipped?
  puts "Skipped: #{result.reason}"
elsif result.failed?
  puts "Failed: #{result.reason} (code #{result.metadata[:code]})"
end

4. Observe

Every execution emits a structured log line with the chain id, task identity, state, status, reason, metadata, and duration — enough to correlate nested tasks and reconstruct what happened.

I, [2026-04-19T18:42:37.000000Z #3784] INFO -- cmdx: cid="018c2b95-b764-7fff-a1d2-..." index=1 root=false type="Task" task=SendAnalyzedEmail id="018c2b95-c091-..." state="complete" status="success" reason=nil metadata={} duration=34.7 tags=[]

I, [2026-04-19T18:43:15.000000Z #3784] INFO -- cmdx: cid="018c2b95-b764-7fff-a1d2-..." index=0 root=true type="Task" task=AnalyzeMetrics id="018c2b95-b764-..." state="complete" status="success" reason=nil metadata={} duration=187.4 tags=[]

Ready to dive in? Check out the Getting Started guide.

Ecosystem

Contributing

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.

License

The gem is available as open source under the terms of the LGPLv3 License.