Skip to content

Instantly share code, notes, and snippets.

@dabit
Last active November 2, 2022 15:29
Show Gist options
  • Save dabit/c8c06f0b2fc51219dd6691bf6306a6e0 to your computer and use it in GitHub Desktop.
Save dabit/c8c06f0b2fc51219dd6691bf6306a6e0 to your computer and use it in GitHub Desktop.
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
def self.inherited(subclass)
super
return unless subclass.has_attribute?(:deleted_at)
setup_for_soft_delete(subclass)
rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished
nil
end
def self.setup_for_soft_delete(subclass)
subclass.send(:default_scope, -> { where(deleted_at: nil) })
class << subclass
def archived
where.not(deleted_at: nil)
end
end
subclass.define_method(:destroy) do
touch(:deleted_at)
end
end
def self.human_enum_name(enum_name, enum_value)
I18n.t("activerecord.attributes.#{model_name.i18n_key}.#{enum_name.to_s.pluralize}.#{enum_value}")
end
def self.plural_name
model_name.human(count: 2)
end
def self.enum_collection_for_select(enum_plural_name)
send(enum_plural_name).collect { |k, _v| [k, human_enum_name(enum_plural_name, k)] }
end
end
@silva96
Copy link

silva96 commented Oct 26, 2022

@alejandrodevs I usually prefer to be more explicit and implement a soft_delete method. I do this in a general purpose concern that I include in all soft_deletable models.

@dabit
Copy link
Author

dabit commented Oct 26, 2022

@alejandrodevs My use case is very simple and straightforward; I just want to keep a small selection of tables where I want the data to be deleted but still keep it handy in case someone needs it back. I have been in several situations where the user deletes a record by mistake or simply because they thought they would not need it in the future and then realize that they do, in fact, need it later.

I use the default scope because I don't expect this data to show up anywhere within the app, and I want it to behave as if it's gone forever. I rarely add the functionality to browse this data. I include an archived scope for my convenience, just in case I want to dig through it via the console.

Adding a deleted_at column is explicit enough to tell me if a table is soft deletable or not (I religiously use annotate, so I can always see what my tables look like).

I override destroy because it makes it simpler to turn it on and off just by adding or removing the deleted_at column.

By the way, I will include your scope-based refactoring in a future iteration; thank you for that. I agree that using scope is better.

Lastly, I understand there are gems for everything these days, but I personally never add a dependency for something I can fix with a few lines of code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment