Use when implementing validation with dry-validation contracts, schemas, or form/input/params validation in Hanami 2.x. Injects dependencies, validates request params using dry-validation contracts, handles monadic Success/Failure result patterns, and writes action specs. Chains inject-dependencies, validate-params, handle-result-pattern, and write-action-spec.
インストール
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Use when implementing validation with dry-validation contracts, schemas, or form/input/params validation in Hanami 2.x. Injects dependencies, validates request params using dry-validation contracts, handles monadic Success/Failure result patterns, and writes action specs. Chains inject-dependencies, validate-params, handle-result-pattern, and write-action-spec.
Use this workflow when implementing complex validation with dry-validation in Hanami 2.x.
Core principle: Complex validation belongs in dedicated Contract classes, not inline in Actions.
Core Process
[Define Contract] — Create a dry-validation Contract
Define schema with types and constraints
Add custom rules for cross-field validation
# app/contracts/user_contract.rbmoduleMyAppmoduleContractsclassUserContract < Dry::Validation::Contract
params do
required(:email).value(:string, format?: /\A.+@.+\z/)
required(:password).value(:string, min_size?:8)
required(:password_confirmation).value(:string)
end
rule(:password_confirmation) do
key.failure("must match password") unless value == values[:password]
endendendend
[Register in DI] — Load skill: inject-dependencies
Register Contract in container so it can be injected via Deps
# config/providers/contracts.rbHanami.app.register_provider(:contracts) do
start do
register("contracts.user_contract", MyApp::Contracts::UserContract.new)
endend
[Inject into Action] — Load skill: validate-params
Inject Contract into Action
Call Contract instead of inline params block for complex validation
classCreate < MyApp::ActionincludeDeps["contracts.user_contract"]
defhandle(request, response)
result = contract.call(request.params[:user])
if result.failure?
halt 422, { errors: result.errors.to_h }.to_json
end# ... proceed with valid dataendend
Checkpoint: Before proceeding, run the action spec to verify the contract is correctly injected. If the spec cannot resolve contracts.user_contract, confirm the provider is registered and the container has been booted.
Handle Failure in Action with appropriate HTTP status
# app/operations/create_user.rbclassCreateUserincludeDry::Monads[:result]
defcall(attrs)
user = User.new(attrs)
if user.save
Success(user)
elseFailure(:save_failed)
endendend# app/actions/users/create.rbclassCreate < MyApp::ActionincludeDeps["contracts.user_contract", "operations.create_user"]
defhandle(request, response)
result = user_contract.call(request.params[:user])
halt 422, { errors: result.errors.to_h }.to_json if result.failure?
case create_user.call(result.to_h)
inSuccess(user)
response.status = 201
response.body = { id: user.id }.to_json
inFailure(:save_failed)
halt 500, { error:"Could not save user" }.to_json
endendend
[Write Tests] — Load skill: write-action-spec
Test Contract in isolation with valid and invalid input
Test Action with stubbed Contract results
# spec/contracts/user_contract_spec.rbRSpec.describe MyApp::Contracts::UserContractdo
subject(:contract) { described_class.new }
it "passes with valid input"do
result = contract.call(email:"a@b.com", password:"secret123", password_confirmation:"secret123")
expect(result).to be_success
end
it "fails when passwords do not match"do
result = contract.call(email:"a@b.com", password:"secret123", password_confirmation:"wrong")
expect(result.errors[:password_confirmation]).to include("must match password")
endend
Common Mistakes & Red Flags
Mistake / Red Flag
Correct Approach
Complex validation inline in Actions
Extract to a Contract class. Actions should be thin.
Contract not registered in DI container
Register via provider; inject with Deps["contracts.user_contract"].
Contracts not tested in isolation
Test directly with valid and invalid inputs before wiring into the Action.