active_reaper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rvmrc +3 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +61 -0
- data/README.md +68 -0
- data/Rakefile +1 -0
- data/active_reaper.gemspec +28 -0
- data/lib/active_reaper/active_reaper.rb +113 -0
- data/lib/active_reaper/railtie.rb +9 -0
- data/lib/active_reaper/version.rb +3 -0
- data/lib/active_reaper.rb +3 -0
- data/lib/tasks/active_reaper.rake +0 -0
- data/spec/active_reaper_spec.rb +201 -0
- data/spec/spec_helper.rb +8 -0
- metadata +131 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
active_reaper (0.0.1)
|
5
|
+
activerecord
|
6
|
+
activesupport
|
7
|
+
rake
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activemodel (3.1.1)
|
13
|
+
activesupport (= 3.1.1)
|
14
|
+
builder (~> 3.0.0)
|
15
|
+
i18n (~> 0.6)
|
16
|
+
activerecord (3.1.1)
|
17
|
+
activemodel (= 3.1.1)
|
18
|
+
activesupport (= 3.1.1)
|
19
|
+
arel (~> 2.2.1)
|
20
|
+
tzinfo (~> 0.3.29)
|
21
|
+
activesupport (3.1.1)
|
22
|
+
multi_json (~> 1.0)
|
23
|
+
archive-tar-minitar (0.5.2)
|
24
|
+
arel (2.2.1)
|
25
|
+
builder (3.0.0)
|
26
|
+
columnize (0.3.4)
|
27
|
+
diff-lcs (1.1.3)
|
28
|
+
i18n (0.6.0)
|
29
|
+
linecache19 (0.5.12)
|
30
|
+
ruby_core_source (>= 0.1.4)
|
31
|
+
multi_json (1.0.3)
|
32
|
+
rake (0.9.2.2)
|
33
|
+
rspec (2.7.0)
|
34
|
+
rspec-core (~> 2.7.0)
|
35
|
+
rspec-expectations (~> 2.7.0)
|
36
|
+
rspec-mocks (~> 2.7.0)
|
37
|
+
rspec-core (2.7.1)
|
38
|
+
rspec-expectations (2.7.0)
|
39
|
+
diff-lcs (~> 1.1.2)
|
40
|
+
rspec-mocks (2.7.0)
|
41
|
+
ruby-debug-base19 (0.11.25)
|
42
|
+
columnize (>= 0.3.1)
|
43
|
+
linecache19 (>= 0.5.11)
|
44
|
+
ruby_core_source (>= 0.1.4)
|
45
|
+
ruby-debug19 (0.11.6)
|
46
|
+
columnize (>= 0.3.1)
|
47
|
+
linecache19 (>= 0.5.11)
|
48
|
+
ruby-debug-base19 (>= 0.11.19)
|
49
|
+
ruby_core_source (0.1.5)
|
50
|
+
archive-tar-minitar (>= 0.5.2)
|
51
|
+
sqlite3 (1.3.4)
|
52
|
+
tzinfo (0.3.31)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
active_reaper!
|
59
|
+
rspec (>= 2.6)
|
60
|
+
ruby-debug19
|
61
|
+
sqlite3
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# README #
|
2
|
+
|
3
|
+
## Description ##
|
4
|
+
|
5
|
+
Mark your ActiveRecord models for deletion after a certain period in a single line. Keep these kinds of specifications out of other files or rake tasks and in the models where they belong. A rake task iterates over all participating classes and finds objects/rows ready for deletion. Useful on Heroku where the rake task can be called from their free daily cron task. Ideal for objects common to social networking applications that can quickly build up over time (notifications, news feeds, private messages, etc).
|
6
|
+
|
7
|
+
## How To Use ##
|
8
|
+
|
9
|
+
Within an ActiveRecord class make a call to the "reap" method with an argument to say when a row should be deleted. ActiveReaper will assume that you intend to base the time calculation on the datetime column 'created_at':
|
10
|
+
|
11
|
+
class PrivateMessage < ActiveRecord::Base
|
12
|
+
# delete private messages 30 days after they've been created
|
13
|
+
reap :after => 30.days
|
14
|
+
end
|
15
|
+
|
16
|
+
If you need the time calculation to be based off of another value, in can be the name of a column or an instance method:
|
17
|
+
|
18
|
+
class PrivateMessage < ActiveRecord::Base
|
19
|
+
# delete private messages 30 days after they've been read
|
20
|
+
reap :after => 30.days, :determined_by => :read_at
|
21
|
+
end
|
22
|
+
|
23
|
+
# == Schema Information
|
24
|
+
#
|
25
|
+
# Table name: private_messages
|
26
|
+
#
|
27
|
+
# id :integer(4) not null, primary key
|
28
|
+
# sweet_nothings :text
|
29
|
+
# read_at :datetime
|
30
|
+
# created_at :datetime
|
31
|
+
# updated_at :datetime
|
32
|
+
|
33
|
+
The default method used by ActiveReaper is to delete the expired rows using `ActiveRecord::Base.delete()`. If there are important callbacks or child models in a `has_many :dependent => :destroy` association that need to be taken care of, tell ActiveReaper to use `ActiveRecord::Base.destroy()` instead:
|
34
|
+
|
35
|
+
class Post < ActiveRecord::Base
|
36
|
+
has_many :comments, :dependent => :destroy
|
37
|
+
|
38
|
+
# delete posts after 3 months, and take all comments with them
|
39
|
+
reap :after => 3.months, :using => :destroy
|
40
|
+
end
|
41
|
+
|
42
|
+
If you want to place a guard on top of the expiration time. The guard can be the name of a column or an instance method (_NOTE: Strings in mysql have a truth value of false_):
|
43
|
+
|
44
|
+
class Post < ActiveRecord::Base
|
45
|
+
# delete posts after 1 week if it's been flagged
|
46
|
+
reap :after => 1.week, :if => :flagged
|
47
|
+
end
|
48
|
+
|
49
|
+
`unless` may also be used:
|
50
|
+
|
51
|
+
class PrivateMessage < ActiveRecord::Base
|
52
|
+
# delete private messages 30 days after they've been created, unless the user wants them saved
|
53
|
+
reap :after => 30.days, :unless => :saved
|
54
|
+
end
|
55
|
+
|
56
|
+
## Running The Reap Task ##
|
57
|
+
|
58
|
+
Straightforward:
|
59
|
+
|
60
|
+
rake reap
|
61
|
+
|
62
|
+
## Don't Fear The Reaper ##
|
63
|
+
|
64
|
+
Since the actual deleting of models gets performed in a rake task, performance may not _necessarily_ be an issue, but ActiveReaper is the fastest when using the delete method, and when both the arguments for 'determined\_by' and 'if/unless' are table columns. Using the destroy method is slower for obvious reasons, and if 'determined\_by' is given as an instance method, then every row has to be instantiated as an ActiveRecord object and evaluated to look for expired objects, making it the slowest strategy.
|
65
|
+
|
66
|
+
## Author ##
|
67
|
+
|
68
|
+
Christopher Eberz; chris@chriseberz.com; @zortnac
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "active_reaper/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "active_reaper"
|
7
|
+
s.version = ActiveReaper::VERSION
|
8
|
+
s.authors = ["Chris Eberz"]
|
9
|
+
s.email = ["chris@chriseberz.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Your ActiveRecord models can cleanly mark themsleves for deletion after a specifc time.}
|
12
|
+
s.description = %q{Set ActiveRecord models to delete themselves after a fixed time, or when a certain criteria is met.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "active_reaper"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib", "spec"]
|
20
|
+
|
21
|
+
s.add_development_dependency "rspec", ">= 2.6"
|
22
|
+
s.add_development_dependency "ruby-debug19"
|
23
|
+
s.add_development_dependency "sqlite3"
|
24
|
+
|
25
|
+
s.add_runtime_dependency "rake"
|
26
|
+
s.add_runtime_dependency "activerecord"
|
27
|
+
s.add_runtime_dependency "activesupport"
|
28
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_record"
|
3
|
+
|
4
|
+
module ActiveReaper
|
5
|
+
REAPED_CLASSES = {}
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.reap!
|
12
|
+
REAPED_CLASSES.each_pair do |klass, settings|
|
13
|
+
if settings[:determined_by_type] == :column && settings[:condition].nil?
|
14
|
+
ActiveReaper.reap_simply(klass, settings)
|
15
|
+
elsif settings[:determined_by_type] == :column && !settings[:condition].nil? && settings[:condition_type] == :column
|
16
|
+
ActiveReaper.reap_quickly(klass, settings)
|
17
|
+
elsif settings[:determined_by_type] == :column && !settings[:condition].nil? && settings[:condition_type] == :method
|
18
|
+
ActiveReaper.reap_conditionally(klass, settings)
|
19
|
+
elsif settings[:determined_by_type] == :method
|
20
|
+
ActiveReaper.reap_meticulously(klass, settings)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def reap(opts)
|
27
|
+
unless opts[:after].respond_to?(:ago)
|
28
|
+
raise ArgumentError.new("ActiveReaper: option for 'after' must be a FixNum (amount in seconds)")
|
29
|
+
end
|
30
|
+
|
31
|
+
unless [:delete, :destroy].include? (opts[:using] || :delete)
|
32
|
+
raise ArgumentError.new("ActiveReaper: option for 'using' must be either :delete or :destroy")
|
33
|
+
end
|
34
|
+
|
35
|
+
unless self.column_names.include?(opts[:determined_by].to_s) ||
|
36
|
+
self.instance_methods.include?(opts[:determined_by]) ||
|
37
|
+
(opts[:determined_by].nil? && self.column_names.include?('created_at'))
|
38
|
+
raise ArgumentError.new("ActiveReaper: option for 'determined_by' must be either a table column name or an instance method. If option is left blank, table must have a datetime 'created_at' column.")
|
39
|
+
end
|
40
|
+
|
41
|
+
unless (opts[:if].nil? && opts[:unless].nil?) || ( !!opts[:if] ^ !!opts[:unless])
|
42
|
+
raise ArgumentError.new("ActiveReaper: Pass only 'if' or 'unless' as a condition")
|
43
|
+
end
|
44
|
+
|
45
|
+
unless self.column_names.include?(opts[:if].to_s) || (opts[:if].to_s.last=='?' && self.column_names.include?(opts[:if].to_s.chop)) || self.instance_methods.include?(opts[:if]) || opts[:if].nil?
|
46
|
+
raise ArgumentError.new("ActiveReaper: option for 'if' or 'unless' must be either a table column name or an instance method.")
|
47
|
+
end
|
48
|
+
|
49
|
+
settings = {
|
50
|
+
:after => opts[:after],
|
51
|
+
:using => opts[:using] || :delete,
|
52
|
+
:determined_by => opts[:determined_by].nil? ? :created_at : opts[:determined_by],
|
53
|
+
:determined_by_type => self.column_names.include?(opts[:determined_by].to_s) ? :column : :method,
|
54
|
+
:condition => opts[:if].nil? ? opts[:unless] : opts[:if],
|
55
|
+
:condition_type => nil,
|
56
|
+
:truth_value => nil
|
57
|
+
}
|
58
|
+
|
59
|
+
if settings[:condition]
|
60
|
+
settings[:truth_value] = opts[:if].nil? ? false : true
|
61
|
+
settings[:condition_type] = self.column_names.include?(settings[:condition].to_s) ? :column : :method
|
62
|
+
end
|
63
|
+
|
64
|
+
REAPED_CLASSES[self] = settings
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# run a simple delete_all or destroy_all; no conditions or method-based expiration
|
69
|
+
def self.reap_simply(klass, settings)
|
70
|
+
if settings[:using] == :delete
|
71
|
+
klass.where("#{settings[:determined_by]} < ?", settings[:after].ago.to_s(:db)).delete_all
|
72
|
+
else
|
73
|
+
klass.where("#{settings[:determined_by]} < ?", settings[:after].ago.to_s(:db)).destroy_all
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# run a delete_all or destroy_all with the value of the column given as the condition made part of the query
|
78
|
+
def self.reap_quickly(klass, settings)
|
79
|
+
if settings[:using] == :delete
|
80
|
+
klass.where("#{settings[:determined_by]} < ?", settings[:after].ago.to_s(:db)).where(settings[:condition] => settings[:truth_value]).delete_all
|
81
|
+
else
|
82
|
+
klass.where("#{settings[:determined_by]} < ?", settings[:after].ago.to_s(:db)).where(settings[:condition] => settings[:truth_value]).destroy_all
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# objects can be selected quickly, but have to be individually evaluated for the extra condition
|
87
|
+
def self.reap_conditionally(klass, settings)
|
88
|
+
klass.where("#{settings[:determined_by]} < ?", settings[:after].ago.to_s(:db)).each do |object|
|
89
|
+
if object.send(settings[:condition]) == settings[:truth_value]
|
90
|
+
if settings[:using] == :delete
|
91
|
+
klass.delete(object.id)
|
92
|
+
else
|
93
|
+
klass.destroy(object.id)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# All objects must be iterated over to see which have expired, and then may or may not require an extra condition
|
100
|
+
def self.reap_meticulously(klass, settings)
|
101
|
+
klass.all.each do |object|
|
102
|
+
if (object.send(settings[:determined_by]) < settings[:after].ago) && (settings[:condition].nil? || object.send(settings[:condition]) == settings[:truth_value])
|
103
|
+
if settings[:using] == :delete
|
104
|
+
klass.delete(object.id)
|
105
|
+
else
|
106
|
+
klass.destroy(object.id)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
::ActiveRecord::Base.send(:include, ActiveReaper)
|
File without changes
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'posts'")
|
4
|
+
ActiveRecord::Base.connection.create_table(:posts) do |t|
|
5
|
+
t.string :title
|
6
|
+
t.string :content
|
7
|
+
t.boolean :flagged
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'comments'")
|
11
|
+
ActiveRecord::Base.connection.create_table(:comments) do |t|
|
12
|
+
t.belongs_to :post
|
13
|
+
t.string :content
|
14
|
+
end
|
15
|
+
|
16
|
+
class Post < ActiveRecord::Base
|
17
|
+
has_many :comments, :dependent => :destroy
|
18
|
+
end
|
19
|
+
|
20
|
+
class Comment < ActiveRecord::Base
|
21
|
+
belongs_to :post
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ActiveReaper do
|
25
|
+
before do
|
26
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
27
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
28
|
+
|
29
|
+
@post_to_be_deleted = Post.create
|
30
|
+
@post_that_survives = Post.create
|
31
|
+
@comment = Comment.create(:post_id => @post_to_be_deleted.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "classes set to be reaped after an expiration date" do
|
35
|
+
before do
|
36
|
+
Post.class_eval do
|
37
|
+
reap :after => 10.days
|
38
|
+
end
|
39
|
+
|
40
|
+
@post_that_survives.update_attribute :created_at, 9.days.ago
|
41
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should correctly delete only the instances that have expired" do
|
45
|
+
ActiveReaper.reap!
|
46
|
+
Post.exists?(@post_that_survives.id).should be_true
|
47
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "classes set to be reaped after an expiration date, based on a field" do
|
52
|
+
before do
|
53
|
+
Post.class_eval do
|
54
|
+
reap :after => 10.days, :determined_by => :created_at
|
55
|
+
end
|
56
|
+
|
57
|
+
@post_that_survives.update_attribute :created_at, 9.days.ago
|
58
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should correctly delete only the instances that have expired" do
|
62
|
+
ActiveReaper.reap!
|
63
|
+
Post.exists?(@post_that_survives.id).should be_true
|
64
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "classes set to be reaped after an expiration date, based on a value returned by a method" do
|
69
|
+
before do
|
70
|
+
Post.class_eval do
|
71
|
+
def last_looked_at
|
72
|
+
created_at
|
73
|
+
end
|
74
|
+
|
75
|
+
reap :after => 10.days, :determined_by => :last_looked_at
|
76
|
+
end
|
77
|
+
|
78
|
+
@post_that_survives.update_attribute :created_at, 9.days.ago
|
79
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should correctly delete only the instances that have expired" do
|
83
|
+
ActiveReaper.reap!
|
84
|
+
Post.exists?(@post_that_survives.id).should be_true
|
85
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "classes set to be reaped using the default delete method" do
|
90
|
+
before do
|
91
|
+
Post.class_eval do
|
92
|
+
reap :after => 10.days, :determined_by => :created_at
|
93
|
+
end
|
94
|
+
|
95
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should leave dependent models behind" do
|
99
|
+
ActiveReaper.reap!
|
100
|
+
Comment.exists?(@comment.id).should be_true
|
101
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "classes set to be reaped using the optional destroy method" do
|
106
|
+
before do
|
107
|
+
Post.class_eval do
|
108
|
+
reap :after => 10.days, :determined_by => :created_at, :using => :destroy
|
109
|
+
end
|
110
|
+
|
111
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should also delete dependent models" do
|
115
|
+
ActiveReaper.reap!
|
116
|
+
Comment.exists?(@comment.id).should_not be_true
|
117
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "classes set to be reaped after an expiration date, with a condition based on a field" do
|
122
|
+
before do
|
123
|
+
Post.class_eval do
|
124
|
+
reap :after => 10.days, :determined_by => :created_at, :if => :flagged
|
125
|
+
end
|
126
|
+
|
127
|
+
@post_that_survives.update_attribute :created_at, 11.days.ago
|
128
|
+
@post_that_survives.update_attribute :flagged, false
|
129
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
130
|
+
@post_to_be_deleted.update_attribute :flagged, true
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should correctly delete only the instances that have expired" do
|
134
|
+
ActiveReaper.reap!
|
135
|
+
Post.exists?(@post_that_survives.id).should be_true
|
136
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "classes set to be reaped after an expiration date, with a condition based on a method" do
|
141
|
+
before do
|
142
|
+
Post.class_eval do
|
143
|
+
reap :after => 10.days, :determined_by => :created_at, :if => :flagged?
|
144
|
+
end
|
145
|
+
|
146
|
+
@post_that_survives.update_attribute :created_at, 11.days.ago
|
147
|
+
@post_that_survives.update_attribute :flagged, false
|
148
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
149
|
+
@post_to_be_deleted.update_attribute :flagged, true
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should correctly delete only the instances that have expired" do
|
153
|
+
ActiveReaper.reap!
|
154
|
+
Post.exists?(@post_that_survives.id).should be_true
|
155
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "classes set to be reaped after an expiration date, with a negative condition based on a field" do
|
160
|
+
before do
|
161
|
+
Post.class_eval do
|
162
|
+
reap :after => 10.days, :determined_by => :created_at, :unless => :flagged
|
163
|
+
end
|
164
|
+
|
165
|
+
@post_that_survives.update_attribute :created_at, 11.days.ago
|
166
|
+
@post_that_survives.update_attribute :flagged, true
|
167
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
168
|
+
@post_to_be_deleted.update_attribute :flagged, false
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should correctly delete only the instances that have expired" do
|
172
|
+
ActiveReaper.reap!
|
173
|
+
Post.exists?(@post_that_survives.id).should be_true
|
174
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "classes set to be reaped after an expiration date, with a negative condition based on a method" do
|
179
|
+
before do
|
180
|
+
Post.class_eval do
|
181
|
+
reap :after => 10.days, :determined_by => :created_at, :unless => :flagged?
|
182
|
+
end
|
183
|
+
|
184
|
+
@post_that_survives.update_attribute :created_at, 11.days.ago
|
185
|
+
@post_that_survives.update_attribute :flagged, true
|
186
|
+
@post_to_be_deleted.update_attribute :created_at, 11.days.ago
|
187
|
+
@post_to_be_deleted.update_attribute :flagged, false
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should correctly delete only the instances that have expired" do
|
191
|
+
ActiveReaper.reap!
|
192
|
+
Post.exists?(@post_that_survives.id).should be_true
|
193
|
+
Post.exists?(@post_to_be_deleted.id).should_not be_true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
after do
|
198
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
199
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
200
|
+
end
|
201
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_reaper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris Eberz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-15 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &2157213160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.6'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2157213160
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ruby-debug19
|
27
|
+
requirement: &2157212740 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2157212740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sqlite3
|
38
|
+
requirement: &2157212280 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2157212280
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &2157211820 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2157211820
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: activerecord
|
60
|
+
requirement: &2157184760 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *2157184760
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: &2157184340 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *2157184340
|
80
|
+
description: Set ActiveRecord models to delete themselves after a fixed time, or when
|
81
|
+
a certain criteria is met.
|
82
|
+
email:
|
83
|
+
- chris@chriseberz.com
|
84
|
+
executables: []
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- .gitignore
|
89
|
+
- .rvmrc
|
90
|
+
- CHANGELOG.md
|
91
|
+
- Gemfile
|
92
|
+
- Gemfile.lock
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- active_reaper.gemspec
|
96
|
+
- lib/active_reaper.rb
|
97
|
+
- lib/active_reaper/active_reaper.rb
|
98
|
+
- lib/active_reaper/railtie.rb
|
99
|
+
- lib/active_reaper/version.rb
|
100
|
+
- lib/tasks/active_reaper.rake
|
101
|
+
- spec/active_reaper_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
homepage: ''
|
104
|
+
licenses: []
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
- spec
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project: active_reaper
|
124
|
+
rubygems_version: 1.8.10
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Your ActiveRecord models can cleanly mark themsleves for deletion after a
|
128
|
+
specifc time.
|
129
|
+
test_files:
|
130
|
+
- spec/active_reaper_spec.rb
|
131
|
+
- spec/spec_helper.rb
|