delete_paranoid 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -6,11 +6,25 @@ Soft Delete ActiveRecord instances.
6
6
  class Blog < ActiveRecord::Base
7
7
  acts_as_paranoid
8
8
  end
9
+ blog = Blog.create! :name => 'foo'
9
10
 
10
- blog = Blog.create :name => 'foo'
11
- blog.destroy #soft delete the record
11
+ # soft delete the instance
12
+ blog.destroy
13
+
14
+ # query database for results *including* soft deleted objects
15
+ Blog.with_deleted do
16
+ Blog.all
17
+ end
18
+
19
+ # permenantly delete the instance from the database
20
+ Blog.delete! blog.id
12
21
 
13
22
  == Features
23
+ * simple configuration
24
+ * preserves existing ActiveRecord API. No magical new API's to use when you want to soft delete a record
25
+ * automatically exclude soft deleted records from database queries (by default)
26
+ * support for querying database for all records (including soft deleted ones)
27
+ * support for permenantly deleting record from database
14
28
 
15
29
  == Contributing
16
30
 
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency(%q<bundler>, [">= 0"])
21
21
  s.add_development_dependency(%q<sqlite3-ruby>, ["~> 1.3.2"])
22
22
  s.add_development_dependency(%q<ruby-debug>, [">= 0"])
23
+ s.add_development_dependency(%q<timecop>, [">= 0"])
23
24
 
24
25
  s.files = `git ls-files`.split("\n")
25
26
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -1,53 +1,42 @@
1
+ require 'active_support/all'
1
2
  require 'active_record'
2
3
 
4
+ module ActiveRecord
5
+ class Relation
6
+ alias_method :delete_all!, :delete_all
7
+ def delete_all(conditions = nil)
8
+ delete_all!(conditions) unless @klass.paranoid?
9
+ update_all({:deleted_at => Time.now.utc}, conditions)
10
+ end
11
+ end
12
+ end
13
+
3
14
  module DeleteParanoid
4
15
  module ActiveRecordExtensions
5
16
  def acts_as_paranoid
6
- class << self
7
- alias_method :delete_all!, :delete_all
8
- end
9
- alias_method :destroy!, :destroy
10
17
  default_scope where(:deleted_at => nil)
18
+
11
19
  extend DeleteParanoid::ClassMethods
12
- include DeleteParanoid::InstanceMethods
20
+ end
21
+
22
+ def paranoid?
23
+ false
13
24
  end
14
25
  end
15
-
26
+
16
27
  module ClassMethods
28
+ # permenantly delete the record from the database
29
+ def delete!(id_or_array)
30
+ where(self.primary_key => id_or_array).delete_all!
31
+ end
32
+ # allow for queries within block to find soft deleted records
17
33
  def with_deleted
18
34
  self.unscoped do
19
35
  yield
20
36
  end
21
37
  end
22
-
23
- def delete_all(conditions = nil)
24
- update_all ["deleted_at = ?", Time.now.utc], conditions
25
- end
26
-
27
- def destroy_all!(conditions = nil)
28
- if conditions
29
- where(conditions).destroy_all!
30
- else
31
- to_a.each {|object| object.destroy! }.tap { reset }
32
- end
33
- end
34
- end
35
-
36
- module InstanceMethods
37
- def destroy
38
- if persisted?
39
- with_transaction_returning_status do
40
- _run_destroy_callbacks do
41
- self.deleted_at = Time.now.utc
42
- self.class.delete_all :id => self.id
43
- @destroyed = true
44
- end
45
- end
46
- else
47
- @destroyed = true
48
- end
49
-
50
- freeze
38
+ def paranoid?
39
+ true
51
40
  end
52
41
  end
53
42
  end
@@ -1,3 +1,3 @@
1
1
  module DeleteParanoid
2
- VERSION = "0.0.3"
2
+ VERSION = "1.0.0"
3
3
  end
data/test/helper.rb CHANGED
@@ -11,6 +11,7 @@ require 'test/unit'
11
11
  require 'shoulda'
12
12
  require 'mocha'
13
13
  require "ruby-debug"
