delete_softly 0.0.2 → 0.0.3

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 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