about summary refs log tree commit diff
path: root/app/models/user.rb
diff options
context:
space:
mode:
authorThibaut Girka <thib@sitedethib.com>2020-04-15 17:07:49 +0200
committerThibaut Girka <thib@sitedethib.com>2020-04-15 17:07:49 +0200
commit66ad30db489ddb2b3ecc98d284aabd47c9d4ae84 (patch)
treed8f035139c20e59c39bb35a74686783094c297da /app/models/user.rb
parent615073df33b9a4317ac5c93fdef83c9cd34567fa (diff)
parent5524258da9bc1d62b1396d19c672d3a8335ffbc5 (diff)
Merge branch 'master' into glitch-soc/merge-upstream
Conflicts:
- `package.json`:
  No real conflict, upstream updated a dependency which is
  textually adjacent to a glitch-soc-specific dependency.
  Updated it.
- `yarn.lock`:
  No real conflict, upstream updated a dependency which is
  textually adjacent to a glitch-soc-specific dependency.
  Updated it.
Diffstat (limited to 'app/models/user.rb')
-rw-r--r--app/models/user.rb30
1 files changed, 29 insertions, 1 deletions
diff --git a/app/models/user.rb b/app/models/user.rb
index cd8a6f273..c8dbd2fd3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -98,6 +98,7 @@ class User < ApplicationRecord
 
   before_validation :sanitize_languages
   before_create :set_approved
+  after_commit :send_pending_devise_notifications
 
   # This avoids a deprecation warning from Rails 5.1
   # It seems possible that a future release of devise-two-factor will
@@ -307,11 +308,38 @@ class User < ApplicationRecord
   protected
 
   def send_devise_notification(notification, *args)
-    devise_mailer.send(notification, self, *args).deliver_later
+    # This method can be called in `after_update` and `after_commit` hooks,
+    # but we must make sure the mailer is actually called *after* commit,
+    # otherwise it may work on stale data. To do this, figure out if we are
+    # within a transaction.
+    if ActiveRecord::Base.connection.current_transaction.try(:records)&.include?(self)
+      pending_devise_notifications << [notification, args]
+    else
+      render_and_send_devise_message(notification, *args)
+    end
   end
 
   private
 
+  def send_pending_devise_notifications
+    pending_devise_notifications.each do |notification, args|
+      render_and_send_devise_message(notification, *args)
+    end
+
+    # Empty the pending notifications array because the
+    # after_commit hook can be called multiple times which
+    # could cause multiple emails to be sent.
+    pending_devise_notifications.clear
+  end
+
+  def pending_devise_notifications
+    @pending_devise_notifications ||= []
+  end
+
+  def render_and_send_devise_message(notification, *args)
+    devise_mailer.send(notification, self, *args).deliver_later
+  end
+
   def set_approved
     self.approved = open_registrations? || valid_invitation? || external?
   end