From f3fca78756c02fb19f75a2a712d80fa712d32229 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sun, 9 Jul 2023 21:06:22 -0400
Subject: [PATCH] Refactor `NotificationMailer` to use parameterization
 (#25718)

---
 app/mailers/notification_mailer.rb            | 71 +++++++++----------
 app/services/notify_service.rb                |  7 +-
 spec/mailers/notification_mailer_spec.rb      | 21 ++++--
 .../previews/notification_mailer_preview.rb   | 29 +++++---
 4 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb
index 7cd3bab1a..277612366 100644
--- a/app/mailers/notification_mailer.rb
+++ b/app/mailers/notification_mailer.rb
@@ -1,83 +1,76 @@
 # frozen_string_literal: true
 
 class NotificationMailer < ApplicationMailer
-  helper :accounts
-  helper :statuses
+  helper :accounts,
+         :statuses,
+         :routing
 
-  helper RoutingHelper
+  before_action :process_params
+  before_action :set_status, only: [:mention, :favourite, :reblog]
+  before_action :set_account, only: [:follow, :favourite, :reblog, :follow_request]
 
-  def mention(recipient, notification)
-    @me     = recipient
-    @user   = recipient.user
-    @type   = 'mention'
-    @status = notification.target_status
+  default to: -> { email_address_with_name(@user.email, @me.username) }
 
+  def mention
     return unless @user.functional? && @status.present?
 
     locale_for_account(@me) do
       thread_by_conversation(@status.conversation)
-      mail to: email_address_with_name(@user.email, @me.username), subject: I18n.t('notification_mailer.mention.subject', name: @status.account.acct)
+      mail subject: default_i18n_subject(name: @status.account.acct)
     end
   end
 
-  def follow(recipient, notification)
-    @me      = recipient
-    @user    = recipient.user
-    @type    = 'follow'
-    @account = notification.from_account
-
+  def follow
     return unless @user.functional?
 
     locale_for_account(@me) do
-      mail to: email_address_with_name(@user.email, @me.username), subject: I18n.t('notification_mailer.follow.subject', name: @account.acct)
+      mail subject: default_i18n_subject(name: @account.acct)
     end
   end
 
-  def favourite(recipient, notification)
-    @me      = recipient
-    @user    = recipient.user
-    @type    = 'favourite'
-    @account = notification.from_account
-    @status  = notification.target_status
-
+  def favourite
     return unless @user.functional? && @status.present?
 
     locale_for_account(@me) do
       thread_by_conversation(@status.conversation)
-      mail to: email_address_with_name(@user.email, @me.username), subject: I18n.t('notification_mailer.favourite.subject', name: @account.acct)
+      mail subject: default_i18n_subject(name: @account.acct)
     end
   end
 
-  def reblog(recipient, notification)
-    @me      = recipient
-    @user    = recipient.user
-    @type    = 'reblog'
-    @account = notification.from_account
-    @status  = notification.target_status
-
+  def reblog
     return unless @user.functional? && @status.present?
 
     locale_for_account(@me) do
       thread_by_conversation(@status.conversation)
-      mail to: email_address_with_name(@user.email, @me.username), subject: I18n.t('notification_mailer.reblog.subject', name: @account.acct)
+      mail subject: default_i18n_subject(name: @account.acct)
     end
   end
 
-  def follow_request(recipient, notification)
-    @me      = recipient
-    @user    = recipient.user
-    @type    = 'follow_request'
-    @account = notification.from_account
-
+  def follow_request
     return unless @user.functional?
 
     locale_for_account(@me) do
-      mail to: email_address_with_name(@user.email, @me.username), subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
+      mail subject: default_i18n_subject(name: @account.acct)
     end
   end
 
   private
 
+  def process_params
+    @notification = params[:notification]
+    @me = params[:recipient]
+    @user = @me.user
+    @type = action_name
+  end
+
+  def set_status
+    @status = @notification.target_status
+  end
+
+  def set_account
+    @account = @notification.from_account
+  end
+
   def thread_by_conversation(conversation)
     return if conversation.nil?
 
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index ad9e6e3d6..06b48d558 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -162,7 +162,12 @@ class NotifyService < BaseService
   end
 
   def send_email!
-    NotificationMailer.public_send(@notification.type, @recipient, @notification).deliver_later(wait: 2.minutes) if NotificationMailer.respond_to?(@notification.type)
+    return unless NotificationMailer.respond_to?(@notification.type)
+
+    NotificationMailer
+      .with(recipient: @recipient, notification: @notification)
+      .public_send(@notification.type)
+      .deliver_later(wait: 2.minutes)
   end
 
   def email_needed?
diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb
index bf364b625..3efb97cb1 100644
--- a/spec/mailers/notification_mailer_spec.rb
+++ b/spec/mailers/notification_mailer_spec.rb
@@ -23,7 +23,8 @@ RSpec.describe NotificationMailer do
 
   describe 'mention' do
     let(:mention) { Mention.create!(account: receiver.account, status: foreign_status) }
-    let(:mail) { described_class.mention(receiver.account, Notification.create!(account: receiver.account, activity: mention)) }
+    let(:notification) { Notification.create!(account: receiver.account, activity: mention) }
+    let(:mail) { prepared_mailer_for(receiver.account).mention }
 
     include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob'
 
@@ -40,7 +41,8 @@ RSpec.describe NotificationMailer do
 
   describe 'follow' do
     let(:follow) { sender.follow!(receiver.account) }
-    let(:mail) { described_class.follow(receiver.account, Notification.create!(account: receiver.account, activity: follow)) }
+    let(:notification) { Notification.create!(account: receiver.account, activity: follow) }
+    let(:mail) { prepared_mailer_for(receiver.account).follow }
 
     include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob'
 
@@ -56,7 +58,8 @@ RSpec.describe NotificationMailer do
 
   describe 'favourite' do
     let(:favourite) { Favourite.create!(account: sender, status: own_status) }
-    let(:mail) { described_class.favourite(own_status.account, Notification.create!(account: receiver.account, activity: favourite)) }
+    let(:notification) { Notification.create!(account: receiver.account, activity: favourite) }
+    let(:mail) { prepared_mailer_for(own_status.account).favourite }
 
     include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob'
 
@@ -73,7 +76,8 @@ RSpec.describe NotificationMailer do
 
   describe 'reblog' do
     let(:reblog) { Status.create!(account: sender, reblog: own_status) }
-    let(:mail) { described_class.reblog(own_status.account, Notification.create!(account: receiver.account, activity: reblog)) }
+    let(:notification) { Notification.create!(account: receiver.account, activity: reblog) }
+    let(:mail) { prepared_mailer_for(own_status.account).reblog }
 
     include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob'
 
@@ -90,7 +94,8 @@ RSpec.describe NotificationMailer do
 
   describe 'follow_request' do
     let(:follow_request) { Fabricate(:follow_request, account: sender, target_account: receiver.account) }
-    let(:mail) { described_class.follow_request(receiver.account, Notification.create!(account: receiver.account, activity: follow_request)) }
+    let(:notification) { Notification.create!(account: receiver.account, activity: follow_request) }
+    let(:mail) { prepared_mailer_for(receiver.account).follow_request }
 
     include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob'
 
@@ -103,4 +108,10 @@ RSpec.describe NotificationMailer do
       expect(mail.body.encoded).to match('bob has requested to follow you')
     end
   end
+
+  private
+
+  def prepared_mailer_for(recipient)
+    described_class.with(recipient: recipient, notification: notification)
+  end
 end
diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb
index 214161881..a63c20c27 100644
--- a/spec/mailers/previews/notification_mailer_preview.rb
+++ b/spec/mailers/previews/notification_mailer_preview.rb
@@ -5,31 +5,40 @@
 class NotificationMailerPreview < ActionMailer::Preview
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/mention
   def mention
-    m = Mention.last
-    NotificationMailer.mention(m.account, Notification.find_by(activity: m))
+    activity = Mention.last
+    mailer_for(activity.account, activity).mention
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow
   def follow
-    f = Follow.last
-    NotificationMailer.follow(f.target_account, Notification.find_by(activity: f))
+    activity = Follow.last
+    mailer_for(activity.target_account, activity).follow
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow_request
   def follow_request
-    f = Follow.last
-    NotificationMailer.follow_request(f.target_account, Notification.find_by(activity: f))
+    activity = Follow.last
+    mailer_for(activity.target_account, activity).follow_request
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/favourite
   def favourite
-    f = Favourite.last
-    NotificationMailer.favourite(f.status.account, Notification.find_by(activity: f))
+    activity = Favourite.last
+    mailer_for(activity.status.account, activity).favourite
   end
 
   # Preview this email at http://localhost:3000/rails/mailers/notification_mailer/reblog
   def reblog
-    r = Status.where.not(reblog_of_id: nil).first
-    NotificationMailer.reblog(r.reblog.account, Notification.find_by(activity: r))
+    activity = Status.where.not(reblog_of_id: nil).first
+    mailer_for(activity.reblog.account, activity).reblog
+  end
+
+  private
+
+  def mailer_for(account, activity)
+    NotificationMailer.with(
+      recipient: account,
+      notification: Notification.find_by(activity: activity)
+    )
   end
 end