0.0
The project is in a healthy, maintained state
TypedOperation is a command pattern implementation where inputs can be defined with runtime type checks. Operations can be partially applied.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

>= 6.0, < 8.0
~> 0.1.0
 Project Readme

TypedOperation

Coverage RubyCritic

A Command pattern implementation for Ruby with typed parameters, partial application, and operation composition.

Why TypedOperation?

  • Type-safe parameters - Runtime validation using the literal gem
  • Partial application & currying - Build specialized operations from general ones
  • Operation composition - Chain operations together with .then, pipelines, and railway-oriented error handling
  • Result types - Built-in Success/Failure types, or use Dry::Monads
  • Rails integration - Generators and Action Policy authorization support

Installation

Add to your Gemfile:

gem "typed_operation"

For Rails, generate the base operation class:

bin/rails g typed_operation:install

# With optional integrations
bin/rails g typed_operation:install --dry_monads --action_policy

Quick Start

class GreetUser < TypedOperation::Base
  param :name, String
  param :greeting, String, default: "Hello"

  def perform
    "#{greeting}, #{name}!"
  end
end

# Direct invocation
GreetUser.call(name: "World")
# => "Hello, World!"

# Partial application
greeter = GreetUser.with(greeting: "Welcome")
greeter.call(name: "Alice")
# => "Welcome, Alice!"

Parameters

class CreatePost < TypedOperation::Base
  # Positional parameter
  positional_param :title, String

  # Named parameters (keyword arguments)
  param :body, String
  param :author, User

  # Optional with default
  param :status, String, default: "draft"

  # Type coercion
  param :view_count, Integer, &:to_i

  def perform
    Post.create!(title: title, body: body, author: author, status: status)
  end
end

CreatePost.call("My Post", body: "Content", author: current_user)

Operation Composition

Chain operations using .then, .transform, and .or_else:

ValidateOrder
  .then(ProcessPayment)
  .then(SendConfirmation)
  .or_else { |failure| LogError.call(failure) }
  .call(order_id: 123)

Or use the Pipeline DSL for declarative composition:

OrderPipeline = TypedOperation::Pipeline.build do
  step ValidateOrder
  step ProcessPayment
  step SendConfirmation, if: ->(ctx) { ctx[:send_email] }
  on_failure { |error| Logger.error(error) }
end

OrderPipeline.call(order_id: 123, send_email: true)

Result Types

TypedOperation includes built-in Success/Failure types for explicit error handling:

class ProcessPayment < TypedOperation::Base
  include TypedOperation::Result::Mixin  # Built-in, no dependencies

  param :amount, Numeric

  def perform
    result = PaymentGateway.charge(amount)
    result.ok? ? Success(result.id) : Failure(:payment_failed)
  end
end

result = ProcessPayment.call(amount: 100)
result.success?  # => true/false
result.value!    # => the value (raises on failure)

For advanced features like Do notation, use Dry::Monads instead. See Getting Started: Error Handling for details.

Documentation

For complete documentation, visit the TypedOperation documentation site or see the website/docs/ directory:

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/stevegeek/typed_operation.

License

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

Code of Conduct

Everyone interacting in the TypedOperation project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.