delete_softly 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -2,19 +2,30 @@ DeleteSoftly
2
2
  ============
3
3
 
4
4
  Add soft delete functionality to ActiveRecord models. Important information:
5
- This is Rails3 only, no backwards compatibility.
5
+ This is Rails3 only, no backwards compatibility. Important features are
6
+ * It works through relations
7
+ * papertrail support
8
+
9
+ Tested with Postgresql
10
+
11
+ New in version 0.3
12
+ * without_deleted, same as active, but not meant to be overwritten
13
+ * deleted is back, misteriously disappeared in version 0.2
6
14
 
7
15
  Example
8
16
  =======
9
17
  class Post
18
+ # Replace normal behavior of object completely
10
19
  delete_softly
11
20
  end
12
21
 
13
22
  class Comment
23
+ # Rely on calling active for this object when needed
14
24
  delete_softly false
15
25
  end
16
26
 
17
27
  Now the following stuff works:
28
+ == The Post model ==
18
29
  p1 = Post.create
19
30
  p2 = Post.create
20
31
  Post.count #=> 2
@@ -29,5 +40,6 @@ Now the following stuff works:
29
40
  Comment.count #=> 2 (Since we added false)
30
41
  Comment.active.count #=> 1
31
42
 
43
+ See the rdoc for better examples and documentation
32
44
 
33
45
  Copyright (c) 2010 [Benjamin ter Kuile], released under the MIT license
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ Rake::TestTask.new(:test) do |t|
15
15
  end
16
16
 
17
17
  desc 'Echoe'
18
- Echoe.new('delete_softly', '0.0.2') do |p|
18
+ Echoe.new('delete_softly', '0.0.3') do |p|
19
19
  p.description = "Add soft delete functionality to your ActiveRecord models"
20
20
  p.url = "http://github.com/bterkuile/delete_softly"
21
21
  p.author = "Benjamin ter Kuile"
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{delete_softly}
5
- s.version = "0.0.2"
5
+ s.version = "0.0.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Benjamin ter Kuile"]
9
- s.date = %q{2010-11-05}
9
+ s.date = %q{2010-12-17}
10
10
  s.description = %q{Add soft delete functionality to your ActiveRecord models}
11
11
  s.email = %q{bterkuile@gmail.com}
12
12
  s.extra_rdoc_files = ["README", "lib/class_methods.rb", "lib/delete_softly.rb", "lib/instance_methods.rb"]
@@ -2,25 +2,25 @@ module DeleteSoftly
2
2
  module ClassMethods
3
3
 
4
4
  # Give the representation of items at a certain date/time.
5
- # class Item
5
+ # class Item < ActiveRecord::Base
6
6
  # delete_softly
7
7
  # end
8
8
  # Will result in:
9
9
  # Item.at_time(DateTime.parse('2010-01-01')) #=> (SELECT "items".* FROM "items" WHERE (((("items"."deleted_at" > '2010-01-01 00:00:00') OR ("items"."deleted_at" IS NULL)) AND ("items"."created_at" < '2010-01-01 00:00:00')))
10
- def at_date(date = Time.now.utc)
10
+ def at_time(date = Time.now.utc)
11
11
  with_deleted do
12
12
  where(({:deleted_at.gt => date} | {:deleted_at => nil}) & {:created_at.lt => date})
13
13
  end
14
14
  end
15
- alias_method :at_time, :at_date
15
+ alias_method :at_date, :at_time
16
16
 
17
17
  # Give the currently active items. When delete_soflty is added this is invoked by default
18
18
  # But when false is added, items are shown by default
19
- # class Item
19
+ # class Item < ActiveRecord::Base
20
20
  # delete_softly false
21
21
  # end
22
22
  # Or similar:
23
- # class Item
23
+ # class Item < ActiveRecord::Base
24
24
  # delete_softly :default => false
25
25
  # end
26
26
  # You need to call active on the model where you want to hide deleted items
@@ -30,8 +30,14 @@ module DeleteSoftly
30
30
  where(:deleted_at => nil)
31
31
  end
32
32
 
33
+ # Same as active, but not to be overwritten. Active might become with disabled => false
34
+ # or something like that. Without deleted should remain intact
35
+ def without_deleted
36
+ where(:deleted_at => nil)
37
+ end
38
+
33
39
  # Include deleted items when performing queries
34
- # class Item
40
+ # class Item < ActiveRecord::Base
35
41
  # default_scope order(:content)
36
42
  # delete_softly
37
43
  # end
@@ -42,7 +48,8 @@ module DeleteSoftly
42
48
  # Item.with_deleted do
43
49
  # Item.where(:content.matches => 'a%') #=> SELECT "items".* FROM "items" WHERE ("items"."content" ILIKE 'a%') ORDER BY "items"."content"
44
50
  # end
