How to Automate Daily Task Emails in Rails using Whenever and Cron



This content originally appeared on DEV Community and was authored by Nemwel Boniface

Background

I have been working on a task assignment system where one of the tasks I need to do is to send everyone at the end of the work day stats of what tasks they had at the start of the work day and what tasks they have been able to close and if there are any new tasks that they were assigned during that work day. Initially, I created a manual method to send emails to them by using a button that I had to click at the end of each workday. This approach worked perfectly well. However, in situations where I was not there physically to click the button to send out the daily tasks reports to all users, this quickly became an issue for me, and coming up with a way to automate this process is no longer an option, but mandatory. This is where the Ruby Whenever gem comes in.

Finding solution for automating email sending with the Whenever Gem

Introduction: Whenever Gem

Whenever Gem is a Ruby DSL(domain-specific language) for managing Unix-style cron jobs. It translates your Ruby syntax into actual crontab entries that your system can use and understand. As a result, it works reliably on Unix-based systems (Linux, macOS). Still, it is not compatible with Windows unless you use a Unix-like environment (such as WSL) or adapt your jobs to Windows Task Scheduler.

The Gem provides a clear syntax for writing and deploying cron jobs. A cron job is a time-based job schedule that allows users to automate tasks by scheduling commands or scripts to run at specific times/ intervals. The scheduled tasks are known as cron jobs and are all defined in a configuration file known as the crontab. A cron is a daemon that runs in the background and checks the crontab file for scheduled tasks to run. Remember that a daemon is a computer program that runs in the background, performing tasks without direct user interaction.

What are schedulers?

Schedulers are components that trigger certain tasks at specific intervals (hourly, daily, weekly, etc). Schedulers operate in the background, outside of the request/ response lifecycle (You do not run them when a user visits your website).

While Rails does not have a native (inbuilt) scheduler for arbitrary cron-style jobs, we have options for using Background jobs using ActiveJobs + Sidekiq. Or asynchronous work and making use of external scheduler tools (such as whenever + sidekiq-scheduler) for repeating jobs.

Prerequisites:

  1. Rails V7
  2. PostgreSQL database
  3. Ruby V 3+
  4. Preferred web browser
  5. Visual studio code
  6. Some experience with Rails MVC architecture

Installing the Whenever Gem

Cat setting up Whenever Gem in the project

Installing the whenever Gem and configuring it to work is fairly straightforward in Ruby on Rails. To install the whenever gem, add the following to your GEMFILE and run bundle install to install the package to your application.:

gem 'whenever', require: false

Optionally run the following in your terminal:

bundle add whenever

Then run the command wheneverize . to create the schedule.rb file inside the config/ directory. This step is very crucial as the schedule.rb file is where you define your cron jobs using Ruby DSL syntax (e.g., every :day, at: ‘5:05pm’). Without this file, the whenever gem has no instructions to generate crontab entries.

If you would like to follow along with me through the article, I created this GitHub repository which you can clone to test out the setup.

Real world practice: Sending out an email every weekday at 5:05 pm

Getting started with a Real world setup of Whenever Gem in the project

This is a relatable situation where you may want to automate the process of sending out emails every workday at 5:05 pm. To do this, we will first create a mailer. In your terminal, head over and type:

rails g mailer DailyMailer

This generates a file in the app/mailers/ directory called daily_mailer.rb, which we are going to open so that we can edit it by adding the following to it:

