Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Dirty state tracking for previous changes
module PreviouslyDirty
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
included do
attribute_method_suffix '_previously_changed?', '_previously_was'
end
# Handle <tt>*_previously_changed?</tt> for +method_missing+.
def attribute_previously_changed?(attr, options = {}) #:nodoc:
result = previous_changes.include?(attr)
result &&= options[:to] == previous_changes[attr][1] if options.key?(:to)
result &&= options[:from] == previous_changes[attr][0] if options.key?(:from)
result
end
# Handle <tt>*_was</tt> for +method_missing+.
def attribute_previously_was(attr) # :nodoc:
attribute_previously_changed?(attr) ? previous_changes[attr][0] : __send__(attr)
end
end
@ideasasylum

This comment has been minimized.

Copy link
Owner Author

@ideasasylum ideasasylum commented May 10, 2016

Rails has some great dirty state tracking methods but this information is cleared after the commit. This prevents you doing things like:

user = 'new_email@example.com'
user.save!
send_some_email if user.email_changed? # this'll never execute

but all the changes from the last commit are stored in the previous_changes hash so this gist adds the attribute_previously_changed? and attribute_previously_was methods following the same pattern as the regular change methods

user.email = 'new_email@example.com'
user.save!
send_some_email if user.email_previously_changed?  #=> now executes
old_email = user.email_previously_was                         #=> the old email address

I find this very handy for conditional after_commit callbacks or, even better, for avoiding callbacks altogether and putting that logic in the controller (on the basis that callbacks can be a complete PITA)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment