-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add soft delete functionality to Avram #323
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
db/migrations/20200316160609_create_soft_deletable_items.cr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class CreateSoftDeletableItems::V20200316160609 < Avram::Migrator::Migration::V1 | ||
def migrate | ||
create table_for(SoftDeletableItem) do | ||
primary_key id : Int64 | ||
add_timestamps | ||
|
||
add soft_deleted_at : Time? | ||
end | ||
end | ||
|
||
def rollback | ||
drop table_for(SoftDeletableItem) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
require "./spec_helper" | ||
|
||
class SoftDeletableItemQuery < SoftDeletableItem::BaseQuery | ||
include Avram::SoftDelete::Query | ||
end | ||
|
||
describe "Avram soft delete" do | ||
describe "models" do | ||
it "allows soft deleting a record" do | ||
item = SoftDeletableItemBox.create &.kept | ||
|
||
item = item.soft_delete | ||
|
||
item.soft_deleted_at.should_not be_nil | ||
end | ||
|
||
it "allows restoring a soft deleted record" do | ||
item = SoftDeletableItemBox.create &.soft_deleted | ||
|
||
item = item.restore | ||
|
||
item.soft_deleted_at.should be_nil | ||
end | ||
|
||
it "allows checking if a record is soft deleted" do | ||
item = SoftDeletableItemBox.create &.kept | ||
item.soft_deleted?.should be_false | ||
|
||
item = item.soft_delete | ||
|
||
item.soft_deleted?.should be_true | ||
end | ||
end | ||
|
||
describe "queries" do | ||
it "can get only kept records" do | ||
kept_item = SoftDeletableItemBox.create &.kept | ||
SoftDeletableItemBox.create &.soft_deleted | ||
|
||
SoftDeletableItemQuery.new.only_soft_deleted.only_kept.results.should eq([ | ||
kept_item, | ||
]) | ||
end | ||
|
||
it "can get only soft deleted records" do | ||
SoftDeletableItemBox.create &.kept | ||
soft_deleted_item = SoftDeletableItemBox.create &.soft_deleted | ||
|
||
SoftDeletableItemQuery.new.only_kept.only_soft_deleted.results.should eq([ | ||
soft_deleted_item, | ||
]) | ||
end | ||
|
||
it "can get soft deleted and kept records" do | ||
kept_item = SoftDeletableItemBox.create &.kept | ||
soft_deleted_item = SoftDeletableItemBox.create &.soft_deleted | ||
|
||
SoftDeletableItemQuery.new.only_kept.with_soft_deleted.results.should eq([ | ||
kept_item, | ||
soft_deleted_item, | ||
]) | ||
end | ||
|
||
it "can bulk soft delete" do | ||
kept_item = SoftDeletableItemBox.create &.kept | ||
soft_deleted_item = SoftDeletableItemBox.create &.soft_deleted | ||
|
||
num_restored = SoftDeletableItemQuery.new.soft_delete | ||
|
||
num_restored.should eq(1) | ||
kept_item.reload.soft_deleted?.should be_true | ||
soft_deleted_item.reload.soft_deleted?.should be_true | ||
end | ||
|
||
it "can bulk restore" do | ||
kept_item = SoftDeletableItemBox.create &.kept | ||
soft_deleted_item = SoftDeletableItemBox.create &.soft_deleted | ||
|
||
num_restored = SoftDeletableItemQuery.new.restore | ||
|
||
num_restored.should eq(1) | ||
kept_item.reload.soft_deleted?.should be_false | ||
soft_deleted_item.reload.soft_deleted?.should be_false | ||
end | ||
end | ||
end | ||
|
||
private def reload(item) | ||
SoftDeletableItemQuery.find(item.id) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
class SoftDeletableItemBox < BaseBox | ||
def kept | ||
soft_deleted_at nil | ||
end | ||
|
||
def soft_deleted | ||
soft_deleted_at Time.utc | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class SoftDeletableItem < BaseModel | ||
include Avram::SoftDelete::Model | ||
|
||
skip_default_columns | ||
|
||
table do | ||
primary_key id : Int64 | ||
column soft_deleted_at : Time? | ||
timestamps | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Add methods for soft deleting and restoring an individual record | ||
# | ||
# Include this module in your model, and make sure to add a `soft_deleted_at` | ||
# column to your model. The column type must be `Time?` | ||
# | ||
# ```crystal | ||
# # In a migration | ||
# add soft_deleted_at : Time? | ||
# | ||
# # In your model | ||
# class Article < BaseModel | ||
# include Avram::SoftDelete::Model | ||
|
||
# table do | ||
# column soft_deleted_at : Time? | ||
# end | ||
# end | ||
# ``` | ||
# | ||
# You should also add the `Avram::SoftDeleteQuery` to your query | ||
# | ||
# ```crystal | ||
# class ArticleQuery < Article::BaseQuery | ||
# include Avram::SoftDelete::Query | ||
# end | ||
# ``` | ||
module Avram::SoftDelete::Model | ||
# Soft delete the record | ||
# | ||
# This will set `soft_deleted_at` to the current time (`Time.utc`) | ||
def soft_delete : self | ||
save_operation_class.update!(self, soft_deleted_at: Time.utc) | ||
end | ||
|
||
# Restore the record | ||
# | ||
# This will set `soft_deleted_at` to `nil` | ||
def restore : self | ||
save_operation_class.update!(self, soft_deleted_at: nil) | ||
end | ||
|
||
abstract def save_operation_class | ||
|
||
# Returns true if soft deleted, otherwise false | ||
# | ||
# If the `soft_deleted_at` has a time value the record is "soft deleted". | ||
# If `soft_deleted_at` is `nil` the record has not been deleted yet. | ||
def soft_deleted? : Bool | ||
soft_deleted_at.present? | ||
end | ||
|
||
abstract def soft_deleted_at : Time? | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Add methods for querying/updating soft deleted and kept records. | ||
# | ||
# First include the model module in your model: `Avram::SoftDelete::Model` | ||
# | ||
# Then add this module your query | ||
# | ||
# ```crystal | ||
# class ArticleQuery < Article::BaseQuery | ||
# include Avram::SoftDelete::Query | ||
# end | ||
# ``` | ||
module Avram::SoftDelete::Query | ||
# Only return kept records | ||
# | ||
# Kept records are considered "kept/not soft deleted" if the | ||
# `soft_deleted_at` column is `nil` | ||
def only_kept | ||
reset_where(&.soft_deleted_at).soft_deleted_at.is_nil | ||
end | ||
|
||
# Only return soft deleted records | ||
# | ||
# Soft deleted records are considered "soft deleted" if the | ||
# `soft_deleted_at` column has a non-nil value | ||
def only_soft_deleted | ||
reset_where(&.soft_deleted_at).soft_deleted_at.is_not_nil | ||
end | ||
|
||
# Returns all records | ||
# | ||
# This works be removing where clauses for the `soft_deleted_at` column. | ||
# That means you can do `MyQuery.new.only_kept.with_soft_deleted` and you | ||
# will get all records, not just the kept ones. | ||
def with_soft_deleted | ||
reset_where(&.soft_deleted_at) | ||
end | ||
|
||
# Bulk soft delete records | ||
# | ||
# ## Example | ||
# | ||
# This will soft delete all `Article` record older than 1 year: | ||
# | ||
# ```crystal | ||
# ArticleQuery.new.created_at.lt(1.year.ago).soft_delete | ||
# ``` | ||
def soft_delete | ||
only_kept.update(soft_deleted_at: Time.utc) | ||
end | ||
|
||
# Bulk restore records | ||
# | ||
# ## Example | ||
# | ||
# This will restore `Article` records updated in the last week: | ||
# | ||
# ```crystal | ||
# ArticleQuery.new.updated_at.gt(1.week.ago).restore | ||
# ``` | ||
def restore : Int64 | ||
only_soft_deleted.update(soft_deleted_at: nil) | ||
end | ||
|
||
abstract def soft_deleted_at | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wontruefree FIxed!