winton-acts_as_archive 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +4 -0
- data/MIT-LICENSE +18 -0
- data/README.markdown +23 -0
- data/Rakefile +41 -0
- data/acts_as_archive.gemspec +40 -0
- data/init.rb +1 -0
- data/lib/acts_as_archive/base/destroy.rb +67 -0
- data/lib/acts_as_archive/base/restore.rb +38 -0
- data/lib/acts_as_archive/base/table.rb +36 -0
- data/lib/acts_as_archive/base.rb +25 -0
- data/lib/acts_as_archive/migration.rb +43 -0
- data/lib/acts_as_archive.rb +2 -0
- data/rails/init.rb +4 -0
- data/spec/acts_as_archive/base/destroy_spec.rb +109 -0
- data/spec/acts_as_archive/base/restore_spec.rb +66 -0
- data/spec/acts_as_archive/base/table_spec.rb +32 -0
- data/spec/acts_as_archive/migration_spec.rb +36 -0
- data/spec/db/config/database.yml.example +6 -0
- data/spec/db/log/.gitignore +0 -0
- data/spec/db/migrate/001_add_to_articles.rb +9 -0
- data/spec/db/models/article.rb +3 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +56 -0
- metadata +75 -0
data/CHANGELOG.markdown
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2009 Winton Welsh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
GemTemplate
|
2
|
+
===========
|
3
|
+
|
4
|
+
A gem template for new projects.
|
5
|
+
|
6
|
+
Setup
|
7
|
+
-----
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
git clone git://github.com/winton/acts_as_archive.git my_project
|
11
|
+
cd my_project
|
12
|
+
rm -Rf .git
|
13
|
+
git init
|
14
|
+
git remote add origin git@github.com:winton/my_project.git
|
15
|
+
</pre>
|
16
|
+
|
17
|
+
Rename these files/folders:
|
18
|
+
|
19
|
+
<pre>
|
20
|
+
ls **/acts_as_archive*
|
21
|
+
</pre>
|
22
|
+
|
23
|
+
Do a project-wide find/replace on <code>acts_as_archive</code>.
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
GEM_NAME = 'acts_as_archive'
|
6
|
+
task :default => "#{GEM_NAME}.gemspec"
|
7
|
+
|
8
|
+
file "#{GEM_NAME}.gemspec" => FileList[ '{lib,spec}/**', 'Rakefile' ] do |f|
|
9
|
+
# Read spec file and split out manifest section
|
10
|
+
spec = File.read(f.name)
|
11
|
+
parts = spec.split(" # = MANIFEST =\n")
|
12
|
+
fail 'bad spec' if parts.length != 3
|
13
|
+
# Determine file list from git ls-files
|
14
|
+
files = `git ls-files`.
|
15
|
+
split("\n").
|
16
|
+
sort.
|
17
|
+
reject{ |file| file =~ /^\./ }.
|
18
|
+
reject { |file| file =~ /^doc/ }.
|
19
|
+
map{ |file| " #{file}" }.
|
20
|
+
join("\n")
|
21
|
+
# Piece file back together and write
|
22
|
+
parts[1] = " s.files = %w[\n#{files}\n ]\n"
|
23
|
+
spec = parts.join(" # = MANIFEST =\n")
|
24
|
+
File.open(f.name, 'w') { |io| io.write(spec) }
|
25
|
+
puts "Updated #{f.name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Install the gem
|
29
|
+
task :install do
|
30
|
+
`sudo gem uninstall #{GEM_NAME} -x`
|
31
|
+
`gem build #{GEM_NAME}.gemspec`
|
32
|
+
`sudo gem install #{GEM_NAME}*.gem`
|
33
|
+
`rm #{GEM_NAME}*.gem`
|
34
|
+
end
|
35
|
+
|
36
|
+
# rake spec
|
37
|
+
desc "Run specs"
|
38
|
+
Spec::Rake::SpecTask.new do |t|
|
39
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
40
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'acts_as_archive'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.date = '2009-04-22'
|
5
|
+
|
6
|
+
s.summary = "Moves your deleted records to a different table"
|
7
|
+
s.description = "Moves your deleted records to a different table"
|
8
|
+
|
9
|
+
s.author = 'Winton Welsh'
|
10
|
+
s.email = 'mail@wintoni.us'
|
11
|
+
s.homepage = 'http://github.com/winton/'
|
12
|
+
|
13
|
+
# = MANIFEST =
|
14
|
+
s.files = %w[
|
15
|
+
CHANGELOG.markdown
|
16
|
+
MIT-LICENSE
|
17
|
+
README.markdown
|
18
|
+
Rakefile
|
19
|
+
acts_as_archive.gemspec
|
20
|
+
init.rb
|
21
|
+
lib/acts_as_archive.rb
|
22
|
+
lib/acts_as_archive/base.rb
|
23
|
+
lib/acts_as_archive/base/destroy.rb
|
24
|
+
lib/acts_as_archive/base/restore.rb
|
25
|
+
lib/acts_as_archive/base/table.rb
|
26
|
+
lib/acts_as_archive/migration.rb
|
27
|
+
rails/init.rb
|
28
|
+
spec/acts_as_archive/base/destroy_spec.rb
|
29
|
+
spec/acts_as_archive/base/restore_spec.rb
|
30
|
+
spec/acts_as_archive/base/table_spec.rb
|
31
|
+
spec/acts_as_archive/migration_spec.rb
|
32
|
+
spec/db/config/database.yml.example
|
33
|
+
spec/db/log/.gitignore
|
34
|
+
spec/db/migrate/001_add_to_articles.rb
|
35
|
+
spec/db/models/article.rb
|
36
|
+
spec/spec.opts
|
37
|
+
spec/spec_helper.rb
|
38
|
+
]
|
39
|
+
# = MANIFEST =
|
40
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module ActsAsArchive
|
2
|
+
module Base
|
3
|
+
module Destroy
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
unless base.included_modules.include?(InstanceMethods)
|
7
|
+
base.class_eval do
|
8
|
+
alias_method :destroy_without_callbacks!, :destroy_without_callbacks
|
9
|
+
class <<self
|
10
|
+
alias_method :delete_all!, :delete_all
|
11
|
+
end
|
12
|
+
end
|
13
|
+
base.send :extend, ClassMethods
|
14
|
+
base.send :include, InstanceMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def copy_to_archive(conditions)
|
20
|
+
add_conditions!(where = '', conditions)
|
21
|
+
col_names = column_names - [ 'deleted_at' ]
|
22
|
+
connection.execute(%{
|
23
|
+
INSERT INTO archived_#{table_name} (#{col_names.join(', ')}, deleted_at)
|
24
|
+
SELECT #{col_names.join(', ')}, '#{Time.now.to_s(:db)}'
|
25
|
+
FROM #{table_name}
|
26
|
+
#{where}
|
27
|
+
})
|
28
|
+
if where.empty?
|
29
|
+
connection.execute("TRUNCATE TABLE #{table_name}")
|
30
|
+
else
|
31
|
+
connection.execute("DELETE FROM #{table_name} #{where}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_all(conditions=nil)
|
36
|
+
copy_to_archive(conditions)
|
37
|
+
end
|
38
|
+
|
39
|
+
def migrate_from_acts_as_paranoid
|
40
|
+
if column_names.include?('deleted_at')
|
41
|
+
copy_to_archive('deleted_at IS NOT NULL')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
def destroy_without_callbacks
|
48
|
+
unless new_record?
|
49
|
+
self.class.copy_to_archive("#{self.class.primary_key} = #{id}")
|
50
|
+
end
|
51
|
+
freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
def destroy!
|
55
|
+
transaction { destroy_with_callbacks! }
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy_with_callbacks!
|
59
|
+
return false if callback(:before_destroy) == false
|
60
|
+
result = destroy_without_callbacks!
|
61
|
+
callback(:after_destroy)
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActsAsArchive
|
2
|
+
module Base
|
3
|
+
module Restore
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
unless base.included_modules.include?(InstanceMethods)
|
7
|
+
base.send :extend, ClassMethods
|
8
|
+
base.send :include, InstanceMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def copy_from_archive(conditions)
|
15
|
+
add_conditions!(where = '', conditions)
|
16
|
+
connection.execute(%{
|
17
|
+
INSERT INTO #{table_name} (#{column_names.join(', ')})
|
18
|
+
SELECT #{column_names.join(', ')}
|
19
|
+
FROM archived_#{table_name}
|
20
|
+
#{where}
|
21
|
+
})
|
22
|
+
if where.empty?
|
23
|
+
connection.execute("TRUNCATE TABLE archived_#{table_name}")
|
24
|
+
else
|
25
|
+
connection.execute("DELETE FROM archived_#{table_name} #{where}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def restore_all(conditions=nil)
|
30
|
+
copy_from_archive(conditions)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActsAsArchive
|
2
|
+
module Base
|
3
|
+
module Table
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
unless base.included_modules.include?(InstanceMethods)
|
7
|
+
base.send :extend, ClassMethods
|
8
|
+
base.send :include, InstanceMethods
|
9
|
+
# Create archive table when Rails starts
|
10
|
+
base.create_archive_table unless $TESTING
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def create_archive_table
|
17
|
+
if connection.table_exists?(table_name) && !connection.table_exists?("archived_#{table_name}")
|
18
|
+
connection.execute(%{
|
19
|
+
CREATE TABLE archived_#{table_name}
|
20
|
+
ENGINE=InnoDB
|
21
|
+
AS SELECT * from #{table_name}
|
22
|
+
WHERE false;
|
23
|
+
})
|
24
|
+
end
|
25
|
+
columns = connection.columns("archived_#{table_name}").collect(&:name)
|
26
|
+
unless columns.include?('deleted_at')
|
27
|
+
connection.add_column("archived_#{table_name}", 'deleted_at', :datetime)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/base/destroy"
|
2
|
+
require File.dirname(__FILE__) + "/base/restore"
|
3
|
+
require File.dirname(__FILE__) + "/base/table"
|
4
|
+
|
5
|
+
module ActsAsArchive
|
6
|
+
module Base
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ActMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActMethods
|
12
|
+
def acts_as_archive
|
13
|
+
include Destroy
|
14
|
+
include Restore
|
15
|
+
include Table
|
16
|
+
class_eval <<-end_eval
|
17
|
+
class Archive < ActiveRecord::Base
|
18
|
+
self.record_timestamps = false
|
19
|
+
self.table_name = "archived_#{self.table_name}"
|
20
|
+
end
|
21
|
+
end_eval
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ActsAsArchive
|
2
|
+
module Migration
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
unless base.included_modules.include?(InstanceMethods)
|
6
|
+
base.send :extend, ClassMethods
|
7
|
+
base.class_eval do
|
8
|
+
class <<self
|
9
|
+
alias_method :method_missing_without_archive, :method_missing
|
10
|
+
alias_method :method_missing, :method_missing_with_archive
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def method_missing_with_archive(method, *arguments, &block)
|
19
|
+
args = Marshal.load(Marshal.dump(arguments))
|
20
|
+
method_missing_without_archive(method, *arguments, &block)
|
21
|
+
supported = [
|
22
|
+
:add_column, :add_timestamps, :change_column,
|
23
|
+
:change_column_default, :change_table,
|
24
|
+
:drop_table, :remove_column, :remove_columns,
|
25
|
+
:remove_timestamps, :rename_column, :rename_table
|
26
|
+
]
|
27
|
+
if !args.empty? && supported.include?(method)
|
28
|
+
connection = ActiveRecord::Base.connection
|
29
|
+
args[0] = "archived_" + ActiveRecord::Migrator.proper_table_name(args[0])
|
30
|
+
if method == :rename_table
|
31
|
+
args[1] = "archived_" + args[1]
|
32
|
+
end
|
33
|
+
if connection.table_exists?(args[0])
|
34
|
+
connection.send(method, *args, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module InstanceMethods
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
|
3
|
+
describe ActsAsArchive::Base::Destroy do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
establish_test_db
|
7
|
+
Article.create_archive_table
|
8
|
+
@connection = ActiveRecord::Base.connection
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'delete_all!' do
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@articles = []
|
15
|
+
@connection.execute("TRUNCATE TABLE #{Article.table_name}")
|
16
|
+
5.times do |x|
|
17
|
+
@articles << Article.create(:title => "Title #{x}", :body => "Body #{x}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should really delete all records" do
|
22
|
+
Article.delete_all!
|
23
|
+
Article.count.should == 0
|
24
|
+
Article::Archive.count.should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'delete_all' do
|
30
|
+
|
31
|
+
before(:each) do
|
32
|
+
@articles = []
|
33
|
+
5.times do |x|
|
34
|
+
@articles << Article.create(:title => "Title #{x}", :body => "Body #{x}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'with conditions' do
|
39
|
+
|
40
|
+
before(:each) do
|
41
|
+
# Mini delete_all parameter test
|
42
|
+
Article.delete_all [ 'id = ?', @articles[0].id ]
|
43
|
+
Article.delete_all "id = #{@articles[1].id}"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should move some records to the archive table" do
|
47
|
+
Article.count.should == 3
|
48
|
+
Article::Archive.count.should == 2
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should preserve record attributes" do
|
52
|
+
2.times do |x|
|
53
|
+
original = @articles[x]
|
54
|
+
copy = Article::Archive.find(original.id)
|
55
|
+
article_match?(original, copy)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'without conditions' do
|
61
|
+
|
62
|
+
before(:each) do
|
63
|
+
Article.delete_all
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should move all records to the archive table" do
|
67
|
+
Article.count.should == 0
|
68
|
+
Article::Archive.count.should == 5
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should preserve record attributes" do
|
72
|
+
5.times do |x|
|
73
|
+
original = @articles[x]
|
74
|
+
copy = Article::Archive.find(original.id)
|
75
|
+
article_match?(original, copy)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
[ :destroy, :delete ].each do |d|
|
82
|
+
|
83
|
+
describe d do
|
84
|
+
|
85
|
+
before(:each) do
|
86
|
+
@articles = []
|
87
|
+
5.times do |x|
|
88
|
+
@articles << Article.create(:title => "Title #{x}", :body => "Body #{x}")
|
89
|
+
end
|
90
|
+
Article.find(@articles[0..1].collect(&:id)).each do |a|
|
91
|
+
a.send(d)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should move some records to the archive table" do
|
96
|
+
Article.count.should == 3
|
97
|
+
Article::Archive.count.should == 2
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should preserve record attributes" do
|
101
|
+
2.times do |x|
|
102
|
+
original = @articles[x]
|
103
|
+
copy = Article::Archive.find(original.id)
|
104
|
+
article_match?(original, copy)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
|
3
|
+
describe ActsAsArchive::Base::Restore do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
establish_test_db
|
7
|
+
Article.create_archive_table
|
8
|
+
@connection = ActiveRecord::Base.connection
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'restore_all' do
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@articles = []
|
15
|
+
@connection.execute("TRUNCATE TABLE #{Article.table_name}")
|
16
|
+
5.times do |x|
|
17
|
+
@connection.execute(%{
|
18
|
+
INSERT INTO archived_#{Article.table_name} (`id`, `title`, `body`) VALUES (#{x+1}, 'Title #{x}', 'Body #{x}')
|
19
|
+
})
|
20
|
+
@articles << Article::Archive.find(x+1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'with conditions' do
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
# Mini restore parameter test
|
28
|
+
Article.restore_all [ 'id = ?', @articles[0].id ]
|
29
|
+
Article.restore_all "id = #{@articles[1].id}"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should move some records to the article table" do
|
33
|
+
Article::Archive.count.should == 3
|
34
|
+
Article.count.should == 2
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should preserve record attributes" do
|
38
|
+
2.times do |x|
|
39
|
+
original = @articles[x]
|
40
|
+
copy = Article.find(original.id)
|
41
|
+
article_match?(original, copy)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'without conditions' do
|
47
|
+
|
48
|
+
before(:each) do
|
49
|
+
Article.restore_all
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should move all records to the archive table" do
|
53
|
+
Article::Archive.count.should == 0
|
54
|
+
Article.count.should == 5
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should preserve record attributes" do
|
58
|
+
5.times do |x|
|
59
|
+
original = @articles[x]
|
60
|
+
copy = Article.find(original.id)
|
61
|
+
article_match?(original, copy)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
|
3
|
+
describe ActsAsArchive::Base::Table do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
establish_test_db
|
7
|
+
@connection = ActiveRecord::Base.connection
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'create_archive_table' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
Article.create_archive_table
|
14
|
+
@article_columns = @connection.columns("articles").collect(&:name)
|
15
|
+
@archive_columns = @connection.columns("archived_articles").collect(&:name)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should create an archive table" do
|
19
|
+
@connection.table_exists?("archived_articles").should == true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should create an archive table with the same structure as the original table" do
|
23
|
+
@article_columns.each do |col|
|
24
|
+
@archive_columns.include?(col).should == true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should add a deleted_at column to the archive table" do
|
29
|
+
(@archive_columns - @article_columns).should == [ 'deleted_at' ]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe ActsAsArchive::Migration do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
establish_test_db
|
7
|
+
Article.create_archive_table
|
8
|
+
@connection = ActiveRecord::Base.connection
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'method_missing_with_archive' do
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@old_article_columns = @connection.columns("articles").collect(&:name)
|
15
|
+
@old_archive_columns = @connection.columns("archived_articles").collect(&:name)
|
16
|
+
ActiveRecord::Migrator.migrate("#{SPEC}/db/migrate")
|
17
|
+
@new_article_columns = @connection.columns("articles").collect(&:name)
|
18
|
+
@new_archive_columns = @connection.columns("archived_articles").collect(&:name)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should migrate both tables up' do
|
22
|
+
(@new_article_columns - @old_article_columns).should == [ 'permalink' ]
|
23
|
+
(@new_archive_columns - @old_archive_columns).should == [ 'permalink' ]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should migrate both tables down' do
|
27
|
+
@old_article_columns = @new_article_columns
|
28
|
+
@old_archive_columns = @new_archive_columns
|
29
|
+
ActiveRecord::Migrator.migrate("#{SPEC}/db/migrate", 0)
|
30
|
+
@new_article_columns = @connection.columns("articles").collect(&:name)
|
31
|
+
@new_archive_columns = @connection.columns("archived_articles").collect(&:name)
|
32
|
+
(@old_article_columns - @new_article_columns).should == [ 'permalink' ]
|
33
|
+
(@old_archive_columns - @new_archive_columns).should == [ 'permalink' ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
File without changes
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
$TESTING=true
|
2
|
+
SPEC = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_record'
|
6
|
+
require 'logger'
|
7
|
+
require 'yaml'
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
require File.expand_path("#{SPEC}/../rails/init")
|
11
|
+
|
12
|
+
Spec::Runner.configure do |config|
|
13
|
+
end
|
14
|
+
|
15
|
+
def article_match?(original, copy)
|
16
|
+
copy.id.should == original.id
|
17
|
+
copy.title.should == original.title
|
18
|
+
copy.body.should == original.body
|
19
|
+
copy.created_at.to_s.should == original.created_at.to_s
|
20
|
+
copy.updated_at.to_s.should == original.updated_at.to_s
|
21
|
+
if copy.respond_to?(:deleted_at)
|
22
|
+
copy.deleted_at.strftime('%j%H%M').should == Time.now.strftime('%j%H%M')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def debug(object)
|
27
|
+
puts "<pre>"
|
28
|
+
pp object
|
29
|
+
puts "</pre>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def establish_test_db
|
33
|
+
# Establish connection
|
34
|
+
unless ActiveRecord::Base.connected?
|
35
|
+
config = YAML::load(File.open("#{SPEC}/db/config/database.yml"))
|
36
|
+
ActiveRecord::Base.configurations = config
|
37
|
+
ActiveRecord::Base.establish_connection(config['test'])
|
38
|
+
end
|
39
|
+
# Establish logger
|
40
|
+
logger_file = File.open("#{SPEC}/db/log/test.log", 'a')
|
41
|
+
logger_file.sync = true
|
42
|
+
@logger = Logger.new logger_file
|
43
|
+
ActiveRecord::Base.logger = @logger
|
44
|
+
# Drop articles and archived_articles
|
45
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS articles")
|
46
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS archived_articles")
|
47
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS schema_migrations")
|
48
|
+
# Create articles table
|
49
|
+
ActiveRecord::Base.connection.create_table(:articles) do |t|
|
50
|
+
t.string :title
|
51
|
+
t.string :body
|
52
|
+
t.timestamps
|
53
|
+
end
|
54
|
+
# Load model
|
55
|
+
require "#{SPEC}/db/models/article"
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: winton-acts_as_archive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Winton Welsh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-22 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Moves your deleted records to a different table
|
17
|
+
email: mail@wintoni.us
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- CHANGELOG.markdown
|
26
|
+
- MIT-LICENSE
|
27
|
+
- README.markdown
|
28
|
+
- Rakefile
|
29
|
+
- acts_as_archive.gemspec
|
30
|
+
- init.rb
|
31
|
+
- lib/acts_as_archive.rb
|
32
|
+
- lib/acts_as_archive/base.rb
|
33
|
+
- lib/acts_as_archive/base/destroy.rb
|
34
|
+
- lib/acts_as_archive/base/restore.rb
|
35
|
+
- lib/acts_as_archive/base/table.rb
|
36
|
+
- lib/acts_as_archive/migration.rb
|
37
|
+
- rails/init.rb
|
38
|
+
- spec/acts_as_archive/base/destroy_spec.rb
|
39
|
+
- spec/acts_as_archive/base/restore_spec.rb
|
40
|
+
- spec/acts_as_archive/base/table_spec.rb
|
41
|
+
- spec/acts_as_archive/migration_spec.rb
|
42
|
+
- spec/db/config/database.yml.example
|
43
|
+
- spec/db/log/.gitignore
|
44
|
+
- spec/db/migrate/001_add_to_articles.rb
|
45
|
+
- spec/db/models/article.rb
|
46
|
+
- spec/spec.opts
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
has_rdoc: false
|
49
|
+
homepage: http://github.com/winton/
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.2.0
|
71
|
+
signing_key:
|
72
|
+
specification_version: 2
|
73
|
+
summary: Moves your deleted records to a different table
|
74
|
+
test_files: []
|
75
|
+
|