45
- # IHaveManyItems.items #=> SELECT "items".* FROM "items" WHERE ("items"."deleted_at" IS NULL) AND ("items".i_have_many_items_id = 1) ORDER BY "items"."content"
51
+ # IHaveManyItems.first.items #=> SELECT "items".* FROM "items" WHERE ("items"."deleted_at" IS NULL) AND ("items".i_have_many_items_id = 1) ORDER BY "items"."content"
52
+ # IHaveManyItems.first.items.with_deleted #=> SELECT "items".* FROM "items" WHERE ("items".i_have_many_items_id = 1) ORDER BY "items"."content"
46
53
  def with_deleted(&block)
47
54
  if scoped_methods.any? # There are scoped methods in place
48
55
 
@@ -63,7 +70,20 @@ module DeleteSoftly
63
70
  end
64
71
  end
65
72
 
66
- # Support for paper_trail if it is installed as well
73
+ def deleted
74
+ with_deleted.where(:deleted_at.ne => nil)
75
+ end
76
+
77
+ # Support for paper_trail if it is installed as well. Then you can use:
78
+ # class Post < ActiveRecord::Base
79
+ # default_scope order(:created_at)
80
+ # delete_softly
81
+ # has_paper_trail
82
+ # has_many :comments
83
+ # end
84
+ # Then
85
+ # Post.version_at(1.week.ago)
86
+ # Will return all post of one week ago, with the appropriate field values at that time
67
87
  def version_at(time)
68
88
  if respond_to?(:at_time)
69
89
  at_time(time).map{|i| i.respond_to?(:version_at) ? i.version_at(time) : i}.compact
@@ -1,4 +1,11 @@
1
1
  # DeleteSoftly
2
+ # This is a gem/plugin that adds soft delete functionality to ActiveRecord.
3
+ # Take a look at the
4
+ # DeleteSoftly::ArExtender
5
+ # DeleteSoftly::ClassMethods
6
+ # DeleteSoftly::InstanceMethods
7
+ # to get a feel of what is being done. This gem works through many relations
8
+
2
9
  require 'active_record'
3
10
  require 'meta_where'
4
11
  require 'class_methods'
@@ -8,26 +15,53 @@ module DeleteSoftly
8
15
 
9
16
  # Always have Model.active available. It is a good practice to use it
10
17
  # when you want the active records. Even use it when no logic is in
11
- # place yet.
18
+ # place yet. Now it is an alias for scoped, but can be overwritten
19
+ # for custom behaviour, for example:
20
+ # class Post < ActiveRecord::Base
21
+ # delete_softly
22
+ # has_many :comments
23
+ # def self.active
24
+ # super.where(:disabled.ne => true)
25
+ # end
26
+ # end
27
+ # class Comment < ActiveRecord::Base
28
+ # belongs_to :post
29
+ # end
30
+ # will result in:
31
+ # Post.all #=> SELECT * FROM posts WHERE deleted_at IS NULL;
32
+ # Post.active #=> SELECT * FROM posts WHERE deleted_at IS NULL AND disabled != 't';
33
+ # Comment.all #=> SELECT * FROM comments;
34
+ # Comment.active #=> SELECT * FROM comments;
12
35
  def active
13
36
  scoped
14
37
  end
15
38
 
16
39
  # Make the model delete softly. A deleted_at:datetime column is required
17
- # for this to work
18
- def delete_softly(options = {:default => :active})
40
+ # for this to work.
41
+ # The two most important differences are that it can be enforce on a model
42
+ # or be more free.
43
+ # class Post < ActiveRecord::Base
44
+ # delete_softly
45
+ # end
46
+ # will enforce soft delete on the post model. A deleted post will never appear,
47
+ # unless the explicit with_deleted is called. When the model is:
48
+ # class Post < ActiveRecord::Base
49
+ # delete_softly :enforce => false
50
+ # end
51
+ # An object will still be available after destroy is called,
52
+ def delete_softly(options = {:enforce => :active})
19
53
  # Make destroy! the old destroy
20
54
  alias_method :destroy!, :destroy
21
55
 
22
56
  include DeleteSoftly::InstanceMethods
23
57
  extend DeleteSoftly::ClassMethods
24
58
  # Support single argument
25
- # delete_softly :active
26
- # delete_softly :default => :with_deleted
27
- options = {:default => options } unless options.is_a?(Hash)
28
- if options[:default]
29
- if options[:default].is_a?(Symbol) && respond_to?(options[:default])
30
- default_scope send(options[:default])
59
+ # delete_softly :active # Same as :enforce => :active, default behaviour
60
+ # delete_softly :enforce=> :with_deleted # Same as without argument
61
+ options = {:enforce=> options } unless options.is_a?(Hash)
62
+ if options[:enforce]
63
+ if options[:enforce].is_a?(Symbol) && respond_to?(options[:enforce])
64
+ default_scope send(options[:enforce])
31
65
  else
32
66
  default_scope active
33
67
  end
