In many Rails based systems we can find models that should not be destroyed but should be tagged as removed/disabled.
This is a simple problem to solve but it becomes complicated when we want to use accepts_nested_attributes_for ... , allow_destroy: true
I want to share with you my latest thoughts about this problem. Maybe you have some fancy solution.
Usually I use simple ActiveSupport::Concern
to satisfy the bussines
module Removable
extend ActiveSupport::Concern
included do
scope :removed, -> { where(removed: true) }
scope :active, -> { where(removed: false) }
end
def removed?
removed
end
def remove(user)
update!({
removed: true,
removed_by: user,
removed_date: DateTime.now
})
# other bussines logic for remove
end
def remove_nested(user)
self.removed = true
self.removed_by = user
self.removed_date: DateTime.now
# other bussines logic for remove
end
def restore
# bussines logic for restore
end
No matter how you implemented nested attributes (Stimulus, Old Rails Way, cocoon, etc..), the rails magic is the same. As always we need to permit controller parameters for our nested attributes: id
and _destroy
:
def todo_params
params.require(:todo).permit(:name, subtasks_attributes: %i[ title content id _destroy])
end
At this point rails magic should destroy nested models.
Important notice
It destroy record due to Active Record Autosave Association #marked_for_destruction
Marks this record to be destroyed as part of the parent's save transaction. This does not actually destroy the record instantly, rather child record will be destroyed when parent.save is called.
Using default controller action like @todo.update(todo_params)
we don't have any option to access and change destroy behaivior to use Removable#remove_nested
.
My current solution
Lets provide before_validation :mark_nested_removed
to take care about almost destroyed records.
def mark_nested_removed
subtasks.filter(&:marked_for_destruction?).each do |subtask|
subtask.reload
subtask.remove_nested(edited_by)
end
end
- we use
subtask.reload
to clear marked_for_destruction flag, that prevent destroying record. - we also use
subtask.remove_nested
to set params to model instead of make update! immediately
What do YOU think about it?
__
If you found these helpful, share them with your mates.
References:
Top comments (1)
Nice!