undestroy 0.0.1 → 0.0.2
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/ARCH.md +84 -0
- data/Gemfile +2 -1
- data/LICENSE +1 -1
- data/README.md +58 -48
- data/lib/undestroy.rb +3 -4
- data/lib/undestroy/archive.rb +36 -0
- data/lib/undestroy/binding.rb +8 -0
- data/lib/undestroy/binding/active_record.rb +63 -0
- data/lib/undestroy/config.rb +54 -0
- data/lib/undestroy/transfer.rb +22 -0
- data/lib/undestroy/version.rb +2 -1
- data/lib/undestroy/with_binding.rb +5 -0
- data/lib/undestroy/without_binding.rb +10 -0
- data/test/fixtures/active_record_models.rb +42 -0
- data/test/fixtures/ar.rb +41 -0
- data/test/fixtures/archive.rb +21 -0
- data/test/helper.rb +42 -1
- data/test/integration/active_record_test.rb +180 -0
- data/test/unit/archive_test.rb +115 -0
- data/test/unit/binding/active_record_test.rb +188 -0
- data/test/unit/config_test.rb +146 -0
- data/test/unit/transfer_test.rb +65 -0
- data/undestroy.gemspec +2 -1
- metadata +29 -6
- data/test/unit/undestroy_test.rb +0 -10
@@ -0,0 +1,42 @@
|
|
1
|
+
module Undestroy::Test::Fixtures::ActiveRecordModels
|
2
|
+
|
3
|
+
class Blog < Undestroy::Test::ARMain
|
4
|
+
undestroy
|
5
|
+
has_many :posts, :dependent => :destroy
|
6
|
+
end
|
7
|
+
|
8
|
+
class Post < Undestroy::Test::ARMain
|
9
|
+
undestroy
|
10
|
+
belongs_to :blog
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add_blog_tables(model)
|
14
|
+
model.connection.create_table :blogs do |t|
|
15
|
+
t.string :name
|
16
|
+
end
|
17
|
+
model.connection.create_table :archive_blogs do |t|
|
18
|
+
t.string :name
|
19
|
+
t.datetime :deleted_at
|
20
|
+
end
|
21
|
+
|
22
|
+
model.connection.create_table :posts do |t|
|
23
|
+
t.integer :blog_id
|
24
|
+
t.string :title
|
25
|
+
t.text :body
|
26
|
+
end
|
27
|
+
model.connection.create_table :archive_posts do |t|
|
28
|
+
t.datetime :deleted_at
|
29
|
+
t.integer :blog_id
|
30
|
+
t.string :title
|
31
|
+
t.text :body
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.remove_blog_tables(model)
|
36
|
+
model.connection.drop_table :blogs
|
37
|
+
model.connection.drop_table :archive_blogs
|
38
|
+
model.connection.drop_table :posts
|
39
|
+
model.connection.drop_table :archive_posts
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/test/fixtures/ar.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
class Undestroy::Test::Fixtures::ARFixture
|
3
|
+
attr_accessor :attributes
|
4
|
+
attr_reader :saved
|
5
|
+
|
6
|
+
alias :saved? :saved
|
7
|
+
|
8
|
+
def initialize(attributes={})
|
9
|
+
@saved = false
|
10
|
+
self.attributes = attributes.dup
|
11
|
+
self.attributes.delete(:id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
self.attributes[key.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, val)
|
19
|
+
self.attributes[key.to_sym] = val
|
20
|
+
end
|
21
|
+
|
22
|
+
# Method missing won't catch this one
|
23
|
+
def id
|
24
|
+
self.attributes[:id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
@saved = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Shortcut to build an instance for testing purposes.
|
32
|
+
def self.construct(attributes={})
|
33
|
+
self.new.tap do |fixture|
|
34
|
+
attributes.each do |field, value|
|
35
|
+
fixture[field] = value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class Undestroy::Test::Fixtures::Archive
|
3
|
+
def initialize(args)
|
4
|
+
@@data[:args] = args
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
@@data[:calls] << [:run]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.data
|
12
|
+
@@data
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.reset
|
16
|
+
@@data = { :calls => [] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Undestroy::Test::Fixtures::Archive.reset
|
21
|
+
|
data/test/helper.rb
CHANGED
@@ -1,2 +1,43 @@
|
|
1
|
-
require '
|
1
|
+
require 'undestroy'
|
2
|
+
|
3
|
+
ActiveRecord::Base.configurations = {
|
4
|
+
'main' => {
|
5
|
+
:adapter => 'sqlite3',
|
6
|
+
:database => 'tmp/main.db'
|
7
|
+
},
|
8
|
+
'alt' => {
|
9
|
+
:adapter => 'sqlite3',
|
10
|
+
:database => 'tmp/alt.db'
|
11
|
+
},
|
12
|
+
}
|
13
|
+
|
14
|
+
module Undestroy::Test
|
15
|
+
|
16
|
+
class Base < Assert::Context
|
17
|
+
|
18
|
+
teardown_once do
|
19
|
+
`rm -f tmp/*.db`
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveRecord::Base.establish_connection 'main'
|
25
|
+
class ARMain < ActiveRecord::Base
|
26
|
+
self.abstract_class = true
|
27
|
+
end
|
28
|
+
|
29
|
+
class ARAlt < ActiveRecord::Base
|
30
|
+
self.abstract_class = true
|
31
|
+
establish_connection 'alt'
|
32
|
+
end
|
33
|
+
|
34
|
+
module Integration
|
35
|
+
end
|
36
|
+
|
37
|
+
module Fixtures
|
38
|
+
autoload :ActiveRecordModels, 'test/fixtures/active_record_models'
|
39
|
+
autoload :ARFixture, 'test/fixtures/ar'
|
40
|
+
autoload :Archive, 'test/fixtures/archive'
|
41
|
+
end
|
42
|
+
end
|
2
43
|
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
module Undestroy::Test::Integration::ActiveRecordTest
|
4
|
+
|
5
|
+
class Base < Undestroy::Test::Base
|
6
|
+
desc 'ActiveRecord integration'
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@ar = Undestroy::Test::ARMain
|
10
|
+
@ar_alt = Undestroy::Test::ARAlt
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class ActiveRecordExtension < Base
|
16
|
+
desc 'extensions'
|
17
|
+
|
18
|
+
should "add extensions to AR" do
|
19
|
+
assert_respond_to :undestroy_model_binding, ActiveRecord::Base
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class BasicModelWithoutUndestroy < Base
|
24
|
+
desc 'basic model without Undestroy'
|
25
|
+
|
26
|
+
setup do
|
27
|
+
@ar.connection.create_table :basic_model_table do |t|
|
28
|
+
t.string :name
|
29
|
+
end
|
30
|
+
@model = Class.new(@ar)
|
31
|
+
@model.table_name = 'basic_model_table'
|
32
|
+
end
|
33
|
+
|
34
|
+
teardown do
|
35
|
+
@ar.connection.drop_table :basic_model_table
|
36
|
+
end
|
37
|
+
|
38
|
+
should "successfully traverse the model lifecycle" do
|
39
|
+
record = @model.create!(:name => 'bar')
|
40
|
+
assert @model.first
|
41
|
+
assert_not_raises { record.destroy }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class BasicModel < Base
|
46
|
+
desc 'basic model with Undestroy'
|
47
|
+
|
48
|
+
setup do
|
49
|
+
@ar.connection.create_table :basic_model_table do |t|
|
50
|
+
t.string :name
|
51
|
+
end
|
52
|
+
@ar.connection.create_table :archive_basic_model_table do |t|
|
53
|
+
t.string :name
|
54
|
+
t.datetime :deleted_at
|
55
|
+
end
|
56
|
+
@model = Class.new(@ar)
|
57
|
+
@model.table_name = 'basic_model_table'
|
58
|
+
end
|
59
|
+
|
60
|
+
teardown do
|
61
|
+
@ar.connection.drop_table :basic_model_table
|
62
|
+
@ar.connection.drop_table :archive_basic_model_table
|
63
|
+
end
|
64
|
+
|
65
|
+
should "create an archive record on destroy" do
|
66
|
+
@model.undestroy
|
67
|
+
target_class = @model.undestroy_model_binding.config.target_class
|
68
|
+
|
69
|
+
@model.create(:name => "foo")
|
70
|
+
original = @model.first
|
71
|
+
original.destroy
|
72
|
+
archive = target_class.first
|
73
|
+
|
74
|
+
assert original
|
75
|
+
assert archive
|
76
|
+
assert_equal original.id, archive.id
|
77
|
+
assert_equal 'foo', archive.name
|
78
|
+
assert_kind_of Time, archive.deleted_at
|
79
|
+
assert (Time.now - archive.deleted_at) < 1.second
|
80
|
+
assert_equal 0, @model.all.size
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class BasicModelWithDifferentBase < Base
|
85
|
+
desc 'basic model with Undestroy and alternate abstract class'
|
86
|
+
|
87
|
+
setup do
|
88
|
+
@ar.connection.create_table :basic_model_table do |t|
|
89
|
+
t.string :name
|
90
|
+
end
|
91
|
+
@ar_alt.connection.create_table :archive_basic_model_table do |t|
|
92
|
+
t.string :name
|
93
|
+
t.datetime :deleted_at
|
94
|
+
end
|
95
|
+
@model = Class.new(@ar)
|
96
|
+
@model.table_name = 'basic_model_table'
|
97
|
+
|
98
|
+
@model.undestroy :abstract_class => @ar_alt
|
99
|
+
@target_class = @model.undestroy_model_binding.config.target_class
|
100
|
+
end
|
101
|
+
|
102
|
+
teardown do
|
103
|
+
@ar.connection.drop_table :basic_model_table
|
104
|
+
@ar_alt.connection.drop_table :archive_basic_model_table
|
105
|
+
end
|
106
|
+
|
107
|
+
should "create an archive record on destroy" do
|
108
|
+
@model.create!(:name => "bar")
|
109
|
+
original = @model.first
|
110
|
+
original.destroy
|
111
|
+
archive = @target_class.first
|
112
|
+
|
113
|
+
assert_not @model.first
|
114
|
+
assert original
|
115
|
+
assert archive
|
116
|
+
assert_equal original.id, archive.id
|
117
|
+
assert_equal 'bar', archive.name
|
118
|
+
assert_kind_of Time, archive.deleted_at
|
119
|
+
assert (Time.now - archive.deleted_at) < 1.second
|
120
|
+
assert_equal 1, @target_class.all.size
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class DependentDestroy < Base
|
125
|
+
desc 'model with dependent undestroy enabled models'
|
126
|
+
|
127
|
+
setup do
|
128
|
+
@fixtures = Undestroy::Test::Fixtures::ActiveRecordModels
|
129
|
+
|
130
|
+
@blog = @fixtures::Blog
|
131
|
+
@post = @fixtures::Post
|
132
|
+
@fixtures.add_blog_tables(@blog)
|
133
|
+
|
134
|
+
@archive_blog = @blog.undestroy_model_binding.config.target_class
|
135
|
+
@archive_post = @post.undestroy_model_binding.config.target_class
|
136
|
+
end
|
137
|
+
|
138
|
+
teardown do
|
139
|
+
@fixtures.remove_blog_tables(@blog)
|
140
|
+
end
|
141
|
+
|
142
|
+
should "archive orphan blog" do
|
143
|
+
blog = @blog.create!(:name => "Foo Blog")
|
144
|
+
assert @blog.first
|
145
|
+
blog.destroy
|
146
|
+
assert_not @blog.first
|
147
|
+
assert @archive_blog.first
|
148
|
+
assert_equal "Foo Blog", @archive_blog.first.name
|
149
|
+
end
|
150
|
+
|
151
|
+
should "archive post" do
|
152
|
+
blog = @blog.create!(:name => "Bar Blog")
|
153
|
+
post = @post.create!(:title => "Foo Post", :body => "text", :blog => blog)
|
154
|
+
assert blog and post
|
155
|
+
post.destroy
|
156
|
+
archive = @archive_post.first
|
157
|
+
|
158
|
+
assert_not @post.first
|
159
|
+
assert archive
|
160
|
+
assert_equal post.id, archive.id
|
161
|
+
assert_equal "text", archive.body
|
162
|
+
assert_equal "Foo Post", archive.title
|
163
|
+
assert_equal blog.id, archive.blog_id
|
164
|
+
end
|
165
|
+
|
166
|
+
should "remove all posts and archive them when the blog is destroyed" do
|
167
|
+
blog = @blog.create!(:name => "My Verbose Blog")
|
168
|
+
posts = (1..10).collect do |i|
|
169
|
+
@post.create!(:title => "Article #{i}", :body => "Some text", :blog => blog)
|
170
|
+
end
|
171
|
+
blog.destroy
|
172
|
+
|
173
|
+
assert_equal 0, @blog.all.size
|
174
|
+
assert_equal 0, @post.all.size
|
175
|
+
assert_equal 1, @archive_blog.all.size
|
176
|
+
assert_equal 10, @archive_post.all.size
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'assert'
|
2
|
+
|
3
|
+
module Undestroy::Archive::Test
|
4
|
+
class Base < Undestroy::Test::Base
|
5
|
+
subject { Undestroy::Archive }
|
6
|
+
desc 'Undestroy::Archive class'
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@default_init = {
|
10
|
+
:source => Undestroy::Test::Fixtures::ARFixture.construct(:id => 1, :name => "Foo"),
|
11
|
+
:config => Undestroy::Config.new
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def archive_instance(args={})
|
16
|
+
Undestroy::Archive.new(@default_init.merge(args))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BasicInstance < Base
|
21
|
+
subject { archive_instance }
|
22
|
+
desc 'basic instance'
|
23
|
+
|
24
|
+
should have_accessors :source, :config, :transfer
|
25
|
+
end
|
26
|
+
|
27
|
+
class InitMethod < Base
|
28
|
+
desc 'initialize method'
|
29
|
+
|
30
|
+
should "require :source and :config options in hash argument" do
|
31
|
+
assert_raises(ArgumentError) { subject.new }
|
32
|
+
assert_raises(ArgumentError) { subject.new :source => nil }
|
33
|
+
assert_raises(ArgumentError) { subject.new :config => nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
should "set the source to source attr" do
|
37
|
+
obj = archive_instance :source => "foo"
|
38
|
+
assert_equal "foo", obj.source
|
39
|
+
end
|
40
|
+
|
41
|
+
should "set the config to config attr" do
|
42
|
+
obj = archive_instance :config => "foo"
|
43
|
+
assert_equal "foo", obj.config
|
44
|
+
end
|
45
|
+
|
46
|
+
should "set optional :transfer to transfer attr" do
|
47
|
+
obj = archive_instance :transfer => "foo"
|
48
|
+
assert_equal "foo", obj.transfer
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class TransferMethod < BasicInstance
|
53
|
+
desc 'transfer method'
|
54
|
+
|
55
|
+
setup do
|
56
|
+
@archive = subject
|
57
|
+
@archive.config.target_class = Undestroy::Test::Fixtures::ARFixture
|
58
|
+
end
|
59
|
+
|
60
|
+
should "return instance of config.internals[:transfer]" do
|
61
|
+
assert_instance_of @archive.config.internals[:transfer], @archive.transfer
|
62
|
+
end
|
63
|
+
|
64
|
+
should "cache the created instance" do
|
65
|
+
assert_equal @archive.transfer.object_id, @archive.transfer.object_id
|
66
|
+
end
|
67
|
+
|
68
|
+
should "set fields on instance to config.fields.merge(source.attributes) with evaled procs" do
|
69
|
+
target = @archive.transfer.target
|
70
|
+
assert_instance_of @archive.config.target_class, @archive.transfer.target
|
71
|
+
assert_equal 1, target.attributes[:id]
|
72
|
+
assert_equal "Foo", target.attributes[:name]
|
73
|
+
assert_instance_of Time, target.attributes[:deleted_at]
|
74
|
+
end
|
75
|
+
|
76
|
+
should "eval lambdas with source instance as argument" do
|
77
|
+
val = nil
|
78
|
+
@archive.config.fields[:test] = proc { |arg| val = arg; "FOO" }
|
79
|
+
target = @archive.transfer.target
|
80
|
+
assert_equal @archive.source, val
|
81
|
+
assert_equal "FOO", target.attributes[:test]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class RunMethod < Base
|
86
|
+
desc 'run method'
|
87
|
+
|
88
|
+
should "exist" do
|
89
|
+
assert_respond_to :run, archive_instance
|
90
|
+
end
|
91
|
+
|
92
|
+
should "call run on the transfer model" do
|
93
|
+
foo = Class.new do
|
94
|
+
@@called = false
|
95
|
+
def initialize(options={})
|
96
|
+
end
|
97
|
+
|
98
|
+
def run
|
99
|
+
@@called = true
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.called
|
103
|
+
@@called
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
archive = archive_instance(:transfer => foo.new)
|
108
|
+
|
109
|
+
assert !foo.called
|
110
|
+
archive.run
|
111
|
+
assert foo.called
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|