register-provider
Use this skill when registering external dependencies (database, mailer, cache, third-party APIs) in the Hanami 2.x DI container.
Core Rules
Generate a provider using the Hanami CLI:
hanami generate provider mailerThis creates
config/providers/mailer.rb.Implement the provider using lifecycle hooks: Use
preparefor requiring dependencies andstartfor component initialization. Keep providers focused on a single external dependency or library.# config/providers/mailer.rb # frozen_string_literal: true Hanami.app.register_provider(:mailer) do prepare do require "mail" end start do client = Mail.new do delivery_method :smtp, { address: target[:settings].smtp_host, port: target[:settings].smtp_port } end register("mailer.client", client) end endValidation checkpoint: After implementing the provider, verify it loads correctly before writing consuming code:
# In `hanami console` Hanami.app["mailer.client"] # => should return the registered instance without errorsIf this raises or returns
nil, fix the provider before proceeding.Access provided components via
Deps:# app/mailers/welcome.rb module MyApp module Mailers class Welcome include Deps["mailer.client"] def deliver(user) client.deliver do to user.email subject "Welcome!" end end end end endUse the built-in database provider: The ROM container is automatically registered at boot by the framework:
include Deps["db.rom"]Register third-party API clients using settings: Always load keys and URLs through
target[:settings]. Do not reference raw environment variables viaENVin providers.Rescue and log errors in
startto control boot failure behavior. Choose one of two strategies:- Swallow the error (register a null/fallback object) if the service is optional and the app should still boot without it.
- Re-raise the error if the service is required and a missing provider should halt boot.
# config/providers/storage.rb Hanami.app.register_provider(:storage) do prepare do require "aws-sdk-s3" end start do client = Aws::S3::Client.new( access_key_id: [REDACTED:API key param], secret_access_key: target[:settings].cloud_storage_secret, region: target[:settings].cloud_storage_region ) register("storage.client", client) rescue StandardError => e target[:logger].error("[provider:storage] failed to start: #{e.message}") # Re-raise if this provider is required for the app to function: raise # Or register a null object and swallow if the service is optional: # register("storage.client", NullStorageClient.new) end endTest components that depend on providers by stubbing the provided dependency:
stub_mailer = double("mailer", deliver: true) welcome = MyApp::Mailers::Welcome.new(mailer__client: stub_mailer)Verify a provider is correctly registered using the Hanami console or a smoke test:
# In `hanami console` Hanami.app["mailer.client"] # => returns the registered instanceFor a lightweight smoke test in specs:
it "registers the mailer client" do expect(Hanami.app["mailer.client"]).to be_a(Mail::Message) end
Integration
| Related Skill | When to chain |
|---|---|
| inject-dependencies | Provided components are injected via Deps[]. Understand Deps before writing providers. |
| manage-settings | Providers read configuration from target[:settings]. Define settings before writing providers. |
| create-action | Actions inject provided services via Deps[]. |
| create-repository | The database provider registers ROM, which Repositories depend on. |
| integrate-api-client | Complex API clients may need a dedicated skill for auth/patterns. |