CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/hanakai-yaku

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

1.20x
Quality

95%

Does it follow best practices?

Impact

96%

1.20x

Average score across 45 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdskills/personas/build-crud-resource/

name:
build-crud-resource
license:
MIT
type:
persona
description:
Use when implementing a full CRUD resource in Hanami 2.x, including when asked to scaffold, generate a resource, create a REST endpoint, or build a new API resource. Chains entity, relation, repository, action, view, write-request-spec, and review-code to build the complete data-to-HTTP pipeline.
metadata:
{"ecosystem_sources":["hanami/hanami","rom-rb/rom"],"tags":["personas","crud","resources","full-stack"],"version":"1.0.0"}

build-crud-resource

Use this workflow when implementing a full CRUD resource (Create, Read, Update, Delete) in Hanami 2.x.

Core principle: CRUD resources follow a predictable pipeline: data layer → domain layer → HTTP layer → presentation layer.


Pipeline

StepSkillKey ActionsHandoff ConditionIf Step Fails
1. Define Entitydefine-entityCreate Entity class with dry-types attributesEntity class exists and is validCheck dry-types attribute definitions; ensure module nesting matches app namespace
2. Run Migrationwrite-migrationGenerate migration; run hanami db migrateMigration applied, schema up-to-dateCheck for schema conflicts; roll back with hanami db rollback and revise
3. Define Relationdefine-relationschema :table_name, infer: true; add query methodsRelation queries work in consoleConfirm table name matches migration; re-run migration if schema is stale
4. Define Repositorycreate-repositoryImplement all, by_id, create, update, delete; auto_struct trueRepository methods return EntitiesVerify relation is registered in ROM container; check auto_struct setting
5. Create Actionscreate-actionIndex, Show, Create, Update, Destroy; inject Repo via Deps; validate params; handle errorsAll Actions respond to HTTP requestsCheck DI key spelling; confirm routes are registered in config/routes.rb
6. Create Viewscreate-viewexpose data points; templates in app/templates/Views render without errorsVerify expose keys match template variable names
7. Write Testswrite-request-specHappy paths + error cases (404, 422); run full suiteAll tests passFix failing specs before proceeding; do not skip to review with red tests
8. Reviewreview-codeCheck Action responsibility, DI, Repository encapsulation, test coverageNo critical violationsAddress critical violations before marking resource complete

Tip: Run bundle exec rspec spec/requests/ after Steps 5 and 7 to catch regressions early. If tests fail at any point, fix them before advancing to the next step.


Expected File Structure

After completing all steps, the resource should produce this layout (example: users):

app/
  entities/
    user.rb                        # Step 1
  db/
    migrate/
      YYYYMMDDHHMMSS_create_users.rb  # Step 2
  relations/
    users.rb                       # Step 3
  repositories/
    users_repository.rb            # Step 4
  actions/
    users/
      index.rb                     # Step 5
      show.rb
      create.rb
      update.rb
      destroy.rb
  views/
    users/
      index.rb                     # Step 6
      show.rb
  templates/
    users/
      index.html.erb
      show.html.erb
spec/
  requests/
    users_spec.rb                  # Step 7

Code Examples

Step 1 — Entity (app/entities/user.rb)

module MyApp
  module Entities
    class User < Hanami::Entity
      attribute :id,    Types::Integer
      attribute :name,  Types::String
      attribute :email, Types::String
    end
  end
end

Step 3 — Relation (app/relations/users.rb)

module MyApp
  module Relations
    class Users < ROM::Relation[:sql]
      schema :users, infer: true

      def by_id(id)
        where(id: id)
      end
    end
  end
end

Step 4 — Repository (app/repositories/users_repository.rb)

module MyApp
  module Repositories
    class UsersRepository < MyApp::Repository[:users]
      def all
        users.to_a
      end

      def by_id(id)
        users.by_id(id).one!
      end

      def create(attrs)
        users.changeset(:create, attrs).commit
      end

      def update(id, attrs)
        users.by_id(id).changeset(:update, attrs).commit
      end

      def delete(id)
        users.by_id(id).delete
      end
    end
  end
end

Step 5 — Action (app/actions/users/index.rb)

module MyApp
  module Actions
    module Users
      class Index < MyApp::Action
        include Deps[repo: "repositories.users_repository"]

        def handle(request, response)
          response.render view, users: repo.all
        end
      end
    end
  end
end

Common Mistakes

MistakeReality
Starting with Actions and working backwardsStart with the data layer (Entity → Relation → Repository), then the HTTP layer.
Skipping the Entity and returning raw hashesAlways define Entities. They are the data contract between layers.
Putting all CRUD in one Action classOne Action per endpoint: Users::Index, Users::Show, etc.
Skipping Views for JSON-only APIsEven JSON APIs benefit from explicit Action structure. Skip Views only if truly API-only.
Writing tests only after everything is implementedFollow the TDD workflow: write failing request specs for each endpoint before implementing.

skills

personas

build-crud-resource

README.md

tile.json