undestroy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ require "undestroy/version"
2
+ require "undestroy/config"
3
+ require "undestroy/transfer"
4
+ require "undestroy/archive"
5
+ require "undestroy/binding"
6
+
7
+ module Undestroy
8
+ end
9
+
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
@@ -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
+
@@ -1,2 +1,43 @@
1
- require 'assert'
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
+