0.0
No release in over 3 years
Low commit activity in last 3 years
This gem takes an open approach and lets you decide how little or how much your project will be using the soft delete pattern. It can be configured on a per-model level to use whichever features are appropriate at the time. This makes it especially easy to introduce soft delete into existing projects.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 12.3.3
~> 3.5.0

Runtime

>= 4.2, < 7
 Project Readme

SoftDelete

(Yet another) Soft delete active_record models.

Why another soft delete? Soft deleting is a relatively common pattern which lets you hide records instead of deleting them. Some gems are heavy handed and override normal deleting. Most of them make a lot of decisions or assumptions about how you will be soft deleting. This gem takes an open approach and lets you decide how little or how much your project will be using the soft delete pattern. It can be configured on a per-model level to use whichever features are appropriate at the time. This makes it especially easy to introduce soft delete into existing projects.

Installation

Add this line to your application's Gemfile:

gem 'ar_soft_delete'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install ar_soft_delete

Usage

SoftDelete works by setting deleted_at to Time.now. Make sure your model has a datetime deleted_at column. And include the module into your active_record class:

class Author < ApplicationRecord
  include SoftDelete::SoftDeletable

  ...
end

myModel.soft_delete returns true|false.

myModel.soft_delete! raises ActiveRecord::RecordInvalid on failure.

You can also pass an optional validate: false argument to ignore validations on save. This can be useful if you want to soft delete a record that would normally not save because it is invalid.

Exa:

foo.valid?
> false
foo.soft_delete(validate: false)
> true

Dependency Behavior

In general, SoftDelete considers "soft deleting" and "normal deleting" to be two separate things. Under this philosophy, SoftDelete's default behavior is to do as little as possible (just set the deleted_at column). There are no callbacks fired off, associations are not updated, etc.

However, sometimes you want to handle soft deletes as if they are real deletes. To that end, SoftDelete allows you to set a dependency behavior include SoftDelete::SoftDeletable.dependent(:ignore|:default|:soft_delete)

  • :ignore: The default. Do nothing with associated records. Useful if you want to soft delete specific records with no other side effects. Example use case: I have identified bad data. I want to be able to delete the data, see the ramifications and potentially quickly restore the data if the delete was a mistake.
  • :default: Fire off the same action that is described by the active_record dsl in the model. Exa (has_many :enemies, default: :destroy) would destroy the enemies when the model is soft deleted. This is useful if you are incrementally adding soft delete to certain models and want the rest of the behavior to remain the same.
  • :soft_delete: overrides the :destroy association option to invoke a soft_delete on the associated records. This comes the closest to automatically replacing normal deletes with soft deletes. It runs before|around|after destroy hooks when it soft deletes.

Exa:

class Author < ApplicationRecord
  include SoftDelete::SoftDeletable.dependent(:soft_delete)

  has_many :notes, dependent: :destroy
  ...
end

Default Scope

By default, SoftDelete uses a default_scope. Do you feel strongly that a default scope is not for you? SoftDelete can be included without a default scope: include SoftDelete::SoftDeletable.not_scoped

This will skip adding a default scope to the model and instead will add an active scope that you can use to filter the records.

Reminder: You can chain the scope and dependency options!

class Author < ApplicationRecord
  include SoftDelete::SoftDeletable.not_scoped.dependent(:default)

  has_many :notes, dependent: :destroy
end

SoftDelete Restorable

You can also include the SoftDelete::Restorable module to include a deleted scope which overrides the default deleted_at scope if it exists.

class Note < ApplicationRecord
  include SoftDelete::Restorable
  ...
end

Note.deleted # returns records that have been soft deleted

It also mixes in restore_soft_delete and restore_soft_delete!. They both can take an optional validate param to restore an otherwise invalid record.

exa:

note = Note.deleted.find_by(id: 2)
note.valid?
> false
note.restore_soft_delete(validate: false)
> true

Caveats

SoftDelete uses a class var to hold the dependency behavior. This has implications if you subclass a model that includes SoftDelete. All subclasses share the same class variable and therefore would share the same soft delete dependency behavior. Changing it in a subclass changes it for the ancestors as well as any children.

SoftDelete does not currently support updating cache counters when a record is soft deleted.

Roadmap

  • before|after soft_delete hooks.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/swelltrain/soft_delete.

License

The gem is available as open source under the terms of the MIT License.