@@ -1,6 +1,8 @@
1
1
  module DeleteSoftly
2
2
  module InstanceMethods
3
- def destroy
3
+
4
+ # Custom destroy method for models using delete_softly
5
+ def destroy
4
6
  if persisted?
5
7
  with_transaction_returning_status do
6
8
  _run_destroy_callbacks do
@@ -12,8 +14,21 @@ module DeleteSoftly
12
14
  @destroyed = true
13
15
  freeze
14
16
  end
15
-
17
+
18
+ # Revive a destroyed item. For a model like:
19
+ # class Post < ActiveRecord::Base
20
+ # delete_softly
21
+ # end
22
+ # Then the following can be done:
23
+ # p = Post.find(1)
24
+ # p.destroy
25
+ # p = Post.find(1) # raise error
26
+ # p = Post.with_deleted.find(1) # Original object but with deleted_at attribute set
27
+ # p.revive #=> deleted_at => nil
28
+ # p = Post.find(1) # business as usual
29
+ # If papertrail is used for this model it will not store a copy
16
30
  def revive
31
+ # Disable paper_trail when it is present and active
17
32
  if self.class.respond_to?(:paper_trail_active) && self.class.paper_trail_active
18
33
  self.class.paper_trail_off
19
34
  update_attribute :deleted_at, nil
@@ -2,7 +2,41 @@ require 'test_helper'
2
2
 
3
3
  class DeleteSoftlyTest < ActiveSupport::TestCase
4
4
  # Replace this with your real tests.
5
+ setup do
6
+ puts "New setup"
7
+ Post.with_deleted.delete_all
8
+ Comment.with_deleted.delete_all
9
+ Tag.delete_all
10
+ @post1 = Post.create(:title => "post1")
11
+ @post1_id = @post1.id
12
+ @comment1_1 = @post1.comments.create(:email => "comment1_1", :body => "Comment 1 for post 1")
13
+ @comment2_1 = @post1.comments.create("email" => "comment1_2", :body => "Comment 2 for post 1")
14
+
15
+ @post2 = Post.create(:title => "post2")
16
+ @post2_id = @post2.id
17
+ end
5
18
  test "the truth" do
6
19
  assert true
7
20
  end
21
+ test "two records available" do
22
+ assert_equal 2, Post.count
23
+ end
24
+
25
+ test "destroy count test" do
26
+ @post1.destroy
27
+ assert_equal 1, Post.count
28
+ assert_equal 2, Post.with_deleted.count
29
+ assert_nil Post.find_by_id(@post1_id)
30
+ @post1 = Post.with_deleted.find(@post1_id)
31
+ assert @post1.deleted_at
32
+ @post1.revive
33
+ assert 2, Post.count
34
+ end
35
+
36
+ test "deleted, without_deleted methods" do
37
+ assert_equal [@post1, @post2], Post.all.sort_by{|p| p.title}
38
+ @post1.destroy
39
+ assert_equal [@post2], Post.without_deleted.all
40
+ assert_equal [@post1], Post.deleted.all
41
+ end
8
42
  end
@@ -1,3 +1,57 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
+ require 'arel'
3
4
  require 'active_support'
5
+ require 'active_support/all'
6
+ require 'lib/delete_softly'
7
+ require 'sqlite3'
8
+
9
+
10
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
11
+ ActiveRecord::Base.establish_connection(
12
+ :adapter => 'sqlite3',
13
+ :database => 'test/db.sqlite3'
14
+ )
15
+
16
+ class Post < ActiveRecord::Base
17
+ delete_softly
18
+ has_many :comments
19
+ end
20
+
21
+ class Comment < ActiveRecord::Base
22
+ delete_softly false
23
+ belongs_to :post
24
+ has_many :tags
25
+ end
26
+
27
+ class Tag < ActiveRecord::Base
28
+ belongs_to :comment
29
+ end
30
+
31
+ unless Post.table_exists?
32
+ puts "Creating table posts"
33
+ ActiveRecord::Base.connection.create_table "posts" do |t|
34
+ t.string :title
35
+ t.text :body
36
+ t.datetime :deleted_at
37
+ t.timestamps
38
+ end
39
+ end
40
+ unless Comment.table_exists?
41
+ puts "Creating table comments"
42
+ ActiveRecord::Base.connection.create_table "comments" do |t|
43
+ t.string :email
44
+ t.text :body
45
+ t.integer :post_id
46
+ t.datetime :deleted_at
47
+ t.timestamps
48
+ end
49
+ end
50
+ unless Tag.table_exists?
51
+ puts "Creating table tags"
52
+ ActiveRecord::Base.connection.create_table "tags" do |t|
53
+ t.string :name
54
+ t.integer :comment_id
55
+ t.timestamps
56
+ end
57
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delete_softly
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Benjamin ter Kuile
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-05 00:00:00 +01:00
18
+ date: 2010-12-17 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency