Triggering your mailers in the model lifecycle is not recommended IMHO. The recommended approach would be to trigger the mailers from the controller.
If you want to achieve separation of concerns in your controller and not pollute your controller code with mailer calls, you can use a combination of ActiveSupport::Notifications
and controller after_filter
to extract the mailer logic into its own module.
module MailerCallbacks
module ControllerExtensions
def self.included(base)
base.after_filter do |controller|
ActiveSupport::Notifications.instrument(
"mailer_callbacks.#{controller_path}##{action_name}", controller: controller
)
end
end
end
module Listener
def listen_to(action, &block)
ActiveSupport::Notifications.subscribe("mailer_callbacks.#{action}") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
controller = event.payload[:controller]
controller.instance_eval(&block)
end
end
end
end
Let's assume you want to refactor the following controller using our module created above:
class PostsController < ApplicationController
def create
@post = Post.new permitted_params
respond_to do |format|
if @post.save
PostMailer.notify(@post).deliver
format.html { redirect_to @post, notice: 'Successfully created Post' }
else
format.html { render action: 'new' }
end
end
end
end
Take the following steps:
Create an initializer to register the controller extension:
# config/initializers/mailer_callbacks.rb
ActiveSupport.on_load(:action_controller) do
include MailerCallbacks::ControllerExtensions
end
In the same or a separate initializer, create a class and extend the Listener
module to register your callbacks:
# config/initializers/mailer_callbacks.rb
class MailerListeners
extend MailerCallbacks::Listener
# register as many listeners as you would like here
listen_to 'posts#create' do
PostMailer.notify(@post).deliver if @post.persisted?
end
end
Remove mailer code from your controller.
class PostsController < ApplicationController
def create
@post = Post.new permitted_params
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Successfully created Post' }
else
format.html { render action: 'new' }
end
end
end
end
Essentially we have created an observer on the controller actions and registered our mailer callbacks with the controller instead of tying it into the model lifecycle. I personally consider this approach cleaner and easier to manage.