rails3_acts_as_paranoid_create 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +20 -0
- data/README.markdown +120 -0
- data/Rakefile +48 -0
- data/init.rb +1 -0
- data/lib/rails3_acts_as_paranoid.rb +197 -0
- data/lib/rails3_acts_as_paranoid/version.rb +3 -0
- data/lib/validations/uniqueness_without_deleted.rb +38 -0
- data/rails3_acts_as_paranoid.gemspec +29 -0
- data/test/rails3_acts_as_paranoid_test.rb +295 -0
- data/test/test_helper.rb +271 -0
- metadata +161 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails3_acts_as_paranoid_create (0.2.0)
|
5
|
+
activerecord (~> 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.1.0)
|
11
|
+
activesupport (= 3.1.0)
|
12
|
+
bcrypt-ruby (~> 3.0.0)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
i18n (~> 0.6)
|
15
|
+
activerecord (3.1.0)
|
16
|
+
activemodel (= 3.1.0)
|
17
|
+
activesupport (= 3.1.0)
|
18
|
+
arel (~> 2.2.1)
|
19
|
+
tzinfo (~> 0.3.29)
|
20
|
+
activesupport (3.1.0)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
arel (2.2.1)
|
23
|
+
bcrypt-ruby (3.0.0)
|
24
|
+
builder (3.0.0)
|
25
|
+
i18n (0.6.0)
|
26
|
+
multi_json (1.0.3)
|
27
|
+
rake (0.9.2)
|
28
|
+
rdoc (3.9.4)
|
29
|
+
sqlite3 (1.3.4)
|
30
|
+
sqlite3-ruby (1.3.3)
|
31
|
+
sqlite3 (>= 1.3.3)
|
32
|
+
tzinfo (0.3.29)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
activesupport (>= 3.1.0)
|
39
|
+
rails3_acts_as_paranoid_create!
|
40
|
+
rake (>= 0.9.0)
|
41
|
+
rdoc (>= 3.9.0)
|
42
|
+
sqlite3-ruby (>= 1.3.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Gonçalo Silva
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# ActsAsParanoid
|
2
|
+
|
3
|
+
A simple plugin which hides records instead of deleting them, being able to recover them.
|
4
|
+
|
5
|
+
## Credits
|
6
|
+
|
7
|
+
This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
|
8
|
+
|
9
|
+
While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
You can enable ActsAsParanoid like this:
|
14
|
+
|
15
|
+
class Paranoiac < ActiveRecord::Base
|
16
|
+
acts_as_paranoid
|
17
|
+
end
|
18
|
+
|
19
|
+
### Options
|
20
|
+
|
21
|
+
You can also specify the name of the column to store it's *deletion* and the type of data it holds:
|
22
|
+
|
23
|
+
- :column => 'deleted_at'
|
24
|
+
- :type => 'time'
|
25
|
+
|
26
|
+
The values shown are the defaults. While *column* can be anything (as long as it exists in your database), *type* is restricted to "boolean", "time" or "string".
|
27
|
+
|
28
|
+
If your column type is a "string", you can also specify which value to use when marking an object as deleted by passing `:deleted_value` (default is "deleted").
|
29
|
+
|
30
|
+
### Filtering
|
31
|
+
|
32
|
+
If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing the database. So, `Paranoiac.all` will **not** include the deleted_records. if you want to access them, you have 2 choices:
|
33
|
+
|
34
|
+
Paranoiac.only_deleted # retrieves the deleted records
|
35
|
+
Paranoiac.with_deleted # retrieves all records, deleted or not
|
36
|
+
|
37
|
+
### Real deletion
|
38
|
+
|
39
|
+
In order to really delete a record, just use:
|
40
|
+
|
41
|
+
paranoiac.destroy!
|
42
|
+
Paranoiac.delete_all!(conditions)
|
43
|
+
|
44
|
+
You can also definitively delete a record by calling `destroy` or `delete_all` on it twice. If a record was already deleted (hidden by ActsAsParanoid) and you delete it again, it will be removed from the database. Take this example:
|
45
|
+
|
46
|
+
Paranoiac.first.destroy # does NOT delete the first record, just hides it
|
47
|
+
Paranoiac.only_deleted.destroy # deletes the first record from the database
|
48
|
+
|
49
|
+
### Recovery
|
50
|
+
|
51
|
+
Recovery is easy. Just invoke `recover` on it, like this:
|
52
|
+
|
53
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover
|
54
|
+
|
55
|
+
All associations marked as `:dependent => :destroy` are also recursively recovered. If you would like to disable this behavior, you can call `recover` with the `recursive` option:
|
56
|
+
|
57
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recursive => false)
|
58
|
+
|
59
|
+
If you would like to change the default behavior for a model, you can use the `recover_dependent_associations` option
|
60
|
+
|
61
|
+
class Paranoiac < ActiveRecord::Base
|
62
|
+
acts_as_paranoid :recover_dependent_associations => false
|
63
|
+
end
|
64
|
+
|
65
|
+
By default when using timestamp fields to mark deletion, dependent records will be recovered if they were deleted within 5 seconds of the object upon which they depend. This restores the objects to the state before the recursive deletion without restoring other objects that were deleted earlier. This window can be changed with the `dependent_recovery_window` option
|
66
|
+
|
67
|
+
class Paranoiac < ActiveRecord::Base
|
68
|
+
acts_as_paranoid
|
69
|
+
has_many :paranoids, :dependent => :destroy
|
70
|
+
end
|
71
|
+
|
72
|
+
class Paranoid < ActiveRecord::Base
|
73
|
+
belongs_to :paranoic
|
74
|
+
|
75
|
+
# Paranoid objects will be recovered alongside Paranoic objects
|
76
|
+
# if they were deleted within 1 minute of the Paranoic object
|
77
|
+
acts_as_paranoid :dependent_recovery_window => 1.minute
|
78
|
+
end
|
79
|
+
|
80
|
+
or in the recover statement
|
81
|
+
|
82
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recovery_window => 30.seconds)
|
83
|
+
|
84
|
+
### Validation
|
85
|
+
ActiveRecord's built-in uniqueness validation does not account for records deleted by ActsAsParanoid. If you want to check for uniqueness among non-deleted records only, use the macro `validates_as_paranoid` in your model. Then, instead of using `validates_uniqueness_of`, use `validates_uniqueness_of_without_deleted`. This will keep deleted records from counting against the uniqueness check.
|
86
|
+
|
87
|
+
class Paranoiac < ActiveRecord::Base
|
88
|
+
acts_as_paranoid
|
89
|
+
validates_as_paranoid
|
90
|
+
validates_uniqueness_of_without_deleted :name
|
91
|
+
end
|
92
|
+
|
93
|
+
Paranoiac.create(:name => 'foo').destroy
|
94
|
+
Paranoiac.new(:name => 'foo').valid? #=> true
|
95
|
+
|
96
|
+
|
97
|
+
### Status
|
98
|
+
Once you retrieve data using `with_deleted` scope you can check deletion status using `deleted?` helper:
|
99
|
+
|
100
|
+
Paranoiac.create(:name => 'foo').destroy
|
101
|
+
Paranoiac.with_deleted.first.deleted? #=> true
|
102
|
+
|
103
|
+
## Caveats
|
104
|
+
|
105
|
+
Watch out for these caveats:
|
106
|
+
|
107
|
+
- You cannot use default\_scope in your model. It is possible to work around this caveat, but it's not pretty. Have a look at [this article](http://joshuaclayton.github.com/code/default_scope/activerecord/is_paranoid/multiple-default-scopes.html) if you really need to have your own default scope.
|
108
|
+
- You cannot use scopes named `with_deleted`, `only_deleted` and `paranoid_deleted_around_time`
|
109
|
+
- `unscoped` will return all records, deleted or not
|
110
|
+
|
111
|
+
## Acknowledgements
|
112
|
+
|
113
|
+
* To [cheerfulstoic](https://github.com/cheerfulstoic) for adding recursive recovery
|
114
|
+
* To [Jonathan Vaught](https://github.com/gravelpup) for adding paranoid validations
|
115
|
+
* To [Geoffrey Hichborn](https://github.com/phene) for improving the overral code quality and adding support for after_commit
|
116
|
+
* To [flah00](https://github.com/flah00) for adding support for STI-based associations (with :dependent)
|
117
|
+
* To [vikramdhillon](https://github.com/vikramdhillon) for the idea and
|
118
|
+
initial implementation of support for string column type
|
119
|
+
|
120
|
+
Copyright © 2010 Gonçalo Silva, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require('bundler/gem_tasks')
|
2
|
+
require "rake/testtask"
|
3
|
+
require "rdoc/task"
|
4
|
+
|
5
|
+
gemspec = eval(File.read(Dir["*.gemspec"].first))
|
6
|
+
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc 'Test the rails3_acts_as_paranoid plugin.'
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.libs << 'test'
|
15
|
+
t.pattern = 'test/**/*_test.rb'
|
16
|
+
t.verbose = true
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'Generate documentation for the rails3_acts_as_paranoid plugin.'
|
20
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
21
|
+
rdoc.rdoc_dir = 'rdoc'
|
22
|
+
rdoc.title = 'ActsAsParanoid'
|
23
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
24
|
+
rdoc.rdoc_files.include('README')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Validate the gemspec"
|
29
|
+
task :gemspec do
|
30
|
+
gemspec.validate
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Build gem locally"
|
34
|
+
task :build => :gemspec do
|
35
|
+
system "gem build #{gemspec.name}.gemspec"
|
36
|
+
FileUtils.mkdir_p "pkg"
|
37
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Install gem locally"
|
41
|
+
task :install => :build do
|
42
|
+
system "gem install pkg/#{gemspec.name}-#{gemspec.version}"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Clean automatically generated files"
|
46
|
+
task :clean do
|
47
|
+
FileUtils.rm_rf "pkg"
|
48
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rails3_acts_as_paranoid'
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'validations/uniqueness_without_deleted'
|
3
|
+
|
4
|
+
module ActsAsParanoid
|
5
|
+
|
6
|
+
def paranoid?
|
7
|
+
self.included_modules.include?(InstanceMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
def validates_as_paranoid
|
11
|
+
extend ParanoidValidations::ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
def acts_as_paranoid(options = {})
|
15
|
+
raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
|
16
|
+
|
17
|
+
class_attribute :paranoid_configuration, :paranoid_column_reference
|
18
|
+
|
19
|
+
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
|
20
|
+
self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
|
21
|
+
self.paranoid_configuration.merge!(options) # user options
|
22
|
+
|
23
|
+
raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{paranoid_configuration[:column_type]}" unless ['time', 'boolean', 'string'].include? paranoid_configuration[:column_type]
|
24
|
+
|
25
|
+
self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
|
26
|
+
|
27
|
+
return if paranoid?
|
28
|
+
|
29
|
+
ActiveRecord::Relation.class_eval do
|
30
|
+
alias_method :delete_all!, :delete_all
|
31
|
+
alias_method :destroy!, :destroy
|
32
|
+
end
|
33
|
+
|
34
|
+
scope :not_deleted, where("#{paranoid_column_reference} IS ?", nil)
|
35
|
+
|
36
|
+
scope :paranoid_deleted_around_time, lambda {|value, window|
|
37
|
+
if self.class.respond_to?(:paranoid?) && self.class.paranoid?
|
38
|
+
if self.class.paranoid_column_type == 'time' && ![true, false].include?(value)
|
39
|
+
self.where("#{self.class.paranoid_column} > ? AND #{self.class.paranoid_column} < ?", (value - window), (value + window))
|
40
|
+
else
|
41
|
+
self.only_deleted
|
42
|
+
end
|
43
|
+
end if paranoid_configuration[:column_type] == 'time'
|
44
|
+
}
|
45
|
+
|
46
|
+
include InstanceMethods
|
47
|
+
extend ClassMethods
|
48
|
+
end
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
def self.extended(base)
|
52
|
+
base.define_callbacks :recover
|
53
|
+
end
|
54
|
+
|
55
|
+
def before_recover(method)
|
56
|
+
set_callback :recover, :before, method
|
57
|
+
end
|
58
|
+
|
59
|
+
def after_recover(method)
|
60
|
+
set_callback :recover, :after, method
|
61
|
+
end
|
62
|
+
|
63
|
+
def with_deleted
|
64
|
+
self.unscoped
|
65
|
+
end
|
66
|
+
|
67
|
+
def only_deleted
|
68
|
+
self.unscoped.where("#{paranoid_column_reference} IS NOT ?", nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_all!(conditions = nil)
|
72
|
+
self.unscoped.delete_all!(conditions)
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_all(conditions = nil)
|
76
|
+
update_all ["#{paranoid_configuration[:column]} = ?", delete_now_value], conditions
|
77
|
+
end
|
78
|
+
|
79
|
+
def paranoid_column
|
80
|
+
paranoid_configuration[:column].to_sym
|
81
|
+
end
|
82
|
+
|
83
|
+
def paranoid_column_type
|
84
|
+
paranoid_configuration[:column_type].to_sym
|
85
|
+
end
|
86
|
+
|
87
|
+
def dependent_associations
|
88
|
+
self.reflect_on_all_associations.select {|a| [:destroy, :delete_all].include?(a.options[:dependent]) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete_now_value
|
92
|
+
case paranoid_configuration[:column_type]
|
93
|
+
when "time" then Time.now
|
94
|
+
when "boolean" then true
|
95
|
+
when "string" then paranoid_configuration[:deleted_value]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module InstanceMethods
|
101
|
+
|
102
|
+
def paranoid_value
|
103
|
+
self.send(self.class.paranoid_column)
|
104
|
+
end
|
105
|
+
|
106
|
+
def destroy!
|
107
|
+
with_transaction_returning_status do
|
108
|
+
run_callbacks :destroy do
|
109
|
+
act_on_dependent_destroy_associations
|
110
|
+
self.class.delete_all!(:id => self.id)
|
111
|
+
self.paranoid_value = self.class.delete_now_value
|
112
|
+
freeze
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def destroy
|
118
|
+
if paranoid_value.nil?
|
119
|
+
with_transaction_returning_status do
|
120
|
+
run_callbacks :destroy do
|
121
|
+
self.class.delete_all(:id => self.id)
|
122
|
+
self.paranoid_value = self.class.delete_now_value
|
123
|
+
self
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
destroy!
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def recover(options={})
|
132
|
+
options = {
|
133
|
+
:recursive => self.class.paranoid_configuration[:recover_dependent_associations],
|
134
|
+
:recovery_window => self.class.paranoid_configuration[:dependent_recovery_window]
|
135
|
+
}.merge(options)
|
136
|
+
|
137
|
+
self.class.transaction do
|
138
|
+
run_callbacks :recover do
|
139
|
+
recover_dependent_associations(options[:recovery_window], options) if options[:recursive]
|
140
|
+
|
141
|
+
self.update_attributes(self.class.paranoid_column.to_sym => nil)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def recover_dependent_associations(window, options)
|
147
|
+
self.class.dependent_associations.each do |association|
|
148
|
+
if association.collection? && self.send(association.name).paranoid?
|
149
|
+
self.send(association.name).unscoped do
|
150
|
+
self.send(association.name).paranoid_deleted_around_time(paranoid_value, window).each do |object|
|
151
|
+
object.recover(options) if object.respond_to?(:recover)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
elsif association.macro == :has_one && association.klass.paranoid?
|
155
|
+
association.klass.unscoped do
|
156
|
+
object = association.klass.paranoid_deleted_around_time(paranoid_value, window).send('find_by_'+association.foreign_key, self.id)
|
157
|
+
object.recover(options) if object && object.respond_to?(:recover)
|
158
|
+
end
|
159
|
+
elsif association.klass.paranoid?
|
160
|
+
association.klass.unscoped do
|
161
|
+
id = self.send(association.foreign_key)
|
162
|
+
object = association.klass.paranoid_deleted_around_time(paranoid_value, window).find_by_id(id)
|
163
|
+
object.recover(options) if object && object.respond_to?(:recover)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def act_on_dependent_destroy_associations
|
170
|
+
self.class.dependent_associations.each do |association|
|
171
|
+
if association.collection? && self.send(association.name).paranoid?
|
172
|
+
association.klass.with_deleted.instance_eval("find_all_by_#{association.foreign_key}(#{self.id})").each do |object|
|
173
|
+
object.destroy!
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def deleted?
|
180
|
+
!paranoid_value.nil?
|
181
|
+
end
|
182
|
+
alias_method :destroyed?, :deleted?
|
183
|
+
|
184
|
+
private
|
185
|
+
def paranoid_value=(value)
|
186
|
+
self.send("#{self.class.paranoid_column}=", value)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
# Extend ActiveRecord's functionality
|
194
|
+
ActiveRecord::Base.send :extend, ActsAsParanoid
|
195
|
+
|
196
|
+
# Push the recover callback onto the activerecord callback list
|
197
|
+
ActiveRecord::Callbacks::CALLBACKS.push(:before_recover, :after_recover)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
|
3
|
+
module ParanoidValidations
|
4
|
+
class UniquenessWithoutDeletedValidator < ActiveRecord::Validations::UniquenessValidator
|
5
|
+
def validate_each(record, attribute, value)
|
6
|
+
finder_class = find_finder_class_for(record)
|
7
|
+
|
8
|
+
if value && record.class.serialized_attributes.key?(attribute.to_s)
|
9
|
+
value = YAML.dump value
|
10
|
+
end
|
11
|
+
|
12
|
+
sql, params = mount_sql_and_params(finder_class, record.class.quoted_table_name, attribute, value)
|
13
|
+
|
14
|
+
# This is the only changed line from the base class version - it does finder_class.unscoped
|
15
|
+
relation = finder_class.where(sql, *params)
|
16
|
+
|
17
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
18
|
+
scope_value = record.send(scope_item)
|
19
|
+
relation = relation.where(scope_item => scope_value)
|
20
|
+
end
|
21
|
+
|
22
|
+
if record.persisted?
|
23
|
+
# TODO : This should be in Arel
|
24
|
+
relation = relation.where("#{record.class.quoted_table_name}.#{record.class.primary_key} <> ?", record.send(:id))
|
25
|
+
end
|
26
|
+
|
27
|
+
if relation.exists?
|
28
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
def validates_uniqueness_of_without_deleted(*attr_names)
|
35
|
+
validates_with UniquenessWithoutDeletedValidator, _merge_attributes(attr_names)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push(File.expand_path('../lib', __FILE__))
|
3
|
+
require('rails3_acts_as_paranoid/version')
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rails3_acts_as_paranoid_create"
|
7
|
+
s.version = Rails3ActsAsParanoid::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Goncalo Silva", "Philipp Ullmann"]
|
10
|
+
s.email = ["goncalossilva@gmail.com", "philipp.ullmann@create.at"]
|
11
|
+
s.homepage = "http://github.com/goncalossilva/rails3_acts_as_paranoid"
|
12
|
+
s.summary = "Active Record (>=3.0) plugin which allows you to hide and restore records without actually deleting them."
|
13
|
+
s.description = "Active Record (>=3.0) plugin which allows you to hide and restore records without actually deleting them. Check its GitHub page for more in-depth information."
|
14
|
+
s.rubyforge_project = s.name
|
15
|
+
|
16
|
+
s.required_rubygems_version = ">= 1.3.6"
|
17
|
+
|
18
|
+
s.add_dependency "activerecord", "~> 3.0"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ['lib']
|
24
|
+
|
25
|
+
s.add_development_dependency('rake', ['>= 0.9.0'])
|
26
|
+
s.add_development_dependency('sqlite3-ruby', ['>= 1.3.0'])
|
27
|
+
s.add_development_dependency('activesupport', ['>= 3.1.0'])
|
28
|
+
s.add_development_dependency('rdoc', ['>= 3.9.0'])
|
29
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ParanoidBaseTest < ActiveSupport::TestCase
|
4
|
+
def assert_empty(collection)
|
5
|
+
assert(collection.respond_to?(:empty?) && collection.empty?)
|
6
|
+
end
|
7
|
+
|
8
|
+
def setup
|
9
|
+
setup_db
|
10
|
+
|
11
|
+
["paranoid", "really paranoid", "extremely paranoid"].each do |name|
|
12
|
+
ParanoidTime.create! :name => name
|
13
|
+
ParanoidBoolean.create! :name => name
|
14
|
+
end
|
15
|
+
|
16
|
+
ParanoidString.create! :name => "strings can be paranoid"
|
17
|
+
NotParanoid.create! :name => "no paranoid goals"
|
18
|
+
ParanoidWithCallback.create! :name => "paranoid with callbacks"
|
19
|
+
|
20
|
+
ParanoidObserver.instance.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
teardown_db
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ParanoidTest < ParanoidBaseTest
|
29
|
+
def test_fake_removal
|
30
|
+
assert_equal 3, ParanoidTime.not_deleted.count
|
31
|
+
assert_equal 3, ParanoidBoolean.not_deleted.count
|
32
|
+
assert_equal 1, ParanoidString.not_deleted.count
|
33
|
+
|
34
|
+
ParanoidTime.not_deleted.first.destroy
|
35
|
+
ParanoidBoolean.delete_all("name = 'paranoid' OR name = 'really paranoid'")
|
36
|
+
ParanoidString.not_deleted.first.destroy
|
37
|
+
assert_equal 2, ParanoidTime.not_deleted.count
|
38
|
+
assert_equal 1, ParanoidBoolean.not_deleted.count
|
39
|
+
assert_equal 0, ParanoidString.not_deleted.count
|
40
|
+
assert_equal 1, ParanoidTime.only_deleted.count
|
41
|
+
assert_equal 2, ParanoidBoolean.only_deleted.count
|
42
|
+
assert_equal 1, ParanoidString.only_deleted.count
|
43
|
+
assert_equal 3, ParanoidTime.with_deleted.count
|
44
|
+
assert_equal 3, ParanoidBoolean.with_deleted.count
|
45
|
+
assert_equal 1, ParanoidString.with_deleted.count
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_real_removal
|
49
|
+
ParanoidTime.not_deleted.first.destroy!
|
50
|
+
ParanoidBoolean.delete_all!("name = 'extremely paranoid' OR name = 'really paranoid'")
|
51
|
+
ParanoidString.not_deleted.first.destroy!
|
52
|
+
assert_equal 2, ParanoidTime.not_deleted.count
|
53
|
+
assert_equal 1, ParanoidBoolean.not_deleted.count
|
54
|
+
assert_equal 0, ParanoidString.not_deleted.count
|
55
|
+
assert_equal 2, ParanoidTime.with_deleted.count
|
56
|
+
assert_equal 1, ParanoidBoolean.with_deleted.count
|
57
|
+
assert_equal 0, ParanoidString.with_deleted.count
|
58
|
+
assert_equal 0, ParanoidTime.only_deleted.count
|
59
|
+
assert_equal 0, ParanoidBoolean.only_deleted.count
|
60
|
+
assert_equal 0, ParanoidString.only_deleted.count
|
61
|
+
|
62
|
+
ParanoidTime.not_deleted.first.destroy
|
63
|
+
ParanoidTime.only_deleted.first.destroy
|
64
|
+
assert_equal 0, ParanoidTime.only_deleted.count
|
65
|
+
|
66
|
+
ParanoidTime.delete_all!
|
67
|
+
assert_empty ParanoidTime.not_deleted.all
|
68
|
+
assert_empty ParanoidTime.with_deleted.all
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_paranoid_scope
|
72
|
+
assert_raise(NoMethodError) { NotParanoid.delete_all! }
|
73
|
+
assert_raise(NoMethodError) { NotParanoid.first.destroy! }
|
74
|
+
assert_raise(NoMethodError) { NotParanoid.with_deleted }
|
75
|
+
assert_raise(NoMethodError) { NotParanoid.only_deleted }
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_recovery
|
79
|
+
assert_equal 3, ParanoidBoolean.not_deleted.count
|
80
|
+
ParanoidBoolean.not_deleted.first.destroy
|
81
|
+
assert_equal 2, ParanoidBoolean.not_deleted.count
|
82
|
+
ParanoidBoolean.only_deleted.first.recover
|
83
|
+
assert_equal 3, ParanoidBoolean.not_deleted.count
|
84
|
+
|
85
|
+
assert_equal 1, ParanoidString.not_deleted.count
|
86
|
+
ParanoidString.not_deleted.first.destroy
|
87
|
+
assert_equal 0, ParanoidString.not_deleted.count
|
88
|
+
ParanoidString.with_deleted.first.recover
|
89
|
+
assert_equal 1, ParanoidString.not_deleted.count
|
90
|
+
end
|
91
|
+
|
92
|
+
def setup_recursive_recovery_tests
|
93
|
+
@paranoid_time_object = ParanoidTime.not_deleted.first
|
94
|
+
|
95
|
+
@paranoid_boolean_count = ParanoidBoolean.not_deleted.count
|
96
|
+
|
97
|
+
assert_equal 0, ParanoidHasManyDependant.not_deleted.count
|
98
|
+
assert_equal 0, ParanoidBelongsDependant.not_deleted.count
|
99
|
+
|
100
|
+
(1..3).each do |i|
|
101
|
+
has_many_object = @paranoid_time_object.paranoid_has_many_dependants.create(:name => "has_many_#{i}")
|
102
|
+
has_many_object.create_paranoid_belongs_dependant(:name => "belongs_to_#{i}")
|
103
|
+
has_many_object.save
|
104
|
+
|
105
|
+
paranoid_boolean = @paranoid_time_object.paranoid_booleans.create(:name => "boolean_#{i}")
|
106
|
+
paranoid_boolean.create_paranoid_has_one_dependant(:name => "has_one_#{i}")
|
107
|
+
paranoid_boolean.save
|
108
|
+
|
109
|
+
@paranoid_time_object.not_paranoids.create(:name => "not_paranoid_a#{i}")
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
@paranoid_time_object.create_not_paranoid(:name => "not_paranoid_belongs_to")
|
114
|
+
|
115
|
+
@paranoid_time_object.create_has_one_not_paranoid(:name => "has_one_not_paranoid")
|
116
|
+
|
117
|
+
assert_equal 3, ParanoidTime.not_deleted.count
|
118
|
+
assert_equal 3, ParanoidHasManyDependant.not_deleted.count
|
119
|
+
assert_equal 3, ParanoidBelongsDependant.not_deleted.count
|
120
|
+
assert_equal 3, ParanoidHasOneDependant.not_deleted.count
|
121
|
+
assert_equal 5, NotParanoid.count
|
122
|
+
assert_equal 1, HasOneNotParanoid.count
|
123
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.not_deleted.count
|
124
|
+
|
125
|
+
@paranoid_time_object.destroy
|
126
|
+
@paranoid_time_object.reload
|
127
|
+
|
128
|
+
assert_equal 2, ParanoidTime.not_deleted.count
|
129
|
+
assert_equal 0, ParanoidHasManyDependant.not_deleted.count
|
130
|
+
assert_equal 0, ParanoidBelongsDependant.not_deleted.count
|
131
|
+
assert_equal 0, ParanoidHasOneDependant.not_deleted.count
|
132
|
+
|
133
|
+
assert_equal 1, NotParanoid.count
|
134
|
+
assert_equal 0, HasOneNotParanoid.count
|
135
|
+
assert_equal @paranoid_boolean_count, ParanoidBoolean.not_deleted.count
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_recursive_recovery
|
139
|
+
setup_recursive_recovery_tests
|
140
|
+
|
141
|
+
@paranoid_time_object.recover(:recursive => true)
|
142
|
+
|
143
|
+
assert_equal 3, ParanoidTime.not_deleted.count
|
144
|
+
assert_equal 3, ParanoidHasManyDependant.not_deleted.count
|
145
|
+
assert_equal 3, ParanoidBelongsDependant.not_deleted.count
|
146
|
+
assert_equal 3, ParanoidHasOneDependant.not_deleted.count
|
147
|
+
assert_equal 1, NotParanoid.count
|
148
|
+
assert_equal 0, HasOneNotParanoid.count
|
149
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.not_deleted.count
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_non_recursive_recovery
|
153
|
+
setup_recursive_recovery_tests
|
154
|
+
|
155
|
+
@paranoid_time_object.recover(:recursive => false)
|
156
|
+
|
157
|
+
assert_equal 3, ParanoidTime.not_deleted.count
|
158
|
+
assert_equal 0, ParanoidHasManyDependant.not_deleted.count
|
159
|
+
assert_equal 0, ParanoidBelongsDependant.not_deleted.count
|
160
|
+
assert_equal 0, ParanoidHasOneDependant.not_deleted.count
|
161
|
+
assert_equal 1, NotParanoid.count
|
162
|
+
assert_equal 0, HasOneNotParanoid.count
|
163
|
+
assert_equal @paranoid_boolean_count, ParanoidBoolean.not_deleted.count
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_deleted?
|
167
|
+
ParanoidTime.not_deleted.first.destroy
|
168
|
+
assert ParanoidTime.with_deleted.first.deleted?
|
169
|
+
|
170
|
+
ParanoidString.not_deleted.first.destroy
|
171
|
+
assert ParanoidString.with_deleted.first.deleted?
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_paranoid_destroy_callbacks
|
175
|
+
@paranoid_with_callback = ParanoidWithCallback.not_deleted.first
|
176
|
+
ParanoidWithCallback.transaction do
|
177
|
+
@paranoid_with_callback.destroy
|
178
|
+
end
|
179
|
+
|
180
|
+
assert @paranoid_with_callback.called_before_destroy
|
181
|
+
assert @paranoid_with_callback.called_after_destroy
|
182
|
+
assert @paranoid_with_callback.called_after_commit_on_destroy
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_hard_destroy_callbacks
|
186
|
+
@paranoid_with_callback = ParanoidWithCallback.not_deleted.first
|
187
|
+
|
188
|
+
ParanoidWithCallback.transaction do
|
189
|
+
@paranoid_with_callback.destroy!
|
190
|
+
end
|
191
|
+
|
192
|
+
assert @paranoid_with_callback.called_before_destroy
|
193
|
+
assert @paranoid_with_callback.called_after_destroy
|
194
|
+
assert @paranoid_with_callback.called_after_commit_on_destroy
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_recovery_callbacks
|
198
|
+
@paranoid_with_callback = ParanoidWithCallback.not_deleted.first
|
199
|
+
|
200
|
+
ParanoidWithCallback.transaction do
|
201
|
+
@paranoid_with_callback.destroy
|
202
|
+
|
203
|
+
assert_nil @paranoid_with_callback.called_before_recover
|
204
|
+
assert_nil @paranoid_with_callback.called_after_recover
|
205
|
+
|
206
|
+
@paranoid_with_callback.recover
|
207
|
+
end
|
208
|
+
|
209
|
+
assert @paranoid_with_callback.called_before_recover
|
210
|
+
assert @paranoid_with_callback.called_after_recover
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class ValidatesUniquenessTest < ParanoidBaseTest
|
215
|
+
def test_should_include_deleted_by_default
|
216
|
+
ParanoidTime.new(:name => 'paranoid').tap do |record|
|
217
|
+
assert !record.valid?
|
218
|
+
ParanoidTime.not_deleted.first.destroy
|
219
|
+
assert !record.valid?
|
220
|
+
ParanoidTime.only_deleted.first.destroy!
|
221
|
+
assert record.valid?
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_should_validate_without_deleted
|
226
|
+
ParanoidBoolean.new(:name => 'paranoid').tap do |record|
|
227
|
+
ParanoidBoolean.not_deleted.first.destroy
|
228
|
+
assert record.valid?
|
229
|
+
ParanoidBoolean.only_deleted.first.destroy!
|
230
|
+
assert record.valid?
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class AssociationsTest < ParanoidBaseTest
|
236
|
+
def test_removal_with_associations
|
237
|
+
# This test shows that the current implementation doesn't handle
|
238
|
+
# assciation deletion correctly (when hard deleting via parent-object)
|
239
|
+
paranoid_company_1 = ParanoidDestroyCompany.create! :name => "ParanoidDestroyCompany #1"
|
240
|
+
paranoid_company_2 = ParanoidDeleteCompany.create! :name => "ParanoidDestroyCompany #1"
|
241
|
+
paranoid_company_1.paranoid_products.create! :name => "ParanoidProduct #1"
|
242
|
+
paranoid_company_2.paranoid_products.create! :name => "ParanoidProduct #2"
|
243
|
+
|
244
|
+
assert_equal 1, ParanoidDestroyCompany.not_deleted.count
|
245
|
+
assert_equal 1, ParanoidDeleteCompany.not_deleted.count
|
246
|
+
assert_equal 2, ParanoidProduct.not_deleted.count
|
247
|
+
|
248
|
+
ParanoidDestroyCompany.not_deleted.first.destroy
|
249
|
+
assert_equal 0, ParanoidDestroyCompany.not_deleted.count
|
250
|
+
assert_equal 1, ParanoidProduct.not_deleted.count
|
251
|
+
assert_equal 1, ParanoidDestroyCompany.with_deleted.count
|
252
|
+
assert_equal 2, ParanoidProduct.with_deleted.count
|
253
|
+
|
254
|
+
ParanoidDestroyCompany.with_deleted.first.destroy!
|
255
|
+
assert_equal 0, ParanoidDestroyCompany.not_deleted.count
|
256
|
+
assert_equal 1, ParanoidProduct.not_deleted.count
|
257
|
+
assert_equal 0, ParanoidDestroyCompany.with_deleted.count
|
258
|
+
assert_equal 1, ParanoidProduct.with_deleted.count
|
259
|
+
|
260
|
+
ParanoidDeleteCompany.with_deleted.first.destroy!
|
261
|
+
assert_equal 0, ParanoidDeleteCompany.not_deleted.count
|
262
|
+
assert_equal 0, ParanoidProduct.not_deleted.count
|
263
|
+
assert_equal 0, ParanoidDeleteCompany.with_deleted.count
|
264
|
+
assert_equal 0, ParanoidProduct.with_deleted.count
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class InheritanceTest < ParanoidBaseTest
|
269
|
+
def test_destroy_dependents_with_inheritance
|
270
|
+
has_many_inherited_super_paranoidz = HasManyInheritedSuperParanoidz.new
|
271
|
+
has_many_inherited_super_paranoidz.save
|
272
|
+
has_many_inherited_super_paranoidz.super_paranoidz.create
|
273
|
+
assert_nothing_raised(NoMethodError) { has_many_inherited_super_paranoidz.destroy }
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_class_instance_variables_are_inherited
|
277
|
+
assert_nothing_raised(ActiveRecord::StatementInvalid) { InheritedParanoid.paranoid_column }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
class ParanoidObserverTest < ParanoidBaseTest
|
282
|
+
|
283
|
+
def test_called_observer_methods
|
284
|
+
@subject = ParanoidWithCallback.new
|
285
|
+
@subject.save
|
286
|
+
|
287
|
+
assert_nil ParanoidObserver.instance.called_before_recover
|
288
|
+
assert_nil ParanoidObserver.instance.called_after_recover
|
289
|
+
|
290
|
+
ParanoidWithCallback.not_deleted.find(@subject.id).recover
|
291
|
+
|
292
|
+
assert_equal @subject, ParanoidObserver.instance.called_before_recover
|
293
|
+
assert_equal @subject, ParanoidObserver.instance.called_after_recover
|
294
|
+
end
|
295
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_record'
|
5
|
+
require 'active_model'
|
6
|
+
|
7
|
+
$:.unshift "#{File.dirname(__FILE__)}/../"
|
8
|
+
$:.unshift "#{File.dirname(__FILE__)}/../lib/"
|
9
|
+
$:.unshift "#{File.dirname(__FILE__)}/../lib/validations"
|
10
|
+
|
11
|
+
require 'init'
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
14
|
+
|
15
|
+
def setup_db
|
16
|
+
ActiveRecord::Schema.define(:version => 1) do
|
17
|
+
create_table :paranoid_times do |t|
|
18
|
+
t.string :name
|
19
|
+
t.datetime :deleted_at
|
20
|
+
t.integer :paranoid_belongs_dependant_id
|
21
|
+
t.integer :not_paranoid_id
|
22
|
+
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table :paranoid_booleans do |t|
|
27
|
+
t.string :name
|
28
|
+
t.boolean :is_deleted
|
29
|
+
t.integer :paranoid_time_id
|
30
|
+
|
31
|
+
t.timestamps
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table :paranoid_strings do |t|
|
35
|
+
t.string :name
|
36
|
+
t.string :deleted
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :not_paranoids do |t|
|
40
|
+
t.string :name
|
41
|
+
t.integer :paranoid_time_id
|
42
|
+
|
43
|
+
t.timestamps
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :has_one_not_paranoids do |t|
|
47
|
+
t.string :name
|
48
|
+
t.integer :paranoid_time_id
|
49
|
+
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table :paranoid_has_many_dependants do |t|
|
54
|
+
t.string :name
|
55
|
+
t.datetime :deleted_at
|
56
|
+
t.integer :paranoid_time_id
|
57
|
+
t.integer :paranoid_belongs_dependant_id
|
58
|
+
|
59
|
+
t.timestamps
|
60
|
+
end
|
61
|
+
|
62
|
+
create_table :paranoid_belongs_dependants do |t|
|
63
|
+
t.string :name
|
64
|
+
t.datetime :deleted_at
|
65
|
+
|
66
|
+
t.timestamps
|
67
|
+
end
|
68
|
+
|
69
|
+
create_table :paranoid_has_one_dependants do |t|
|
70
|
+
t.string :name
|
71
|
+
t.datetime :deleted_at
|
72
|
+
t.integer :paranoid_boolean_id
|
73
|
+
|
74
|
+
t.timestamps
|
75
|
+
end
|
76
|
+
|
77
|
+
create_table :paranoid_with_callbacks do |t|
|
78
|
+
t.string :name
|
79
|
+
t.datetime :deleted_at
|
80
|
+
|
81
|
+
t.timestamps
|
82
|
+
end
|
83
|
+
|
84
|
+
create_table :paranoid_destroy_companies do |t|
|
85
|
+
t.string :name
|
86
|
+
t.datetime :deleted_at
|
87
|
+
|
88
|
+
t.timestamps
|
89
|
+
end
|
90
|
+
|
91
|
+
create_table :paranoid_delete_companies do |t|
|
92
|
+
t.string :name
|
93
|
+
t.datetime :deleted_at
|
94
|
+
|
95
|
+
t.timestamps
|
96
|
+
end
|
97
|
+
|
98
|
+
create_table :paranoid_products do |t|
|
99
|
+
t.integer :paranoid_destroy_company_id
|
100
|
+
t.integer :paranoid_delete_company_id
|
101
|
+
t.string :name
|
102
|
+
t.datetime :deleted_at
|
103
|
+
|
104
|
+
t.timestamps
|
105
|
+
end
|
106
|
+
|
107
|
+
create_table :super_paranoids do |t|
|
108
|
+
t.string :type
|
109
|
+
t.references :has_many_inherited_super_paranoidz
|
110
|
+
t.datetime :deleted_at
|
111
|
+
|
112
|
+
t.timestamps
|
113
|
+
end
|
114
|
+
|
115
|
+
create_table :has_many_inherited_super_paranoidzs do |t|
|
116
|
+
t.references :super_paranoidz
|
117
|
+
t.datetime :deleted_at
|
118
|
+
|
119
|
+
t.timestamps
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def teardown_db
|
125
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
126
|
+
ActiveRecord::Base.connection.drop_table(table)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class ParanoidTime < ActiveRecord::Base
|
131
|
+
acts_as_paranoid
|
132
|
+
validates_uniqueness_of :name
|
133
|
+
|
134
|
+
has_many :paranoid_has_many_dependants, :dependent => :destroy
|
135
|
+
has_many :paranoid_booleans, :dependent => :destroy
|
136
|
+
has_many :not_paranoids, :dependent => :delete_all
|
137
|
+
|
138
|
+
has_one :has_one_not_paranoid, :dependent => :destroy
|
139
|
+
|
140
|
+
belongs_to :not_paranoid, :dependent => :destroy
|
141
|
+
end
|
142
|
+
|
143
|
+
class ParanoidBoolean < ActiveRecord::Base
|
144
|
+
acts_as_paranoid :column_type => "boolean", :column => "is_deleted"
|
145
|
+
validates_as_paranoid
|
146
|
+
|
147
|
+
belongs_to :paranoid_time
|
148
|
+
has_one :paranoid_has_one_dependant, :dependent => :destroy
|
149
|
+
end
|
150
|
+
|
151
|
+
class ParanoidString < ActiveRecord::Base
|
152
|
+
acts_as_paranoid :column_type => "string", :column => "deleted", :deleted_value => "dead"
|
153
|
+
end
|
154
|
+
|
155
|
+
class NotParanoid < ActiveRecord::Base
|
156
|
+
end
|
157
|
+
|
158
|
+
class HasOneNotParanoid < ActiveRecord::Base
|
159
|
+
end
|
160
|
+
|
161
|
+
class ParanoidHasManyDependant < ActiveRecord::Base
|
162
|
+
acts_as_paranoid
|
163
|
+
belongs_to :paranoid_time
|
164
|
+
|
165
|
+
belongs_to :paranoid_belongs_dependant, :dependent => :destroy
|
166
|
+
end
|
167
|
+
|
168
|
+
class ParanoidBelongsDependant < ActiveRecord::Base
|
169
|
+
acts_as_paranoid
|
170
|
+
|
171
|
+
has_many :paranoid_has_many_dependants
|
172
|
+
end
|
173
|
+
|
174
|
+
class ParanoidHasOneDependant < ActiveRecord::Base
|
175
|
+
acts_as_paranoid
|
176
|
+
|
177
|
+
belongs_to :paranoid_boolean
|
178
|
+
end
|
179
|
+
|
180
|
+
class ParanoidWithCallback < ActiveRecord::Base
|
181
|
+
acts_as_paranoid
|
182
|
+
|
183
|
+
attr_accessor :called_before_destroy, :called_after_destroy, :called_after_commit_on_destroy
|
184
|
+
attr_accessor :called_before_recover, :called_after_recover
|
185
|
+
|
186
|
+
before_destroy :call_me_before_destroy
|
187
|
+
after_destroy :call_me_after_destroy
|
188
|
+
|
189
|
+
after_commit :call_me_after_commit_on_destroy, :on => :destroy
|
190
|
+
|
191
|
+
before_recover :call_me_before_recover
|
192
|
+
after_recover :call_me_after_recover
|
193
|
+
|
194
|
+
def initialize(*attrs)
|
195
|
+
@called_before_destroy = @called_after_destroy = @called_after_commit_on_destroy = false
|
196
|
+
super(*attrs)
|
197
|
+
end
|
198
|
+
|
199
|
+
def call_me_before_destroy
|
200
|
+
@called_before_destroy = true
|
201
|
+
end
|
202
|
+
|
203
|
+
def call_me_after_destroy
|
204
|
+
@called_after_destroy = true
|
205
|
+
end
|
206
|
+
|
207
|
+
def call_me_after_commit_on_destroy
|
208
|
+
@called_after_commit_on_destroy = true
|
209
|
+
end
|
210
|
+
|
211
|
+
def call_me_before_recover
|
212
|
+
@called_before_recover = true
|
213
|
+
end
|
214
|
+
|
215
|
+
def call_me_after_recover
|
216
|
+
@called_after_recover = true
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class ParanoidDestroyCompany < ActiveRecord::Base
|
221
|
+
acts_as_paranoid
|
222
|
+
validates :name, :presence => true
|
223
|
+
has_many :paranoid_products, :dependent => :destroy
|
224
|
+
end
|
225
|
+
|
226
|
+
class ParanoidDeleteCompany < ActiveRecord::Base
|
227
|
+
acts_as_paranoid
|
228
|
+
validates :name, :presence => true
|
229
|
+
has_many :paranoid_products, :dependent => :delete_all
|
230
|
+
end
|
231
|
+
|
232
|
+
class ParanoidProduct < ActiveRecord::Base
|
233
|
+
acts_as_paranoid
|
234
|
+
belongs_to :paranoid_destroy_company
|
235
|
+
belongs_to :paranoid_delete_company
|
236
|
+
validates_presence_of :name
|
237
|
+
end
|
238
|
+
|
239
|
+
class SuperParanoid < ActiveRecord::Base
|
240
|
+
acts_as_paranoid
|
241
|
+
belongs_to :has_many_inherited_super_paranoidz
|
242
|
+
end
|
243
|
+
|
244
|
+
class HasManyInheritedSuperParanoidz < ActiveRecord::Base
|
245
|
+
has_many :super_paranoidz, :class_name => "InheritedParanoid", :dependent => :destroy
|
246
|
+
end
|
247
|
+
|
248
|
+
class InheritedParanoid < SuperParanoid
|
249
|
+
acts_as_paranoid
|
250
|
+
end
|
251
|
+
|
252
|
+
class ParanoidObserver < ActiveRecord::Observer
|
253
|
+
observe :paranoid_with_callback
|
254
|
+
|
255
|
+
attr_accessor :called_before_recover, :called_after_recover
|
256
|
+
|
257
|
+
def before_recover(paranoid_object)
|
258
|
+
self.called_before_recover = paranoid_object
|
259
|
+
end
|
260
|
+
|
261
|
+
def after_recover(paranoid_object)
|
262
|
+
self.called_after_recover = paranoid_object
|
263
|
+
end
|
264
|
+
|
265
|
+
def reset
|
266
|
+
self.called_before_recover = nil
|
267
|
+
self.called_after_recover = nil
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
ParanoidWithCallback.add_observer(ParanoidObserver.instance)
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails3_acts_as_paranoid_create
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Goncalo Silva
|
14
|
+
- Philipp Ullmann
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-09-06 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
type: :runtime
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
30
|
+
segments:
|
31
|
+
- 3
|
32
|
+
- 0
|
33
|
+
version: "3.0"
|
34
|
+
name: activerecord
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
prerelease: false
|
38
|
+
type: :development
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 59
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 9
|
48
|
+
- 0
|
49
|
+
version: 0.9.0
|
50
|
+
name: rake
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
prerelease: false
|
54
|
+
type: :development
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 27
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
version: 1.3.0
|
66
|
+
name: sqlite3-ruby
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
prerelease: false
|
70
|
+
type: :development
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 3
|
79
|
+
- 1
|
80
|
+
- 0
|
81
|
+
version: 3.1.0
|
82
|
+
name: activesupport
|
83
|
+
version_requirements: *id004
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
prerelease: false
|
86
|
+
type: :development
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 35
|
93
|
+
segments:
|
94
|
+
- 3
|
95
|
+
- 9
|
96
|
+
- 0
|
97
|
+
version: 3.9.0
|
98
|
+
name: rdoc
|
99
|
+
version_requirements: *id005
|
100
|
+
description: Active Record (>=3.0) plugin which allows you to hide and restore records without actually deleting them. Check its GitHub page for more in-depth information.
|
101
|
+
email:
|
102
|
+
- goncalossilva@gmail.com
|
103
|
+
- philipp.ullmann@create.at
|
104
|
+
executables: []
|
105
|
+
|
106
|
+
extensions: []
|
107
|
+
|
108
|
+
extra_rdoc_files: []
|
109
|
+
|
110
|
+
files:
|
111
|
+
- .gitignore
|
112
|
+
- Gemfile
|
113
|
+
- Gemfile.lock
|
114
|
+
- LICENSE
|
115
|
+
- README.markdown
|
116
|
+
- Rakefile
|
117
|
+
- init.rb
|
118
|
+
- lib/rails3_acts_as_paranoid.rb
|
119
|
+
- lib/rails3_acts_as_paranoid/version.rb
|
120
|
+
- lib/validations/uniqueness_without_deleted.rb
|
121
|
+
- rails3_acts_as_paranoid.gemspec
|
122
|
+
- test/rails3_acts_as_paranoid_test.rb
|
123
|
+
- test/test_helper.rb
|
124
|
+
homepage: http://github.com/goncalossilva/rails3_acts_as_paranoid
|
125
|
+
licenses: []
|
126
|
+
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 3
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
version: "0"
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 23
|
147
|
+
segments:
|
148
|
+
- 1
|
149
|
+
- 3
|
150
|
+
- 6
|
151
|
+
version: 1.3.6
|
152
|
+
requirements: []
|
153
|
+
|
154
|
+
rubyforge_project: rails3_acts_as_paranoid_create
|
155
|
+
rubygems_version: 1.8.8
|
156
|
+
signing_key:
|
157
|
+
specification_version: 3
|
158
|
+
summary: Active Record (>=3.0) plugin which allows you to hide and restore records without actually deleting them.
|
159
|
+
test_files:
|
160
|
+
- test/rails3_acts_as_paranoid_test.rb
|
161
|
+
- test/test_helper.rb
|