Proposal: ActiveRecord Strict Persistence

I want a way to ensure that certain ActiveRecord attributes always fire callbacks during persistence, and the implementation would look similar to readonly attributes. This would be very helpful for things like audit logging, where I want to make sure that I’m always preserving the history of certain columns with something like PaperTrail. If an attribute is configured for strict persistence, attempting to update it via a callback-skipping persistence method like update_column would raise an error.

class Forum < ApplicationRecord
  attr_strict_persistence :title
end

forum = Forum.create!(title: "Best Tacos")
forum.update_column(title: "Best Pizza") # raises StrictPersistenceError

I’m happy to submit the PR unless there are concerns up front that this does not feel like a true Rails feature. Is this something that other developers would use?

My subjective reaction: this may not be a common enough scenario to warrant adding such a feature. A gem could achieve the same result without adding to the size and complexity of Rails.

Is the difference from attr_readonly the fact that it also checks update_column? I wonder if attr_readonly should just do that too.

That said, I think if you absolutely need to ensure a value is never changed, you probably need to enforce that at the DB level…

The goal of this feature is to ensure that AR callbacks always fire on specific column updates, not to disable writes to a particular column. In my case, I’m building audit logging and need to enforce that audit records are always created when a particular column changes, and there’s not an easy way to do this currently. It’s just very easy to bypass audit log generation (or any callback for that matter) via persistence methods that go straight to the DB instead of instantiating AR objects.

I understand you’re being asked to built a highly robust auditing solution. This can be tricky to do at the Rails (or any other framework) level, because someone can write database triggers that modify the data, and no framework would have any way of knowing about it. Or the Rails programmer can write a CREATE, UPDATE, or DELETE statement in raw SQL. So if you require that level of completeness from your audit log, you need to implement audit logging at the database level. You can Google that for your database.

If you what you want is logging auditing of the Rails app, don’t overlook simple solutions like grabbing the relevant lines out of the Rails log. Perhaps that’s robust enough for your application?

In sum, as @djmolny suggested, the requirement are varied enough that this probably fits better for a gem, than to be in Rails itself.