From f0296e2150e6bfadcd73b8353b89ec0c3f438ef4 Mon Sep 17 00:00:00 2001 From: CactusPuppy Date: Wed, 31 Mar 2021 21:08:28 -0700 Subject: [PATCH] Properly fire update_email hook after commit which changes user email --- app/models/concerns/track_saved_changes.rb | 50 ++++++++++++++++++++++ app/models/user.rb | 9 +++- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 app/models/concerns/track_saved_changes.rb diff --git a/app/models/concerns/track_saved_changes.rb b/app/models/concerns/track_saved_changes.rb new file mode 100644 index 00000000..03596050 --- /dev/null +++ b/app/models/concerns/track_saved_changes.rb @@ -0,0 +1,50 @@ +# https://github.com/ccmcbeck/after-commit +module TrackSavedChanges + extend ActiveSupport::Concern + + included do + # expose the details if consumer wants to do more + attr_reader :saved_changes_history, :saved_changes_unfiltered + after_initialize :reset_saved_changes + after_save :track_saved_changes + end + + # on initalize, but useful for fine grain control + def reset_saved_changes + @saved_changes_unfiltered = {} + @saved_changes_history = [] + end + + # filter out any changes that result in the original value + def saved_changes + @saved_changes_unfiltered.reject { |k,v| v[0] == v[1] } + end + + private + + # on save + def track_saved_changes + # maintain an array of ActiveModel::Dirty.changes + @saved_changes_history << changes.dup + # accumulate the most recent changes + @saved_changes_history.last.each_pair { |k, v| track_saved_change k, v } + end + + # v is an an array of [prev, current] + def track_saved_change(k, v) + if @saved_changes_unfiltered.key? k + @saved_changes_unfiltered[k][1] = track_saved_value v[1] + else + @saved_changes_unfiltered[k] = v.dup + end + end + + # type safe dup inspred by http://stackoverflow.com/a/20955038 + def track_saved_value(v) + begin + v.dup + rescue TypeError + v + end + end +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 8b95fee2..c3efd46d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -82,7 +82,12 @@ class User < ApplicationRecord after_create_commit :mailbluster_create_lead after_destroy_commit :mailbluster_delete_lead - after_update_commit :mailbluster_update_email, if: :saved_change_to_email? + # Note that because a commit may cause multiple changes + # which are not fully tracked by ActiveRecord::Dirty, + # we must resort to using a ActiveRecord::Concern which accumulates + # changes from a commit (app/models/concerns/track_saved_changes.rb) + # See https://github.com/ccmcbeck/after-commit + after_update_commit :mailbluster_update_email, if: ->(obj){ obj.saved_changes.key? 'email' } # add scope scope :comment_notifiable, ->(conference) {joins(:roles).where('roles.name IN (?)', [:organizer, :cfp]).where('roles.resource_type = ? AND roles.resource_id = ?', 'Conference', conference.id)} @@ -386,7 +391,7 @@ class User < ApplicationRecord def mailbluster_update_email # FIXME: May fail if multiple saves occur in one commit - MailblusterEditLeadJob.perform_later(self, old_email: email_before_last_save) + MailblusterEditLeadJob.perform_later(self, old_email: self.saved_changes['email'][0]) end def touch_events