Create a gist now

Instantly share code, notes, and snippets.

@maxivak /00.md
Last active Mar 26, 2017

Sending emails with ActionMailer and Sidekiq

Sending emails with ActionMailer and Sidekiq

Send email asynchroniously using Sidekiq.

ActionMailer

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

    

Sidekiq

Gemfile:

gem 'sidekiq'

Install Redis

Redis provides data storage for Sidekiq. It holds all the job data along with runtime and historical data

Configure Sidekiq

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

Configure Sidekiq

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

Sidekiq and ActionMailer (ActionJob)

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

Sidekiq and Devise

Read this: https://github.com/mperham/sidekiq/wiki/Devise

Run Sidekiq

bundle exec sidekiq --environment development -C config/sidekiq.yml 

God + Sidekiq

Use god for monitoring and running sidekiq automatically: https://gist.github.com/maxivak/05847dc7f558d5ef282e

RSpec tests

RSpec tests for ActionMailer

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
    
    

RSpec tests for Sidekiq

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment