CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/hanakai-yaku

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

1.33x
Quality

94%

Does it follow best practices?

Impact

92%

1.33x

Average score across 35 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

CONTRACT_PATTERNS.mdskills/dry-rb/create-validation-contract/

Validation Contract Patterns

Custom Predicates

# config/contracts/predicates.rb
require "dry/validation"

Dry::Validation.register_macro(:email_format) do
  key.failure("must be a valid email") unless value.match?(URI::MailTo::EMAIL_REGEXP)
end

# Usage
rule(:email) { key.email_format }

Nested Schemas

class OrderContract < Dry::Validation::Contract
  schema do
    required(:customer).hash do
      required(:name).filled(:string)
      required(:email).filled(:string)
    end
    required(:items).array(:hash) do
      required(:product_id).filled(:integer)
      required(:quantity).filled(:integer, gt?: 0)
    end
  end
end

Composed Contracts

module Shared
  class PaginationContract < Dry::Validation::Contract
    schema do
      optional(:page).filled(:integer, gt?: 0)
      optional(:per_page).filled(:integer, gt?: 0, lteq?: 100)
    end
  end
end

class Users::IndexContract < Dry::Validation::Contract
  # Compose shared contracts
  config.messages.namespace = :users

  schema do
    optional(:role).filled(:string)
  end

  # Merge pagination schema
  schema do
    import(Shared::PaginationContract.schema)
  end
end

Testing Contracts

RSpec.describe Users::CreateContract do
  subject(:contract) { described_class.new }

  context "with valid input" do
    let(:input) { { email: "test@example.com", name: "Test User" } }

    it "is valid" do
      expect(contract.call(input)).to be_success
    end
  end

  context "with missing email" do
    let(:input) { { name: "Test User" } }

    it "has error on email" do
      result = contract.call(input)
      expect(result).to be_failure
      expect(result.errors.to_h).to include(email: ["is missing"])
    end
  end

  context "with invalid email" do
    let(:input) { { email: "not-an-email", name: "Test" } }

    it "has email format error" do
      result = contract.call(input)
      expect(result.errors.to_h).to include(email: ["must be a valid email"])
    end
  end
end

skills

dry-rb

create-validation-contract

README.md

tile.json