Curated library of atomic AI agent skills for Hanami, dry-rb, and ROM Ruby development. Covers actions, slices, repositories, relations, changesets, providers, DI, operations, TDD, CLI, views, routing, and validation. Shared Ruby process skills have moved to ruby-core-skills. Uses Markdown + Front-matter architecture.
92
94%
Does it follow best practices?
Impact
92%
1.33xAverage score across 35 eval scenarios
Passed
No known issues
Use this skill when creating or reviewing Hanami 2.x Actions.
Core principle: One Action = one HTTP endpoint.
| Scenario | Workflow Step |
|---|---|
| Create a new Action | Step 1: Generate |
| Validate / coerce params | Step 2: Params block |
| Implement request logic | Step 3: #handle |
| Inject dependencies | Step 4: Deps[] |
| Handle errors / halt | Step 5: Error handling |
| Set HTTP status codes | Step 6: Status codes |
| Test the action | Step 7: Request spec |
Generate the Action file. One Action per file — generate Index, Show, Create, Update, Destroy as separate classes:
hanami generate action Users::IndexDefine a Params block to validate and coerce input. Invalid params should halt 422 before any business logic runs:
class Create < MyApp::Action
params do
required(:user).hash do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
def handle(request, response)
halt 422, { errors: request.params.errors.to_h }.to_json unless request.params.valid?
# proceed with valid params
end
endImplement #handle — Actions are HTTP handlers only; delegate all business logic to repositories, interactors, or service objects. Actions have only #handle; never use instance variables for state sharing between methods:
# app/actions/users/index.rb
# frozen_string_literal: true
module MyApp
module Actions
module Users
class Index < MyApp::Action
include Deps["repos.user_repo"]
def handle(request, response)
response.render(view, users: user_repo.all)
end
end
end
end
endInject dependencies via Deps — never access the container directly (Hanami.app['key'] is untestable and forbidden):
include Deps["repos.user_repo", "views.users.show"]Wire error handling — use halt for early returns. Log errors and return meaningful responses; never swallow exceptions silently:
def handle(request, response)
user = user_repo.by_id(request.params[:id]).one
halt 404, { error: "User not found" }.to_json unless user
response.render(view, user: user)
endSet appropriate HTTP status codes where they differ from defaults:
201 for successful creates204 for successful deletes with no body422 for validation errorsTo return JSON: response.format = :json; response.body = data.to_json
To redirect: response.redirect_to("/path")
Validate with a request spec (see write-request-spec skill) before considering the action complete.
| Related Skill | When to chain |
|---|---|
| create-repository | Actions inject Repositories to fetch and persist data. |
| create-view | Actions render Views by passing exposures. |
| validate-params | Actions define Params blocks to validate input. |
| handle-errors | Actions use halt for early returns and error responses. |
| inject-dependencies | Actions use include Deps[] for dependency injection. |
| write-request-spec (testing) | Test Actions via Rack request specs. |
docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
skills
actions
build-json-api
create-action
handle-errors
validate-params
context
load-context
db
create-changeset
create-repository
define-relation
write-migration
dry-monads
handle-result-pattern
dry-rb
create-operation
create-validation-contract
providers
configure-providers
implement-di
review-security
routing
define-routes
slices
configure-slice
create-slice
extract-slice
review-slice-boundaries
test-slice