Use when integrating background jobs, async jobs, or background processing in Hanami 2.x — including Sidekiq integration, GoodJob worker setup, or any job queue configuration. Sets up background job providers, injects job-adapter dependencies into Hanami actions, creates job-triggering actions, and generates corresponding RSpec tests.
설치
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Use when integrating background jobs, async jobs, or background processing in Hanami 2.x — including Sidekiq integration, GoodJob worker setup, or any job queue configuration. Sets up background job providers, injects job-adapter dependencies into Hanami actions, creates job-triggering actions, and generates corresponding RSpec tests.
Handoff condition: Adapter accessible via Deps["jobs.client"]
# config/providers/background_jobs.rbHanami.app.register_provider(:background_jobs) do
start dorequire"sidekiq"Sidekiq.configure_client do |config|
config.redis = { url: target[:settings].redis_url }
end
register("jobs.client", Sidekiq::Client)
endend
Validation: Verify registration with MyApp::App["jobs.client"] in a Hanami console or container lookup spec. It should return Sidekiq::Client without raising Dry::Container::Error.
[Inject into Action] — Load skill: inject-dependencies
Inject job adapter into Action
Handoff condition: Adapter injectable
classCreate < MyApp::ActionincludeDeps["jobs.client", "repos.user_repo"]
end
[Enqueue from Action] — Load skill: create-action
Define job class
Enqueue job from Action after successful operation
Handle enqueue failures gracefully — rescue enqueue errors and log them; do not crash the Action
Handoff condition: Jobs are enqueued and executed
# app/jobs/welcome_email.rbmoduleMyAppmoduleJobsclassWelcomeEmailincludeSidekiq::Workerdefperform(user_id)
user = user_repo.by_id(user_id).one
mailer.deliver(to: user.email, subject:"Welcome!")
endendendend
classCreate < MyApp::Actiondefhandle(request, response)
result = create_user.call(request.params[:user])
case result
inSuccess(user)
jobs.client.push("class" => "MyApp::Jobs::WelcomeEmail", "args" => [user.id])
response.status = 201inFailure(error)
response.status = 422endrescue => e
Hanami.logger.error("Job enqueue failed: #{e.message}")
response.status = 201# Still succeed; enqueue failure is non-fatalendend
Validation: Confirm jobs are being enqueued by checking the Sidekiq dashboard (/sidekiq) or inspecting the Redis queue directly (Sidekiq::Queue.new.size). In tests, use Sidekiq::Testing.fake! and assert on MyApp::Jobs::WelcomeEmail.jobs.
[Write Job Specs] — Load skill: write-action-spec
Test job class in isolation with stubbed dependencies
Test that Action enqueues the job
Handoff condition: Job specs pass
# spec/unit/my_app/jobs/welcome_email_spec.rbRSpec.describe MyApp::Jobs::WelcomeEmaildo
subject(:job) { described_class.new }
let(:user_repo) { instance_double(MyApp::Repos::UserRepo) }
let(:mailer) { instance_double(MyApp::Mailers::Welcome) }
let(:user) { instance_double(MyApp::Entities::User, id:42, email:"user@example.com") }
before do
allow(user_repo).to receive(:by_id).with(42).and_return(double(one: user))
allow(mailer).to receive(:deliver)
job.instance_variable_set(:@user_repo, user_repo)
job.instance_variable_set(:@mailer, mailer)
end
it "delivers a welcome email to the user"do
job.perform(42)
expect(mailer).to have_received(:deliver).with(to:"user@example.com", subject:"Welcome!")
endend# spec/requests/users/create_spec.rb (enqueue assertion)RSpec.describe "POST /users"do
before { Sidekiq::Testing.fake! }
after { Sidekiq::Worker.clear_all }
it "enqueues a WelcomeEmail job on success"do
expect {
post "/users", params: { user: { name:"Alice", email:"alice@example.com" } }
}.to change(MyApp::Jobs::WelcomeEmail.jobs, :size).by(1)
expect(last_response.status).to eq(201)
endend
Common Pitfalls
Synchronous execution of deferrable work — Use jobs for emails, notifications, exports, or anything that can be deferred.
Missing enqueue error handling — Rescue enqueue errors and log them. Do not crash the Action on transient queue failures.
Direct container access in jobs — Inject dependencies via DI container; do not access the container directly inside jobs.
Passing complex objects as job arguments — Jobs serialize to JSON. Pass IDs or simple scalar data, never Ruby objects.
Missing job tests — Always test the job class in isolation and verify the Action enqueues correctly.
Jobs not registered in DI container — The job adapter client must be registered as a provider before it can be injected.