Need to return the :name of an object, even if it was soft deleted (using paranoia)

454 Views Asked by At

I have Users in the system which can be soft_deleted using paranoia (2.0.2) and TimeRecords which keep track of how many :hours a user worked on a given assignment and what their total :cost was for the entire assignment (:cost = :rate * :hours to keep it simple).

These records persist so even if a user's rate is changed in the future, you will still have an accurate snapshot of what they charged for a given task in the past. Enter the soft deletes. A user can be removed from the system using a soft delete (setting deleted_at: Time.now) but I need their name to still show up linked to the :hours and :cost they charged in the past. I have a solution that works but feels too hacky for me and I haven't been able to find a more elegant solution. I'd appreciate any suggestions/help people may have to do this the right way instead of the easy way.

Current solution:

class TimeRecord < ActiveRecord::Base
  belongs_to :user

  delegate :name, to: :user, prefix: true, allow_nil: true

  def name
    user_name || "#{User.with_deleted.find(user_id).name}" rescue 'n/a'
  end
end
3

There are 3 best solutions below

1
On BEST ANSWER

If your TimeRecord needs the name, regardless of whether the User still exists or not, then I would recommend storing the user_name on TimeRecord as well as on User instead of delegating it.

When the name changes on the User, I would update the relevant TimeRecords accordingly.

5
On

If you want to include associated soft-deleted objects, you can simply unscope the association like this:

class TimeRecord < ActiveRecord::Base
  belongs_to :user, -> { with_deleted } # associate soft-deleted user
  delegate :name, to: :user, prefix: true, allow_nil: true
end
1
On

It's better to pretend that soft deleted records are deleted for real to keep referential integrity. Since you already said a TimeRecord keeps a "Snapshot" of the User at that time, the solution should be clear: add name to the fields you store in TimeRecord as a snapshot.

This is a perfect usecase for this kind of de-normalization even if the User is not allowed to change its name.