8

I am trying to send a notification email in my rails app only if the value of my column status was modified by the current update. I tried using Active Model Dirty as was suggested in some post and the status_changed? method. Unfortunately my email is never sent because @partnership.status_changed? constantly returns false even though the value of status was indeed changed during the last update. Here's my controller code :

  def update
    authorize @partnership
    if @partnership.update(partnership_params)
      send_notification_email
      render json: {success: "partnership successfully updated"}, status: 200
    else
      render_error(nil, @partnership)
    end
  end

  private

  def send_notification_email
    PartnershipMailer.partnership_status_change(@partnership).deliver_now if @partnership.status_changed?
  end

I have also included Active Model Dirty in my model :

class Partnership < ActiveRecord::Base
  include ActiveModel::Dirty

What am I doing wrong ?

David Geismar
  • 3,152
  • 6
  • 41
  • 80

5 Answers5

7

.update also saves the model after updating it's data, therefore resetting the dirty-values. Try using .assign_attributes. It will just assign the attributes, then you can check for changes, and finally remember to save the model.

ollpu
  • 1,783
  • 15
  • 26
7

As @Thounder pointed out, the ActiveModel::Dirty method <attribute>_changed? is reset whenever you save a record. Thus, it only tracks changes between saves.

For your use case, what you want to use is the previous_changes method, which returns a hash with the key being the attribute changed and the value being an array of 2 values: old and new.

person = Person.new(name: "Bob")
person.name_changed? # => true
person.save
person.name_changed? # => false (reset when save called)
person.previous_changes # => { name: [nil, "Bob"] }
person.previous_changes[:name] # => returns a "truthy" statement if :name attribute changed

My pseudo-code may be wrong, but the principle works. I've been bitten by this "gotcha" before, and I wish the Rails core team would change it.

I understand their reasoning, but it makes more sense to me to track <attribute>_changed? after a save as well, because that seems the common use case to me.

Dan L
  • 4,319
  • 5
  • 41
  • 74
3

You can try this method to check the changed attributes for the active record.

@partnership.changed.include?("status")

If it returns true then we have status attribute which was changed in this record.

BIlal Khan
  • 453
  • 3
  • 16
1

Use @partnership.saved_change_to_status? or @partnership.saved_change_to_attribute(:status) as per docs.

vladiim
  • 1,862
  • 2
  • 20
  • 27
0

Here is a one line method you can into the model which is the best for your case :

after_commit :send_notification_email, if: Proc.new { |model| model.previous_changes[:status]}
Thounder
  • 315
  • 2
  • 14