with one click
hanami-operations
// Expert guidance on building, configuring, and testing Hanami Operations
// Expert guidance on building, configuring, and testing Hanami Operations
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | hanami-operations |
| description | Expert guidance on building, configuring, and testing Hanami Operations |
This skill provides expert guidance on building, configuring, and testing Hanami Operations (v2.x). Operations organize business logic as a linear flow of steps, forming the foundation of your app's service layer. They are built on top of dry-operation.
Create an operation
bundle exec hanami generate operation <slice>.<name>App::Operation#call method as the entry point# app/books/create.rb
module Bookshelf
module Books
class Create < Bookshelf::Operation
def call
end
end
end
end
Build a linear flow of steps
step to chain operations togetherSuccess or FailureFailure, execution short-circuits and returns that failure immediatelySuccess, the value is passed to the next stepdef call(attrs)
attrs = step validate(attrs)
book = step create(attrs)
step update_feeds(book)
book
end
Define private step methods
Success(value) or Failure(key, error)private
def validate(attrs)
if valid?(attrs)
Success(attrs)
else
Failure(:invalid, errors: attrs.errors)
end
end
def create(attrs)
Success(book_repo.create(attrs))
end
def update_feeds(book)
Success(feed_service.update(book))
end
Inject dependencies using Deps mixin
include Deps["dependency.key"] to inject services, repos, etc.module Bookshelf
module Books
class Create < Bookshelf::Operation
include Deps["repos.book_repo"]
include Deps["services.feed_service"]
def call(attrs)
attrs = step validate(attrs)
book = step create(attrs)
step update_feeds(book)
book
end
private
def create(attrs)
Success(book_repo.create(attrs))
end
def update_feeds(book)
Success(feed_service.update(book))
end
end
end
end
Access dependencies within step methods
Wrap steps in a transaction block
transaction do ... end to wrap database operationsdef call(attrs)
transaction do
attrs = step validate(attrs)
book = step create(attrs)
step update_feeds(book)
book
end
end
Specify a custom gateway
gateway: option to target a specific database gatewaytransaction(gateway: :other) do
attrs = step validate(attrs)
record = step create(attrs)
record
end
Pattern match on Success/Failure in callers
Success or Failure resultscase create.call(params)
in Success(book)
response.redirect_to routes.path(:book, book.id)
in Failure[:invalid, validation]
response.render view, validation:
end
Check result type
result.success? / result.failure? for type checkingresult.value / result.failure for extracting valuesUse Failure keys for granular error handling
Failure(:key, **kwargs) from stepsCall operations from actions
module Bookshelf
module Actions
class Create < Bookshelf::Action
include Deps["books.create"]
def handle(request, response)
case create.call(request.params[:book])
in Success(book)
response.redirect_to routes.path(:book, book.id)
in Failure[:invalid, validation]
response.render view, validation:
end
end
end
end
end
Pass request data to operations
operation.call(...)Test operations directly
RSpec.describe Books::Create do
subject(:operation) { described_class.new(book_repo:) }
let(:book_repo) { instance_double(BookRepo) }
it "creates a book and returns it" do
allow(book_repo).to receive(:create).with(title: "Test").and_return(book)
result = operation.call(title: "Test")
expect(result).to be_a(Success)
expect(result.value).to eq(book)
end
it "returns failure on invalid input" do
allow(book_repo).to receive(:create)
result = operation.call(title: "")
expect(result).to be_a(Failure)
expect(result.failure).to eq(invalid: {errors: [...]})
end
end
Test transaction behavior
transaction gateway mocking for isolated testsWhen assisting with Hanami Operations tasks, follow this workflow:
Identify the business logic requirements
Design the step flow
Configure dependencies
Handle database transactions
transaction blockIntegrate with callers
Write tests
Review and refine
When detailed information is needed about specific topics, consult the Hanami Operations documentation:
Success or Failurestep for the linear flow; never call step methods directly outside calltransaction blockstransaction blocks:invalid, :not_found)