Detecting if value of attribute changed during last update doesnt work with Active Model Dirty
Asked Answered
S

5

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 ?

Spendable answered 30/8, 2016 at 18:24 Comment(0)
I
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.

Ides answered 30/8, 2016 at 18:32 Comment(0)
S
9

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.

Sula answered 17/5, 2017 at 14:23 Comment(0)
I
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.

Ides answered 30/8, 2016 at 18:32 Comment(0)
P
2

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.

Porett answered 30/8, 2016 at 19:8 Comment(0)
V
1

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]}
Valueless answered 30/8, 2016 at 21:2 Comment(0)
I
0

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

Inefficacy answered 23/4, 2019 at 3:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.