delete_paranoid 0.0.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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