Send email asynchroniously using Sidekiq.
Create your mailer us usual:
# app/mailers/users_mailer.rb
class UsersMailer < ActionMailer::Base
def welcome_email(user_id)
@user = User.find(user_id)
mail( :to => @user.email,
:subject => "Welcome"
) do |format|
format.text
format.html
end
end
end
Views for email:
app/views/users_mailer/welcome_email.html.erb - HTML version
app/views/users_mailer/welcome_email.text.erb - TEXT version
Send email:
user = User.find(1)
mail = UsersMailer.welcome_email(user.id)
#mail.deliver_now
mail.deliver_later
Gemfile:
gem 'sidekiq'
Redis provides data storage for Sidekiq. It holds all the job data along with runtime and historical data
To make #deliver_later
work we need to tell ActiveJob to use Sidekiq.
As long as Active Job is setup to use Sidekiq we can use #deliver_later.
# config/initializers/active_job.rb
config.active_job.queue_adapter = :sidekiq
Environment file:
# config/environments/development.rb
Rails.application.configure do
...
config.active_job.queue_adapter = :sidekiq
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { ... }
end
Read more about ActionJob and Sidekiq: https://github.com/mperham/sidekiq/wiki/Active-Job
config/sidekiq.yml:
---
:concurrency: 1
:queues:
- default
- mailers
Specify Redis namespace for different environments:
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379/0', namespace: "app3_sidekiq_#{Rails.env}" }
end
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://localhost:6379/0', namespace: "app3_sidekiq_#{Rails.env}" }
end
By default, jobs to deliver emails will be placed in queue named :mailers. To change queue name for ActionMailer use this config:
# config/environments/development.rb
config.active_job.queue_adapter = :sidekiq
config.active_job.queue_name_prefix = "mysite"
config.active_job.queue_name_delimiter = "_"
This will use queues named :mysite_mailers
, etc.
!!! important. You may need to include new queue names in sidekiq.yml file:
# config/sidekiq.yml
---
:concurrency: 1
:queues:
- default
- mailers
- mysite_default
- mysite_mailers
Read this: https://github.com/mperham/sidekiq/wiki/Devise
bundle exec sidekiq --environment development -C config/sidekiq.yml
Use god for monitoring and running sidekiq automatically: https://gist.github.com/maxivak/05847dc7f558d5ef282e
In these tests we do not use Sidekiq.
test environment:
# config/environments/test.rb
Rails.application.configure do
...
config.active_job.queue_adapter = :test
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :test
config.action_mailer.raise_delivery_errors = true
Test that deliver_later method was called:
user = User.first
#
message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(UsersMailer).to receive(:welcome_email).with(user.id).and_return(message_delivery)
expect(message_delivery).to receive(:deliver_later)
#
mail = UsersMailer.welcome_email(user.email)
mail.deliver_later
test environment:
# config/environments/test.rb
Rails.application.configure do
...
config.active_job.queue_adapter = :sidekiq
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :test
config.action_mailer.raise_delivery_errors = true
spec/rails_helper.rb:
# sidekiq
require 'sidekiq/testing'
Sidekiq::Testing.fake! # by default it is fake
User Sidekiq::Worker.jobs.size to see the number of jobs in the queue.
Test that email was enqueued:
RSpec.describe "Test sending email with sidekiq", :type => :request do
it 'send email to sidekiq' do
user = User.first
expect{
UsersMailer.welcome_email(user.id).deliver_later
}.to change( Sidekiq::Worker.jobs, :size ).by(1)
end
end
Thanks for this great reference.
Note that redis-namespace is no longer supported. You might want to update that section.