Curated library of atomic skills and personas for Hanami, dry-rb, and ROM Ruby development. Covers actions, slices, repositories, relations, changesets, providers, DI, operations, TDD, CLI, views, routing, validation, and 10 orchestration personas. Shared Ruby process skills have moved to ruby-core-skills. Uses Markdown + Front-matter architecture.
95
95%
Does it follow best practices?
Impact
96%
1.20xAverage score across 45 eval scenarios
Passed
No known issues
Use this skill when defining ROM Relations that map to database tables in Hanami 2.x.
Core principle: The Relation is the query layer. It defines how to read and filter data, not business logic.
| Scenario | Approach |
|---|---|
| Define a Relation | Class inherits from Hanami::DB::Relation with schema :table_name, infer: true |
| Infer schema from DB | schema :table_name, infer: true |
| Define explicit schema | schema :table_name do { attribute :id, Types::Integer } end |
| Add a custom query method | Define a method that returns a restricted/ordered relation |
| Define an association | many_to_one :users, as: :user or one_to_many :posts, as: :posts |
| Access the Relation in an Action | include Deps["relations.users"] |
| Call a query method | relations.users.active or relations.users.by_email("a@b.com") |
| Verify inferred schema | bundle exec hanami console → app['relations.users'].schema |
Create the Relation file in the app or slice:
# app/relations/users.rb
# frozen_string_literal: true
module MyApp
module Relations
class Users < Hanami::DB::Relation
schema :users, infer: true
end
end
endUse infer: true for new tables where the database schema is the source of truth. ROM will introspect the table at boot.
Define explicit schema when you need custom type coercion or virtual attributes:
schema :users do
attribute :id, Types::Integer
attribute :email, Types::String
attribute :created_at, Types::Time
endAdd query methods for reusable filters. Encapsulate all query filters inside public relation methods — keep business logic, validations, and side effects out of Relations:
def active
where(status: "active")
end
def by_email(email)
where(email: email)
endDefine associations: Use the association DSL macros for basic and advanced linkage between tables:
associations do
# Basic cases
many_to_one :users, as: :author
one_to_many :posts, as: :posts
# Aliased foreign key
many_to_one :users, foreign_key: :author_id, as: :author
# Nested / through association
one_to_many :comments, through: :posts
endLoading strategies: use combine to eagerly load associations in a single composed query, or load to issue a separate query:
# In a repository or relation method:
users.combine(:posts) # eager-load posts into each user struct
users.combine(posts: :comments) # nested eager-loadKeep Relations in sync with migrations:
If not using infer: true, manually sync explicit schema declarations when migrations alter the database. Verify using the console:
bundle exec hanami console
# check app["relations.users"].schemaIf schema mismatch or boot failure: check migration status → run pending migrations (bundle exec hanami db migrate) → restart the console → re-verify the schema. If infer: true fails at boot, confirm the table exists in the database and that the connection configuration is correct.
| Mistake | Resolution |
|---|---|
| Redundant declarations | Do not declare explicit attributes for columns that are already auto-inferred (causes boot errors). |
| Direct View access | Bypassing the Repository layer to query relations directly in views is an anti-pattern. |
| Singular class names | Relations map to plural database tables and must be plural: Users, not User. |
For detailed relation pattern examples, see RELATIONS.md.
| Related Skill | When to chain |
|---|---|
| write-migration | After any migration that changes the schema — verify Relation still matches |
| create-repository | When you need domain-level read/write operations that wrap Relations |
| define-entity | When defining the data structures returned by Relations |
| write-rom-spec (testing) | When writing tests for Relation query methods |
| build-crud-resource | Full end-to-end: Relation → Repository → Action → View → Test |
.tessl-plugin
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
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
scenario-42
scenario-43
scenario-44
scenario-45
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
personas
providers
configure-providers
implement-di
review-security
routing
define-routes
slices
configure-slice
create-slice
extract-slice
review-slice-boundaries
test-slice