Rails loses change tracking when calling instance method

114 Views Asked by At

When changing a model's attributes in the controller's update, I want to generate a history of changes that are about to be made. For that, I've created a method generate_history to access from the instance.

The class:

class Assist < ActiveRecord::Base
    belongs_to: status

    def generate_history
        #Also tried without self

        p 'Testing round two'
        p 'Status' + self.status.id.to_s
        p 'Modified: ' + self.status.id_changed?.to_s
        p 'Old value ' + self.status.id_was.to_s

        #do something something dark side
    end

end

The issue is within that method, the ActiveModel::Dirty isn't aware of the changes that have been made, although the self.status_id's value is the new one.

Meanwhile in the controller:

def update

    ...

    @assist.assign_attributes(assist_params)

    p 'Testing round one'
    p 'Status' + @assist.status_id.to_s
    p 'Modified: ' + @assist.status_id_changed?.to_s
    p 'Old value ' + @assist.status_id_was.to_s

    p 'Generating history'

    @assist.generate_history

    p 'Testing round three'
    p 'Status' + @assist.status_id.to_s
    p 'Modified: ' + @assist.status_id_changed?.to_s
    p 'Old value ' + @assist.status_id_was.to_s
end

At first I was suspicious of assign_attributes that somehow interfered with ActiveModel::Dirty but I've realized that ActiveModel::Dirty works in the controller, where the values are being modified, but not when I'm calling generate_history.

Am I doing something wrong from within the instance method or its the way ActiveModel::Dirty works?

Example of output:

Testing round one
Status 1
Modified: true
Old value 2

Generating history
Testing round two
Status 1
Modified: false
Old value 1

Testing round three
Status 1
Modified: true
Old value 2
1

There are 1 best solutions below

2
On BEST ANSWER

My guess is that the status_id is not an attribute that is fully tracked by ActiveRecord::Dirty. Instead of looking at status_id, i think you should look at status. Something like:

class Assist < ActiveRecord::Base
    belongs_to: status
    def generate_history
        p 'Testing round two'
        p 'Status' + status_id.to_s
        p 'Modified: ' + status_id_changed?.to_s
        p 'Old value ' + status_id_was.to_s

        #do something something dark side
    end
end

Then your controller code is right.

And because you're not assigning status but only asking about it, you probably don't need the self.