delete_soft 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake'
4
+
5
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the delete_softly plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "delete_soft/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "delete_soft"
7
+ s.version = DeleteSoft::VERSION
8
+ s.authors = ["Orban Botond"]
9
+ s.email = ["orbanbotond@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{A gem which adds soft delete functionality}
12
+ s.description = %q{A gem which adds soft delete functionality}
13
+
14
+ s.rubyforge_project = "delete_soft"
15
+
16
+ s.add_dependency('squeel', '>= 0.9.3')
17
+
18
+ s.add_development_dependency('sqlite3', ['>= 1.3.4'])
19
+ s.add_development_dependency('activerecord', ['>= 3.1.0'])
20
+ s.add_development_dependency('rake', ['0.9.2'])
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,79 @@
1
+ require 'arel'
2
+
3
+ module DeleteSoftly
4
+ module ClassMethods
5
+ # Give the representation of items at a certain date/time.
6
+ # class Item < ActiveRecord::Base
7
+ # delete_softly
8
+ # end
9
+ # Will result in:
10
+ # 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')))
11
+ def at_time(date = Time.now.utc)
12
+ with_deleted.where{ (deleted_at > date || deleted_at != nil) && created_at < date }
13
+ end
14
+ alias_method :at_date, :at_time
15
+
16
+ # Give the currently active items. When delete_soflty is added this is invoked by default
17
+ # But when false is added, items are shown by default
18
+ # class Item < ActiveRecord::Base
19
+ # delete_softly false
20
+ # end
21
+ # Or similar:
22
+ # class Item < ActiveRecord::Base
23
+ # delete_softly :default => false
24
+ # end
25
+ # You need to call active on the model where you want to hide deleted items
26
+ # Item.all #=> SELECT "items".* FROM "items"
27
+ # Item.active #=> SELECT "items".* FROM "items" WHERE ("items"."deleted_at" IS NULL)
28
+ def active
29
+ where{deleted_at == nil}
30
+ end
31
+
32
+ # Same as active, but not to be overwritten. Active might become with disabled => false
33
+ # or something like that. Without deleted should remain intact
34
+ def without_deleted
35
+ where{deleted_at.nil?}
36
+ end
37
+
38
+ # Include deleted items when performing queries
39
+ # class Item < ActiveRecord::Base
40
+ # default_scope order(:content)
41
+ # delete_softly
42
+ # end
43
+ # Will result in:
44
+ # Item.first #=> SELECT "items".* FROM "items" WHERE ("items"."deleted_at" IS NULL) ORDER BY "items"."content" LIMIT 1
45
+ # Item.with_deleted.first #=> SELECT "items".* FROM "items" ORDER BY "items"."content" LIMIT 1
46
+ # Item.where(:content.matches => 'a%') #=> SELECT "items".* FROM "items" WHERE ("items"."deleted_at" IS NULL) AND ("items"."content" ILIKE 'a%') ORDER BY "items"."content"
47
+ # Item.with_deleted do
48
+ # Item.where(:content.matches => 'a%') #=> SELECT "items".* FROM "items" WHERE ("items"."content" ILIKE 'a%') ORDER BY "items"."content"
49
+ # end
50
+ # 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"
51
+ # IHaveManyItems.first.items.with_deleted #=> SELECT "items".* FROM "items" WHERE ("items".i_have_many_items_id = 1) ORDER BY "items"."content"
52
+ def with_deleted(&block)
53
+ unscoped
54
+ end
55
+
56
+ def deleted
57
+ with_deleted.where{deleted_at != nil}
58
+ end
59
+
60
+ # Support for paper_trail if it is installed as well. Then you can use:
61
+ # class Post < ActiveRecord::Base
62
+ # default_scope order(:created_at)
63
+ # delete_softly
64
+ # has_paper_trail
65
+ # has_many :comments
66
+ # end
67
+ # Then
68
+ # Post.version_at(1.week.ago)
69
+ # Will return all post of one week ago, with the appropriate field values at that time
70
+ def version_at(time)
71
+ if respond_to?(:at_time)
72
+ at_time(time).map{|i| i.respond_to?(:version_at) ? i.version_at(time) : i}.compact
73
+ else
74
+ scoped.map{|i| i.respond_to?(:version_at) ? i.version_at(time) : i}.compact
75
+ end
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,3 @@
1
+ module DeleteSoft
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,68 @@
1
+ require "delete_soft/version"
2
+
3
+ require 'active_record'
4
+ require 'squeel'
5
+ require 'class_methods'
6
+ require 'instance_methods'
7
+
8
+ module DeleteSoftly
9
+ module ARExtender
10
+
11
+ # Always have Model.active available. It is a good practice to use it
12
+ # when you want the active records. Even use it when no logic is in
13
+ # place yet. Now it is an alias for scoped, but can be overwritten
14
+ # for custom behaviour, for example:
15
+ # class Post < ActiveRecord::Base
16
+ # delete_softly
17
+ # has_many :comments
18
+ # def self.active
19
+ # super.where(:disabled.ne => true)
20
+ # end
21
+ # end
22
+ # class Comment < ActiveRecord::Base
23
+ # belongs_to :post
24
+ # end
25
+ # will result in:
26
+ # Post.all #=> SELECT * FROM posts WHERE deleted_at IS NULL;
27
+ # Post.active #=> SELECT * FROM posts WHERE deleted_at IS NULL AND disabled != 't';
28
+ # Comment.all #=> SELECT * FROM comments;
29
+ # Comment.active #=> SELECT * FROM comments;
30
+ def active
31
+ scoped
32
+ end
33
+
34
+ # Make the model delete softly. A deleted_at:datetime column is required
35
+ # for this to work.
36
+ # The two most important differences are that it can be enforce on a model
37
+ # or be more free.
38
+ # class Post < ActiveRecord::Base
39
+ # delete_softly
40
+ # end
41
+ # will enforce soft delete on the post model. A deleted post will never appear,
42
+ # unless the explicit with_deleted is called. When the model is:
43
+ # class Post < ActiveRecord::Base
44
+ # delete_softly :enforce => false
45
+ # end
46
+ # An object will still be available after destroy is called,
47
+ def delete_softly(options = {:enforce => :active})
48
+ # Make destroy! the old destroy
49
+ alias_method :destroy!, :destroy
50
+
51
+ include DeleteSoftly::InstanceMethods
52
+ extend DeleteSoftly::ClassMethods
53
+ # Support single argument
54
+ # delete_softly :active # Same as :enforce => :active, default behaviour
55
+ # delete_softly :enforce=> :with_deleted # Same as without argument
56
+ options = {:enforce=> options } unless options.is_a?(Hash)
57
+ if options[:enforce]
58
+ if options[:enforce].is_a?(Symbol) && respond_to?(options[:enforce])
59
+ default_scope send(options[:enforce])
60
+ else
61
+ default_scope active
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ ActiveRecord::Base.send(:extend, DeleteSoftly::ARExtender)
@@ -0,0 +1,50 @@
1
+ module DeleteSoftly
2
+ module InstanceMethods
3
+
4
+ # This method reports whether or not the record has been soft deleted.
5
+ #
6
+ def deleted?
7
+ self.deleted_at?
8
+ end
9
+
10
+ # Custom destroy method for models using delete_softly
11
+ def destroy
12
+ if persisted?
13
+ with_transaction_returning_status do
14
+ _run_destroy_callbacks do
15
+ update_attribute :deleted_at, Time.now
16
+ end
17
+ end
18
+ end
19
+
20
+ @destroyed = true
21
+ freeze
22
+ end
23
+
24
+ # Revive a destroyed item. For a model like:
25
+ # class Post < ActiveRecord::Base
26
+ # delete_softly
27
+ # end
28
+ # Then the following can be done:
29
+ # p = Post.find(1)
30
+ # p.destroy
31
+ # p = Post.find(1) # raise error
32
+ # p = Post.with_deleted.find(1) # Original object but with deleted_at attribute set
33
+ # p.revive #=> deleted_at => nil
34
+ # p = Post.find(1) # business as usual
35
+ # If papertrail is used for this model it will not store a copy
36
+ def revive
37
+ # Disable paper_trail when it is present and active
38
+ if self.class.respond_to?(:paper_trail_active) && self.class.paper_trail_active
39
+ self.class.paper_trail_off
40
+ update_attribute :deleted_at, nil
41
+ self.class.paper_trail_on
42
+ else
43
+ update_attribute :deleted_at, nil
44
+ end
45
+ end
46
+ alias_method :undelete, :revive
47
+ alias_method :undestroy, :revive
48
+
49
+ end
50
+ end
data/test/db.sqlite3 ADDED
Binary file
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ class DeleteSoftlyTest < ActiveSupport::TestCase
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 => "test@gmail.com", :body => "Comment 1 for post 1")
13
+ @comment2_1 = @post1.comments.create(:email => "test@gmail.com", :body => "Comment 2 for post 1")
14
+ @comment3_1 = @post1.comments.create(:email => "testother@gmail.com", :body => "Comment 2 for post 1")
15
+
16
+ @post2 = Post.create(:title => "post2")
17
+ @post2_id = @post2.id
18
+
19
+ end
20
+ test "two records available" do
21
+ assert_equal 2, Post.count
22
+ end
23
+
24
+ test "destroy count test" do
25
+ @post1.destroy
26
+ assert_equal 1, Post.count
27
+ puts "It is 1"
28
+ assert_equal 2, Post.with_deleted.count
29
+ puts "It is 2"
30
+ assert_nil Post.find_by_id(@post1_id)
31
+ @post1 = Post.with_deleted.find(@post1_id)
32
+ assert @post1.deleted_at
33
+ @post1.revive
34
+ assert_equal 2, Post.count
35
+ end
36
+
37
+ test "deleted, without_deleted methods" do
38
+ assert_equal [@post1, @post2], Post.all.sort_by{|p| p.title}
39
+ @post1.destroy
40
+ assert_equal [@post2], Post.without_deleted
41
+ assert_equal [@post1], Post.deleted
42
+ end
43
+
44
+ test "at_time methods" do
45
+ t = Time.now
46
+ @post1.destroy
47
+ assert_equal [@post2], Post.without_deleted
48
+ assert_equal [@post1, @post2], Post.at_time(t)
49
+ end
50
+
51
+ test "other default scopes" do
52
+ assert_equal 2, Comment.count
53
+ @comment2_1.destroy
54
+ assert_equal 1, Comment.count
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'arel'
4
+ require 'active_support'
5
+ require 'active_support/all'
6
+ require 'delete_soft'
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
23
+ belongs_to :post
24
+ has_many :tags
25
+ default_scope where(:email => 'test@gmail.com')
26
+ end
27
+
28
+ class Tag < ActiveRecord::Base
29
+ belongs_to :comment
30
+ end
31
+
32
+ if Post.table_exists?
33
+ ActiveRecord::Base.connection.drop_table "posts"
34
+ end
35
+
36
+ puts "Creating table posts"
37
+ ActiveRecord::Base.connection.create_table "posts" do |t|
38
+ t.string :title
39
+ t.text :body
40
+ t.datetime :deleted_at
41
+ t.timestamps
42
+ end
43
+
44
+ if Comment.table_exists?
45
+ ActiveRecord::Base.connection.drop_table "comments"
46
+ end
47
+
48
+ puts "Creating table comments"
49
+ ActiveRecord::Base.connection.create_table "comments" do |t|
50
+ t.string :email
51
+ t.text :body
52
+ t.integer :post_id
53
+ t.datetime :deleted_at
54
+ t.timestamps
55
+ end
56
+
57
+ if Tag.table_exists?
58
+ ActiveRecord::Base.connection.drop_table "tags"
59
+ end
60
+
61
+ puts "Creating table tags"
62
+ ActiveRecord::Base.connection.create_table "tags" do |t|
63
+ t.string :name
64
+ t.integer :comment_id
65
+ t.timestamps
66
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delete_soft
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Orban Botond
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-03 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 61
29
+ segments:
30
+ - 0
31
+ - 9
32
+ - 3
33
+ version: 0.9.3
34
+ type: :runtime
35
+ name: squeel
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 19
45
+ segments:
46
+ - 1
47
+ - 3
48
+ - 4
49
+ version: 1.3.4
50
+ type: :development
51
+ name: sqlite3
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 3
63
+ - 1
64
+ - 0
65
+ version: 3.1.0
66
+ type: :development
67
+ name: activerecord
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - "="
75
+ - !ruby/object:Gem::Version
76
+ hash: 63
77
+ segments:
78
+ - 0
79
+ - 9
80
+ - 2
81
+ version: 0.9.2
82
+ type: :development
83
+ name: rake
84
+ version_requirements: *id004
85
+ description: A gem which adds soft delete functionality
86
+ email:
87
+ - orbanbotond@gmail.com
88
+ executables: []
89
+
90
+ extensions: []
91
+
92
+ extra_rdoc_files: []
93
+
94
+ files:
95
+ - .gitignore
96
+ - Gemfile
97
+ - Rakefile
98
+ - delete_soft.gemspec
99
+ - lib/class_methods.rb
100
+ - lib/delete_soft.rb
101
+ - lib/delete_soft/version.rb
102
+ - lib/instance_methods.rb
103
+ - test/db.sqlite3
104
+ - test/delete_softly_test.rb
105
+ - test/test_helper.rb
106
+ has_rdoc: true
107
+ homepage: ""
108
+ licenses: []
109
+
110
+ post_install_message:
111
+ rdoc_options: []
112
+
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ hash: 3
121
+ segments:
122
+ - 0
123
+ version: "0"
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ requirements: []
134
+
135
+ rubyforge_project: delete_soft
136
+ rubygems_version: 1.6.2
137
+ signing_key:
138
+ specification_version: 3
139
+ summary: A gem which adds soft delete functionality
140
+ test_files:
141
+ - test/db.sqlite3
142
+ - test/delete_softly_test.rb
143
+ - test/test_helper.rb