In Stock Email Notifications
Action Mailer is a feature of Rails that allows you to send emails. We’ll use it to notify subscribers when a product is back in stock.
We can generate a mailer with the following command:
$ bin/rails g mailer Product in_stock
This generates a class at with an in_stock
method.
Update this method to mail to a subscriber’s email address.
class ProductMailer < ApplicationMailer # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.product_mailer.in_stock.subject # def in_stock @product = params[:product] mail to: params[:subscriber].email endend
The mailer generator also generates two email templates in our views folder: one for HTML and one for Text. We can update those to include a message and link to the product.
Change to:
<h1>Good news!</h1>
<p><%= link_to @product.name, product_url(@product) %> is back in stock.</p>
And to:
Good news!
<%= @product.name %> is back in stock.<%= product_url(@product) %>
We use product_url
instead of product_path
in mailers because email clients
need to know the full URL to open in the browser when the link is clicked.
We can test an email by opening the Rails console and loading a product and subscriber to send to:
store(dev)> product = Product.firststore(dev)> subscriber = product.subscribers.find_or_create_by(email: "subscriber@example.org")store(dev)> ProductMailer.with(product: product, subscriber: subscriber).in_stock.deliver_later
You’ll see that it prints out an email in the logs.
ProductMailer#in_stock: processed outbound mail in 63.0msDelivered mail 66a3a9afd5d4a_108b04a4c41443@local.mail (33.1ms)Date: Fri, 26 Jul 2024 08:50:39 -0500From: from@example.comTo: subscriber@example.comMessage-ID: <66a3a9afd5d4a_108b04a4c41443@local.mail>Subject: In stockMime-Version: 1.0Content-Type: multipart/alternative; boundary="--==_mimepart_66a3a9afd235e_108b04a4c4136f"; charset=UTF-8Content-Transfer-Encoding: 7bit
----==_mimepart_66a3a9afd235e_108b04a4c4136fContent-Type: text/plain; charset=UTF-8Content-Transfer-Encoding: 7bit
Good news!
T-Shirt is back in stock.http://localhost:3000/products/1
----==_mimepart_66a3a9afd235e_108b04a4c4136fContent-Type: text/html; charset=UTF-8Content-Transfer-Encoding: 7bit
<!-- BEGIN app/views/layouts/mailer.html.erb --><!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <style> /* Email styles need to be inline */ </style> </head>
<body> <!-- BEGIN app/views/product_mailer/in_stock.html.erb --><h1>Good news!</h1>
<p><a href="http://localhost:3000/products/1">T-Shirt</a> is back in stock.</p><!-- END app/views/product_mailer/in_stock.html.erb --> </body></html><!-- END app/views/layouts/mailer.html.erb -->----==_mimepart_66a3a9afd235e_108b04a4c4136f--
Performed ActionMailer::MailDeliveryJob (Job ID: 5e2bd5f2-f54f-4088-ace3-3f6eb15aaf46) from Async(default) in 111.34ms
To trigger these emails, we can use a callback in the Product model to send emails anytime the inventory count changes from 0 to a positive number.
class Product < ApplicationRecord has_many :subscribers, dependent: :destroy has_one_attached :featured_image has_rich_text :description
validates :name, presence: true validates :inventory_count, numericality: { greater_than_or_equal_to: 0 }
after_update_commit :notify_subscribers, if: :back_in_stock?
def back_in_stock? inventory_count_previously_was&.zero? && inventory_count > 0 end
def notify_subscribers subscribers.each do |subscriber| ProductMailer.with(product: self, subscriber: subscriber).in_stock.deliver_later end endend
after_update_commit
is an Active Record callback that is fired after changes
are saved to the database. if: :back_in_stock?
tells the callback to run only
if the back_in_stock?
method returns true.
Active Record keeps track of changes to attributes so back_in_stock?
checks
the previous value of inventory_count
using inventory_count_previously_was
.
Then we can compare that against the current inventory count to determine if the
product is back in stock.
notify_subscribers
uses the Active Record association to query the
subscribers
table for all subscribers for this specific product and then
queues up the in_stock
email to be sent to each of them.
- Preparing Ruby runtime
- Prepare development database