14
+ require 'timecop'
14
15
 
15
16
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
17
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -20,22 +21,19 @@ require 'database_setup'
20
21
  class Test::Unit::TestCase
21
22
 
22
23
  def self.should_soft_destroy(subject)
23
- should "assign deleted_at on #{subject}" do
24
- assert_not_nil instance_variable_get(:"@#{subject}").deleted_at
25
- end
26
24
  should "set #{subject} to destroyed" do
27
25
  assert instance_variable_get(:"@#{subject}").destroyed?
28
26
  end
29
27
  should "freeze #{subject}" do
30
28
  assert instance_variable_get(:"@#{subject}").frozen?
31
29
  end
32
- should "not be found normally" do
30
+ should "not find #{subject} normally" do
33
31
  destroyed_subject = instance_variable_get(:"@#{subject}")
34
32
  assert_raises ActiveRecord::RecordNotFound do
35
33
  destroyed_subject.class.find destroyed_subject.id
36
34
  end
37
35
  end
38
- should "be found when in with_deleted block" do
36
+ should "find #{subject} when in with_deleted block" do
39
37
  destroyed_subject = instance_variable_get(:"@#{subject}")
40
38
  destroyed_subject.class.with_deleted do
41
39
  assert_nothing_raised ActiveRecord::RecordNotFound do
@@ -46,13 +44,13 @@ class Test::Unit::TestCase
46
44
  end
47
45
 
48
46
  def self.should_hard_destroy(subject)
49
- should "not be retrievable" do
47
+ should "not find #{subject} normally" do
50
48
  destroyed_subject = instance_variable_get(:"@#{subject}")
51
49
  assert_raises ActiveRecord::RecordNotFound do
52
50
  destroyed_subject.class.find destroyed_subject.id
53
51
  end
54
52
  end
55
- should "not be retrievable in with_deleted block" do
53
+ should "not find #{subject} in with_deleted block" do
56
54
  destroyed_subject = instance_variable_get(:"@#{subject}")
57
55
  destroyed_subject.class.with_deleted do
58
56
  assert_raises ActiveRecord::RecordNotFound do
@@ -11,39 +11,84 @@ class TestDeleteParanoid < Test::Unit::TestCase
11
11
  class Comment < ActiveRecord::Base
12
12
  acts_as_paranoid
13
13
  attr_accessible :text
14
+ belongs_to :blog
14
15
  include CallbackTester
15
16
  end
16
-
17
- context 'with paranoid instance' do
18
- setup do
19
- @blog = Blog.new
17
+
18
+ class User < ActiveRecord::Base
19
+ end
20
+ context 'with non-paranoid activerecord class' do
21
+ should 'not be paranoid' do
22
+ assert !User.paranoid?
20
23
  end
21
- should 'have destroy! method' do
22
- assert @blog.respond_to? :destroy!
24
+ end
25
+ context 'with paranoid activerecord class' do
26
+ should 'be paranoid' do
27
+ assert Blog.paranoid?
23
28
  end
24
29
  end
25
- context 'with paranoid class' do
26
- should 'have delete_all! method' do
27
- assert Blog.respond_to? :delete_all!
30
+ context 'with instance of paranoid class' do
31
+ setup do
32
+ @blog = Blog.create! :title => 'foo'
28
33
  end
29
-
30
- context 'when on instance destroyed softly' do
34
+ context 'when destroying instance' do
31
35
  setup do
32
- @blog = Blog.create! :title => 'foo'
33
- @blog.destroy
36
+ @now = Time.now.utc
37
+ Timecop.travel @now do
38
+ @blog.destroy
39
+ end
34
40
  end
35
41
 
36
42
  should_soft_destroy :blog
37
43
  should_trigger_destroy_callbacks :blog
38
44
  should_not_trigger_update_callbacks :blog
45
+ should 'save deleted_at timestamp on database record' do
46
+ blog = Blog.find_by_sql(['SELECT deleted_at FROM blogs WHERE id = ?', @blog.id]).first
47
+ assert_not_nil blog
48
+ assert_not_nil blog.deleted_at
49
+ assert_equal @now.to_i, blog.deleted_at.to_i
50
+ end
39
51
  end
