This skill should be used when the user asks about "background jobs", "Active Job", "perform_later", "perform_now", "Sidekiq", "queue adapters", "job retries", "job callbacks", "async tasks", "scheduled jobs", "delayed jobs", "job queues", or needs guidance on background processing in Rails applications.
/plugin marketplace add bastos/rails-plugin/plugin install bastos-ruby-on-rails@bastos/rails-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Comprehensive guide to background job processing in Rails with Active Job.
rails generate job ProcessOrder
# app/jobs/process_order_job.rb
class ProcessOrderJob < ApplicationJob
queue_as :default
def perform(order)
# Process the order
order.process!
OrderMailer.confirmation(order).deliver_now
end
end
# Enqueue to run as soon as possible
ProcessOrderJob.perform_later(order)
# Enqueue multiple jobs at once
ActiveJob.perform_all_later(
ProcessOrderJob.new(order1),
ProcessOrderJob.new(order2)
)
# Run at specific time
ProcessOrderJob.set(wait_until: Date.tomorrow.noon).perform_later(order)
# Run after delay
ProcessOrderJob.set(wait: 1.hour).perform_later(order)
ProcessOrderJob.set(wait: 5.minutes).perform_later(order)
# Run immediately (blocking, no queue)
ProcessOrderJob.perform_now(order)
class ProcessOrderJob < ApplicationJob
queue_as :high_priority
end
# Or dynamically
ProcessOrderJob.set(queue: :urgent).perform_later(order)
class LowPriorityJob < ApplicationJob
queue_as :default
queue_with_priority 50 # Higher number = lower priority
end
# Dynamic priority
ImportantJob.set(priority: 10).perform_later(data)
# config/application.rb or config/environments/*.rb
config.active_job.queue_adapter = :sidekiq
| Adapter | Best For | Notes |
|---|---|---|
:solid_queue | Rails 8+ default | Database-backed, no Redis needed |
:sidekiq | Production | Fast, Redis-based, web UI |
:good_job | PostgreSQL apps | Uses existing DB |
:delayed_job | Simple apps | ActiveRecord-based |
:resque | Legacy apps | Redis-based |
:async | Development | In-process, not for production |
:inline | Testing | Runs immediately |
:test | Testing | Stores jobs for assertions |
# Gemfile
gem 'sidekiq'
# config/application.rb
config.active_job.queue_adapter = :sidekiq
# config/sidekiq.yml
:concurrency: 5
:queues:
- critical
- default
- low
class ExternalApiJob < ApplicationJob
retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 5
retry_on CustomTransientError, wait: 5.seconds, attempts: 3
def perform(record)
ExternalApi.sync(record)
end
end
| Option | Description |
|---|---|
wait | Seconds to wait (:polynomially_longer for backoff) |
attempts | Number of retry attempts |
queue | Queue for retried job |
priority | Priority for retried job |
class DataImportJob < ApplicationJob
discard_on ActiveJob::DeserializationError
discard_on CustomPermanentError
def perform(record_id)
record = Record.find(record_id)
# ...
end
end
class ProcessOrderJob < ApplicationJob
rescue_from(PaymentError) do |exception|
order = arguments.first
order.mark_payment_failed!(exception.message)
OrderMailer.payment_failed(order).deliver_later
end
def perform(order)
PaymentService.charge(order)
end
end
class ImportJob < ApplicationJob
before_enqueue :log_enqueue
after_enqueue :notify_enqueued
before_perform :setup
around_perform :measure_time
after_perform :cleanup
after_discard :log_discard
private
def log_enqueue
Rails.logger.info "Enqueueing import job"
end
def setup
@start_time = Time.current
end
def measure_time
start = Time.current
yield
duration = Time.current - start
Rails.logger.info "Job completed in #{duration}s"
end
def cleanup
# Clean up resources
end
def log_discard(job, exception)
Rails.logger.error "Job discarded: #{exception.message}"
end
end
Active Job automatically serializes:
# ActiveRecord models are serialized via GlobalID
class NotifyUserJob < ApplicationJob
def perform(user)
# user is automatically deserialized from GlobalID
UserNotifier.send_notification(user)
end
end
NotifyUserJob.perform_later(User.find(1))
# Serialized as: "gid://myapp/User/1"
# app/serializers/money_serializer.rb
class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
def serialize(money)
super("amount" => money.amount, "currency" => money.currency)
end
def deserialize(hash)
Money.new(hash["amount"], hash["currency"])
end
private
def klass
Money
end
end
# config/initializers/active_job.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
# Async email delivery
UserMailer.welcome(user).deliver_later
UserMailer.welcome(user).deliver_later(wait: 1.hour)
UserMailer.welcome(user).deliver_later(wait_until: Date.tomorrow.noon)
# Sync delivery (use in jobs)
UserMailer.welcome(user).deliver_now
# test/jobs/process_order_job_test.rb
require "test_helper"
class ProcessOrderJobTest < ActiveJob::TestCase
test "processes the order" do
order = orders(:pending)
ProcessOrderJob.perform_now(order)
assert order.reload.processed?
end
test "enqueues job" do
order = orders(:pending)
assert_enqueued_with(job: ProcessOrderJob, args: [order]) do
ProcessOrderJob.perform_later(order)
end
end
test "sends confirmation email" do
order = orders(:pending)
assert_enqueued_emails 1 do
ProcessOrderJob.perform_now(order)
end
end
end
# spec/jobs/process_order_job_spec.rb
require "rails_helper"
RSpec.describe ProcessOrderJob, type: :job do
let(:order) { create(:order) }
it "processes the order" do
described_class.perform_now(order)
expect(order.reload).to be_processed
end
it "is enqueued to correct queue" do
expect {
described_class.perform_later(order)
}.to have_enqueued_job.on_queue("default")
end
it "retries on transient errors" do
allow(ExternalService).to receive(:call).and_raise(Net::OpenTimeout)
expect {
described_class.perform_now(order)
}.to have_enqueued_job(described_class)
end
end
perform_now - Test job logic synchronouslyThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.