Class DailyMailer < ApplicationMailer
  def daily_email
    mail (to: "user@gmail.com", subject: "My update"
  end
end

The next step will be to create a view inside the app/views/daily_mailer/ directory called daily_email.html.erb adding this line: <h1>Hello! This is your daily update at 5:05 pm </h1> to it.

We shall then create a Rake task inside the lib/tasks/ creating the file called email.rake, using the command touch lib/tasks/email.rake, and open it to edit it as follows:

namespace :email do
  desc "Your daily email"
  task send_daily_email: :environment do:
    DailyMailer.daily-email.deliver_now
  end
end

We shall now run the command: rails email:send_daily_email, to test out our current setup.

How the Automation Flow Works

Before we test locally with Letter Opener, let’s visualize the full workflow:

Flowchart showing the flow from generating the cron task to when the email is sent

Key Steps showcased in the flowchart include:

  1. schedule.rb which defines the cron schedule in Ruby (e.g., “Weekdays at 5:05 PM”).
  2. crontab system converts this into a background process.
  3. Rake Task which executes your custom logic (e.g., querying tasks).
  4. Mailer which formats and sends the email.
  5. Letter Opener (Dev) / SMTP (Prod) which handles delivery of your email.

Integrating the email send automation with the Whenever Gem

We are now going to ensure that after every work day at 5:05 pm, users can receive an email sent to them. Open the config/schedule.rb file and inside it add:

every :week, at: '5:05 pm', on: [:monday, :tuesday, :wednesday, :thursday, :friday] do
  rake "email:send_daily_email"
end

To be able to check the output, run the command whenever in your terminal. In case nothing happens on your end, not to worry. It means that you do not have any SMTP server configured, and this is where we will resolve it in the next section of the article, the letter_opener gem.

Local testing using the letter_opener gem

In case you do not have an email SMTP setup locally, we will use the letter_opener Gem that catches any email that we want to send. Instead of trying to connect to an SMTP server, it renders the email in our browser and is an amazing tool for testing, as it will show your email like an HTML page for you. This means that:

  1. We do not have to configure an email server
  2. We can see and test emails quickly
  3. We do not send any actual emails over the internet

If you are following along upto here, right now when we try to send the email by connecting to localhost:25, it fails because we do not have an SMTP server configured locally. But with the letter opener gem configured, when our app tries to send an email using the mail() or deliver_now(), letter opener intercepts it and saves it as an HTML page. Then it automatically opens the page in your browser for you. The letter opener gem inline allows you to test your mailer easily, with no error messages, no ECONNREFUSED, and you can review your email layout. Amazing right?

How to use the letter opener gem

You will add the gem 'letter_opener' to your Gemfile in the development group, and run bundle install. Open the config/environments/development.rb file and edit it to add the following configurations:

config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :letter_opener
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false

config.action_mailer.default_url_options = { host: "localhost", port: 3000 }

These settings configure Action Mailer to actually send emails in development mode, preview them in the browser using the letter_opener Gem, suppress delivery errors, disable caching for email templates, and generate proper URLs using localhost:3000 as the base. Amazing right?

Once this has been done, run: rails email:send_daily_email in your terminal. If you have followed along so far, your default email browser will be opened, and you will see something similar to what I have below.

HTML output when you run rails email:send_daily_email

Up to this point, it will load the email in the browser as expected. The application will no longer attempt to connect to localhost:25 (SMTP port) and will instead open your browser.

Up to where we are, we have been able to test out our setup, and we all validated that it works perfectly fine.

We have been able to solve the problem and we can now comfortably test our setup locally

Notes about the letter_opener gem:

  1. Is not for production, it is a tool for development testing alone
  2. Helps you test and view emails quickly without sending them explicitly over an SMTP network
  3. Fixes the connection refused error for local testing

Considerations while using the whenever and the letter_opener gems

When you update the schedule.rb file, and you run the command whenever, Rails will automatically assume RAILS_ENV = production by default. So, when you run whenever without specifying any flags, it shows you the schedule tasks for production and not for development. This may mean that you are not going to see any output and you may think that your setup is all wrong. Luckily, we have a few work a rounds for this and below are some options to use when testing out in development mode. See attached screenshot below as an example.

Running Whenever in production mode will just not work

Remember that running the whenever command is essential because it converts your human-readable schedule written in Ruby (in schedule.rb) into raw cron syntax that your system understands. Without this step, your defined jobs won’t actually get scheduled (We mentioned that whenever doesn’t run jobs itself. It simply tells your operating system when and how to run them by updating the crontab). If you skip this step, your background jobs will never run, no matter how well your Rake tasks or mailers are written.

To fix this, we have a few options, starting with the temporary option, which is running the command shown below which will allow you to preview the schedule for development environment.

whenever --set environment=development

The other solution will be:

whenever --update-crontab --set environment=development

To explicitly set the environment in the schedule.rb file at the top of the schedule.rb file, you will add the following:

set :environment, "development"
set :output, "log/cron.log"

After adding this to our schedule.rb file, we will need to run the command whenever --update-crontab With the flag --update-crontab because running whenever alone simply previews what the generated cron entries would look like but it does not install them. To actually schedule your tasks, you must run whenever --update-crontab, which writes those entries into your system’s active crontab.

To confirm whether this was successful run crontab -l in your terminal. If you were s, you should see something like:

Successful write cron entries

Always remember that: the whenever Gem does not send emails itself, it just tells your OS (using cron) to run the command at a specific time. In our case, we want it to run every time at 5:05 pm every workday. The cron service will run RAILS_ENV=development bundle exec rake email:send_daily_email

We are so cool for automating sending of emails

Conclusion

Technology exists to help us work faster and focus on what truly matters. Let it handle repetitive, automatable tasks like sending daily emails at specific times so you can focus on building smarter systems.

By using the whenever gem together with letter_opener, we’ve implemented a powerful and practical scheduling pattern. With this setup, we’ve removed human dependency from a critical workflow and learned how to safely test scheduled jobs in a development environment without the need for a live email server.

This one marks the end of the step by step guide on automating tasks using Whenever Gem and Cron. What will you be automating next in your systems? I would love to hear about that.

This is the end of the automate tasks using Whenever Gem and Cron

I hope this was useful information for you. See you in my next article.


This content originally appeared on DEV Community and was authored by Nemwel Boniface