40
-
41
- context 'when an instance with dependents is destroyed softly' do
52
+ context 'when destroying instance with destroy_all' do
42
53
  setup do
43
- @comment = Comment.create! :text => 'bar'
44
- @blog = Blog.create!(:title => 'foo').tap do |blog|
45
- blog.comments << @comment
54
+ Blog.destroy_all :id => @blog.id
55
+ end
56
+ should "not find instance normally" do
57
+ assert_raises ActiveRecord::RecordNotFound do
58
+ Blog.find @blog.id
59
+ end
60
+ end
61
+ should "find instance when in with_deleted block" do
62
+ Blog.with_deleted do
63
+ assert_nothing_raised ActiveRecord::RecordNotFound do
64
+ Blog.find @blog.id
65
+ end
46
66
  end
67
+ end
68
+ end
69
+ context "when destroying instance with delete_all!" do
70
+ setup do
71
+ @blog = Blog.create! :title => 'foo'
72
+ Blog.where({:id => @blog.id}).delete_all!
73
+ end
74
+ should_hard_destroy :blog
75
+ end
76
+ context "when destroying instance with delete!" do
77
+ setup do
78
+ @blog = Blog.create! :title => 'foo'
79
+ Blog.delete! @blog.id
80
+ end
81
+ should_hard_destroy :blog
82
+ end
83
+ end
84
+
85
+ context 'with paranoid instance that has dependents' do
86
+ setup do
87
+ @blog = Blog.create!(:title => 'foo')
88
+ @comment = @blog.comments.create! :text => 'bar'
89
+ end
90
+ context 'when destroying paranoid instance' do
91
+ setup do
47
92
  @blog.destroy
48
93
  end
49
94
 
@@ -53,36 +98,17 @@ class TestDeleteParanoid < Test::Unit::TestCase
53
98
 
54
99
  should_soft_destroy :comment
55
100
  should_trigger_destroy_callbacks :comment
56
- # should_not_trigger_update_callbacks :comment
57
101
  end
58
-
59
- context "when on instance destroyed hardly" do
60
- setup do
61
- @blog = Blog.create! :title => 'foo'
62
- @blog.destroy!
63
- end
64
-
65
- should_hard_destroy :blog
66
- should_trigger_destroy_callbacks :blog
67
- should_not_trigger_update_callbacks :blog
68
- end
69
-
70
- context 'when an instance with dependents is destroyed hardly' do
102
+ context 'when destroying paranoid instance with delete_all!' do
71
103
  setup do
72
- @comment = Comment.create! :text => 'bar'
73
- @blog = Blog.create!(:title => 'foo').tap do |blog|
74
- blog.comments << @comment
75
- end
76
- @blog.destroy!
104
+ Blog.where({:id => @blog.id}).delete_all!
77
105
  end
78
106
 
79
107
  should_hard_destroy :blog
80
- should_trigger_destroy_callbacks :blog
81
- should_not_trigger_update_callbacks :blog
82
- # should_hard_destroy :comment
83
- should_trigger_destroy_callbacks :comment
84
- # should_not_trigger_update_callbacks :comment
108
+ should 'not destroy associated comment' do
109
+ assert_not_nil @comment.reload
110
+ assert_nil @comment.blog
111
+ end
85
112
  end
86
-
87
113
  end
88
114
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delete_paranoid
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
+ - 1
7
8
  - 0
8
9
  - 0
9
- - 3
10
- version: 0.0.3
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Sonnek
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-04 00:00:00 -05:00
18
+ date: 2011-04-05 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -106,6 +106,20 @@ dependencies:
106
106
  version: "0"
107
107
  type: :development
108
108
  version_requirements: *id006
109
+ - !ruby/object:Gem::Dependency
110
+ name: timecop
111
+ prerelease: false
112
+ requirement: &id007 !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ type: :development
122
+ version_requirements: *id007
109
123
  description: flag database records as deleted and hide them from subsequent queries
110
124
  email:
111
125
  - ryan@codecrate.com