-
Notifications
You must be signed in to change notification settings - Fork 60
/
paranoia.rb
138 lines (126 loc) · 3.61 KB
/
paranoia.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# encoding: utf-8
require 'mongoid/paranoia/monkey_patches'
module Mongoid
# Include this module to get soft deletion of root level documents.
# This will add a deleted_at field to the +Document+, managed automatically.
# Potentially incompatible with unique indices. (if collisions with deleted items)
#
# @example Make a document paranoid.
# class Person
# include Mongoid::Document
# include Mongoid::Paranoia
# end
module Paranoia
include Mongoid::Persistable::Deletable
extend ActiveSupport::Concern
included do
field :deleted_at, type: Time
class_attribute :paranoid
self.paranoid = true
default_scope -> { where(deleted_at: nil) }
scope :deleted, -> { ne(deleted_at: nil) }
define_model_callbacks :restore
end
# Delete the paranoid +Document+ from the database completely. This will
# run the destroy callbacks.
#
# @example Hard destroy the document.
# document.destroy!
#
# @return [ true, false ] If the operation succeeded.
#
# @since 1.0.0
def destroy!
run_callbacks(:destroy) { delete! }
end
# Delete the +Document+, will set the deleted_at timestamp and not actually
# delete it.
#
# @example Soft remove the document.
# document.remove
#
# @param [ Hash ] options The database options.
#
# @return [ true ] True.
#
# @since 1.0.0
def remove_with_paranoia(options = {})
cascade!
time = self.deleted_at = Time.now
paranoid_collection.find(atomic_selector).
update({ "$set" => { paranoid_field => time }})
@destroyed = true
true
end
alias_method_chain :remove, :paranoia
alias :delete :remove
# Delete the paranoid +Document+ from the database completely.
#
# @example Hard delete the document.
# document.delete!
#
# @return [ true, false ] If the operation succeeded.
#
# @since 1.0.0
def delete!
remove_without_paranoia
end
# Determines if this document is destroyed.
#
# @example Is the document destroyed?
# person.destroyed?
#
# @return [ true, false ] If the document is destroyed.
#
# @since 1.0.0
def destroyed?
(@destroyed ||= false) || !!deleted_at
end
alias :deleted? :destroyed?
# Restores a previously soft-deleted document. Handles this by removing the
# deleted_at flag.
#
# @example Restore the document from deleted state.
# document.restore
#
# TODO: @return [ Time ] The time the document had been deleted.
#
# @since 1.0.0
def restore
run_callbacks(:restore) do
paranoid_collection.find(atomic_selector).
update({ "$unset" => { paranoid_field => true }})
attributes.delete("deleted_at")
@destroyed = false
true
end
end
# Returns a string representing the documents's key suitable for use in URLs.
def to_param
new_record? ? nil : to_key.join('-')
end
private
# Get the collection to be used for paranoid operations.
#
# @example Get the paranoid collection.
# document.paranoid_collection
#
# @return [ Collection ] The root collection.
#
# @since 2.3.1
def paranoid_collection
embedded? ? _root.collection : self.collection
end
# Get the field to be used for paranoid operations.
#
# @example Get the paranoid field.
# document.paranoid_field
#
# @return [ String ] The deleted at field.
#
# @since 2.3.1
def paranoid_field
embedded? ? "#{atomic_position}.deleted_at" : "deleted_at"
end
end
end