undestroy 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/ARCH.md +13 -24
  2. data/README.md +72 -27
  3. data/lib/undestroy/binding.rb +1 -0
  4. data/lib/undestroy/binding/active_record.rb +53 -11
  5. data/lib/undestroy/binding/active_record/migration_statement.rb +118 -0
  6. data/lib/undestroy/binding/active_record/restorable.rb +28 -0
  7. data/lib/undestroy/config.rb +32 -8
  8. data/lib/undestroy/config/field.rb +20 -0
  9. data/lib/undestroy/restore.rb +44 -0
  10. data/lib/undestroy/version.rb +1 -1
  11. data/lib/undestroy/without_binding.rb +1 -0
  12. data/test/fixtures/ar.rb +8 -4
  13. data/test/fixtures/load_test/models1/test_model001.rb +2 -0
  14. data/test/fixtures/load_test/models1/test_model002.rb +2 -0
  15. data/test/fixtures/load_test/models1/test_module001.rb +2 -0
  16. data/test/fixtures/load_test/models1/test_module001/test_model003.rb +2 -0
  17. data/test/fixtures/load_test/models2/test_model001.rb +2 -0
  18. data/test/fixtures/load_test/models2/test_model002.rb +2 -0
  19. data/test/fixtures/load_test/models2/test_module001.rb +2 -0
  20. data/test/fixtures/load_test/models2/test_module001/test_model003.rb +2 -0
  21. data/test/helper.rb +12 -0
  22. data/test/helpers/model_loading.rb +20 -0
  23. data/test/integration/active_record_test.rb +50 -1
  24. data/test/irb.rb +2 -0
  25. data/test/unit/archive_test.rb +1 -1
  26. data/test/unit/binding/active_record/migration_statement_test.rb +306 -0
  27. data/test/unit/binding/active_record/restorable_test.rb +132 -0
  28. data/test/unit/binding/active_record_test.rb +187 -19
  29. data/test/unit/config/field_test.rb +86 -0
  30. data/test/unit/config_test.rb +114 -8
  31. data/test/unit/restore_test.rb +95 -0
  32. metadata +35 -3
@@ -0,0 +1,132 @@
1
+ require 'assert'
2
+
3
+ module Undestroy::Binding::ActiveRecord::Restorable::Test
4
+
5
+ class Base < Undestroy::Test::Base
6
+ desc 'Binding::ActiveRecord::Restorable module'
7
+ subject { Undestroy::Binding::ActiveRecord::Restorable }
8
+
9
+ setup do
10
+ @ar = Undestroy::Test::ARMain
11
+ @receiver = Class.new(@ar) do
12
+ cattr_accessor :calls
13
+ self.calls = []
14
+
15
+ def destroy
16
+ calls << [:destroy]
17
+ self.freeze
18
+ end
19
+ end
20
+ @receiver.table_name = "foobar"
21
+ @receiver.class_attribute :undestroy_model_binding, :instance_writer => false
22
+ @receiver.undestroy_model_binding = Undestroy::Binding::ActiveRecord.new(@receiver)
23
+ @receiver.connection.create_table :foobar do |t|
24
+ t.string :name
25
+ end
26
+
27
+ @restore = Class.new do
28
+ cattr_accessor :calls
29
+ self.calls = []
30
+ def initialize(*args)
31
+ calls << [:init, args]
32
+ end
33
+ def run
34
+ calls << [:run]
35
+ end
36
+ end
37
+ @receiver.undestroy_model_binding.config.internals[:restore] = @restore
38
+ end
39
+
40
+ teardown do
41
+ @receiver.connection.drop_table :foobar
42
+ end
43
+
44
+ def assert_ran_restore(object)
45
+ assert_equal [:init, [{ :target => object, :config => @receiver.undestroy_model_binding.config }]],
46
+ @restore.calls[0]
47
+ assert_equal [:run], @restore.calls[1]
48
+ assert_equal 2, @restore.calls.size
49
+
50
+ end
51
+ end
52
+
53
+ class RestoreBaseMethods < Base
54
+ desc 'restore and restore__copy methods'
55
+
56
+ setup do
57
+ @receiver.send :include, subject
58
+ end
59
+
60
+ should "be instance methods" do
61
+ assert_respond_to :restore_copy, @receiver.new
62
+ assert_respond_to :restore, @receiver.new
63
+ end
64
+
65
+ should "create a config[:restore] instance passing self and the binding's config and call run on restore_copy" do
66
+ object = @receiver.new
67
+ object.restore_copy
68
+
69
+ assert_ran_restore(object)
70
+
71
+ # No Destroy
72
+ assert_equal 0, @receiver.calls.size
73
+ end
74
+
75
+ should "run restore_copy and then destroy when restore called" do
76
+ object = @receiver.new
77
+ object.restore
78
+
79
+ assert_ran_restore(object)
80
+
81
+ assert_equal [:destroy], @receiver.calls[0]
82
+ end
83
+ end
84
+
85
+ class RestoreRelationMethods < Base
86
+ desc 'restore_all method for relation'
87
+
88
+ setup do
89
+ @receiver.send :include, subject
90
+ end
91
+
92
+ should "include RelationExtensions on the singleton_class for @relation" do
93
+ assert_respond_to :restore_all, @receiver.send(:relation)
94
+ assert_includes subject::RelationExtensions, @receiver.send(:relation).singleton_class.ancestors
95
+ end
96
+
97
+ should "call restore on all items in the query" do
98
+ query = [@receiver.new, @receiver.new, @receiver.new]
99
+ relation = @receiver.send(:relation)
100
+ relation.instance_variable_set(:@records, query)
101
+ relation.instance_variable_set(:@loaded, true)
102
+ relation.restore_all
103
+ assert_equal [[:destroy], [:destroy], [:destroy]], @receiver.calls
104
+ end
105
+
106
+ should "reset the relation" do
107
+ relation = @receiver.send(:relation)
108
+ relation.instance_variable_set(:@records, [@receiver.new])
109
+ relation.instance_variable_set(:@loaded, true)
110
+ relation.restore_all
111
+ assert_equal [], relation.instance_variable_get(:@records)
112
+ end
113
+
114
+ should "return an array of restored records" do
115
+ records = [@receiver.new]
116
+ relation = @receiver.send(:relation)
117
+ relation.instance_variable_set(:@records, records)
118
+ relation.instance_variable_set(:@loaded, true)
119
+ assert_equal records, relation.restore_all
120
+ end
121
+
122
+ should "not be visible on other Relation instances" do
123
+ model = Class.new(@ar)
124
+ model.table_name = 'test'
125
+ @ar.connection.create_table :test
126
+ assert_not_respond_to :restore_all, model.send(:relation)
127
+ @ar.connection.drop_table :test
128
+ end
129
+ end
130
+
131
+ end
132
+
@@ -25,7 +25,15 @@ module Undestroy::Binding::ActiveRecord::Test
25
25
  desc 'basic instance'
26
26
  subject { Undestroy::Binding::ActiveRecord.new @model }
27
27
 
28
- should have_accessors :config, :model
28
+ should have_accessors :config, :model, :active
29
+
30
+ should "have instance method :active? as alias for :active" do
31
+ obj = subject
32
+ obj.active = false
33
+ assert_equal false, obj.active?
34
+ obj.active = true
35
+ assert_equal true, obj.active?
36
+ end
29
37
  end
30
38
 
31
39
  class AddClassMethod < Base
@@ -36,20 +44,13 @@ module Undestroy::Binding::ActiveRecord::Test
36
44
  undef_method :undestroy_model_binding if respond_to?(:undestroy_model_binding)
37
45
  remove_possible_method :undestroy_model_binding=
38
46
  class << self
39
- undef_method :undestroy_model_binding
40
47
  undef_method :undestroy
41
48
  end
42
49
  end
43
50
  @model._destroy_callbacks = []
44
51
  end
45
52
 
46
- should "add class_attr called `undestroy_model_binding`" do
47
- subject.add @model
48
- assert_respond_to :undestroy_model_binding, @model
49
- assert_respond_to :undestroy_model_binding=, @model
50
- end
51
-
52
- should "add undestroy class method to AR::Base initializing this binding" do
53
+ should "add undestroy class method to klass initializing this binding" do
53
54
  subject.add @model
54
55
  assert_respond_to :undestroy, @model
55
56
 
@@ -59,8 +60,31 @@ module Undestroy::Binding::ActiveRecord::Test
59
60
  assert_equal Hash.new, @model.undestroy_model_binding.config.fields
60
61
  end
61
62
 
62
- should "add before_destroy callback when undestroy called" do
63
+ should "allow adding to other classes" do
64
+ new_model = Class.new(@model)
65
+ subject.add(new_model)
66
+ assert_respond_to :undestroy, new_model
67
+ assert_not_respond_to :undestroy, @model
68
+ end
69
+
70
+ end
71
+
72
+ class UndestroyExtensionMethod < AddClassMethod
73
+ desc 'added undestroy method'
74
+
75
+ setup do
63
76
  subject.add @model
77
+ end
78
+
79
+ should "add class_attr called `undestroy_model_binding`" do
80
+ assert_not_respond_to :undestroy_model_binding, @model
81
+ assert_not_respond_to :undestroy_model_binding=, @model
82
+ @model.undestroy
83
+ assert_respond_to :undestroy_model_binding, @model
84
+ assert_respond_to :undestroy_model_binding=, @model
85
+ end
86
+
87
+ should "add before_destroy callback when undestroy called" do
64
88
  archive_class = Undestroy::Test::Fixtures::Archive
65
89
  @model.undestroy :internals => { :archive => archive_class }
66
90
 
@@ -75,20 +99,94 @@ module Undestroy::Binding::ActiveRecord::Test
75
99
  end
76
100
 
77
101
  should "only add callbacks once" do
78
- subject.add @model
79
102
  @model.undestroy
80
103
  @model.undestroy
81
104
  assert_equal 1, @model._destroy_callbacks.size
82
105
  end
83
106
 
84
- should "allow adding to other classes" do
85
- new_model = Class.new(@model)
86
- subject.add(new_model)
87
- assert_respond_to :undestroy_model_binding, new_model
88
- assert_not_respond_to :undestroy_model_binding, @model
107
+ should "add archived class method" do
108
+ assert_not_respond_to :archived, @model
109
+ @model.undestroy
110
+ assert_respond_to :archived, @model
111
+ end
112
+
113
+ should "add archived method that returns configured target_class after undestroy configured" do
114
+ @model.undestroy
115
+ assert_equal @model.undestroy_model_binding.config.target_class, @model.archived
116
+ end
117
+
118
+ should "add restore method" do
119
+ assert_not_respond_to :restore, @model
120
+ @model.undestroy
121
+ assert_respond_to :restore, @model
122
+ end
123
+
124
+ should "add restore method that passes args to target_class find and then calls 'restore' on each record" do
125
+ @model.undestroy
126
+ fixture = Class.new do
127
+ cattr_accessor :calls, :return_val
128
+ self.calls = []
129
+ self.return_val = nil
130
+
131
+ def self.find(*args)
132
+ self.calls << [:find, args]
133
+ self.return_val
134
+ end
135
+
136
+ def restore
137
+ self.calls << [:restore]
138
+ end
139
+ end
140
+
141
+ @model.undestroy_model_binding.config.target_class = fixture
142
+
143
+ fixture.return_val = fixture.new
144
+ @model.restore(:foo, :bar => :baz)
145
+
146
+ assert_equal [:find, [:foo, { :bar => :baz }]], fixture.calls[0]
147
+ assert_equal [:restore], fixture.calls[1]
148
+
149
+ fixture.calls = []
150
+
151
+ fixture.return_val = [fixture.new, fixture.new]
152
+ @model.restore
153
+
154
+ assert_equal [:restore], fixture.calls[1]
155
+ assert_equal [:restore], fixture.calls[2]
156
+ assert_nil fixture.calls[3]
157
+ end
158
+
159
+ should "add destroy! instance method that calls destroy without archival" do
160
+ @model.class_eval do
161
+ cattr_accessor :calls
162
+ self.calls = []
163
+
164
+ def destroy
165
+ calls << [:destroy, undestroy_model_binding.active]
166
+ end
167
+ end
168
+ @model.undestroy
169
+ assert_respond_to :destroy!, @model.new
170
+
171
+ @model.new.destroy!
172
+
173
+ assert_equal [:destroy, false], @model.calls[0]
89
174
  end
90
175
  end
91
176
 
177
+ class LoadModelsClassMethod < Base
178
+ desc 'load_models class method'
179
+ include Undestroy::Test::Helpers::ModelLoading
180
+
181
+ should "force all models to load recursively in supplied path" do
182
+ path = Undestroy::Test.fixtures_path('load_test', 'models1')
183
+ assert_loads_models(path) do
184
+ subject.load_models(path)
185
+ end
186
+ end
187
+
188
+ end
189
+
92
190
  class InitMethod < Base
93
191
  desc 'init method'
94
192
 
@@ -111,24 +209,36 @@ module Undestroy::Binding::ActiveRecord::Test
111
209
  assert_instance_of Undestroy::Config, binding.config
112
210
  end
113
211
 
212
+ should "default :active attr to true" do
213
+ assert subject.new(@model).active, "Should be active by default"
214
+ end
215
+
114
216
  should "merge config options onto global config" do
115
217
  Undestroy::Config.configure do |config|
116
218
  config.fields = {}
117
219
  end
118
220
  assert_equal Hash.new, subject.new(@model).config.fields
119
221
  assert_not_equal subject.new(@model).config.object_id, Undestroy::Config.config.object_id
222
+ assert_not_equal subject.new(@model).config.fields.object_id, Undestroy::Config.config.fields.object_id
120
223
  assert_equal({ :foo => :bar }, subject.new(@model, :fields => { :foo => :bar }).config.fields)
121
224
  end
122
225
 
226
+ should "accept block and pass config object to it" do
227
+ binding = subject.new @model, {} do |config|
228
+ config.fields = {}
229
+ end
230
+ assert_equal Hash.new, binding.config.fields
231
+ end
232
+
123
233
  should "set config.source_class to value of model" do
124
234
  binding = subject.new(@model)
125
235
  assert_equal @model, binding.config.source_class
126
236
  end
127
237
 
128
- should "default :table_name to 'archive_{source.table_name}'" do
238
+ should "default :table_name to '{config.prefix}{source.table_name}'" do
129
239
  @model.table_name = :foobar
130
- binding = subject.new(@model)
131
- assert_equal 'archive_foobar', binding.config.table_name
240
+ binding = subject.new(@model, :prefix => "prefix_archive_")
241
+ assert_equal 'prefix_archive_foobar', binding.config.table_name
132
242
  end
133
243
 
134
244
  should "create a target_class if none provided" do
@@ -162,6 +272,24 @@ module Undestroy::Binding::ActiveRecord::Test
162
272
  assert_equal 'tmp/alt.db', binding.config.target_class.connection_config[:database]
163
273
  end
164
274
 
275
+ should "create class_attribute undestroy_model_binding with no instance writer" do
276
+ binding = subject.new(@model)
277
+ assert_respond_to :undestroy_model_binding, binding.config.target_class
278
+ assert_respond_to :undestroy_model_binding=, binding.config.target_class
279
+ assert_respond_to :undestroy_model_binding, binding.config.target_class.new
280
+ assert_not_respond_to :undestroy_model_binding=, binding.config.target_class.new
281
+ end
282
+
283
+ should "set self to the undestroy_model_binding on the target_class" do
284
+ binding = subject.new(@model)
285
+ assert_equal binding, binding.config.target_class.undestroy_model_binding
286
+ end
287
+
288
+ should "include Restorable mixin" do
289
+ binding = subject.new(@model)
290
+ assert_includes Undestroy::Binding::ActiveRecord::Restorable, binding.config.target_class.ancestors
291
+ end
292
+
165
293
  end
166
294
 
167
295
  class BeforeDestroy < Base
@@ -184,5 +312,45 @@ module Undestroy::Binding::ActiveRecord::Test
184
312
  end
185
313
  end
186
314
 
315
+ class PrefixTableNameMethod < Base
316
+ desc 'prefix_table_name method'
317
+ subject { @binding ||= Undestroy::Binding::ActiveRecord.new(@model) }
318
+
319
+ should "return {config.prefix}{source.table_name}" do
320
+ subject.config.prefix = "archive_prefix_"
321
+ assert_equal "archive_prefix_foo", subject.prefix_table_name("foo")
322
+ end
323
+ end
324
+
325
+ class DeactivatedMethod < Base
326
+ desc 'deactivated method'
327
+ subject { @binding ||= Undestroy::Binding::ActiveRecord.new(@model) }
328
+
329
+ should "set activated to false and yield to block" do
330
+ called = false
331
+ block = proc {
332
+ called = true
333
+ assert !subject.active, "Binding should be deactivated"
334
+ }
335
+ subject.deactivated(&block)
336
+ assert called, "Block should be called"
337
+ end
338
+
339
+ should "always set activated back to original value" do
340
+ block = proc {}
341
+ raise_block = proc { raise "FOO" }
342
+
343
+ subject.deactivated(&block)
344
+ assert subject.active, "Should be activated"
345
+
346
+ assert_raises { subject.deactivated(&raise_block) }
347
+ assert subject.active, "Should be activated"
348
+
349
+ subject.active = false
350
+ subject.deactivated(&block)
351
+ assert !subject.active, "Should not be activated"
352
+ end
353
+ end
354
+
187
355
  end
188
356
 
@@ -0,0 +1,86 @@
1
+ require 'assert'
2
+
3
+ class Undestroy::Config::Field::Test
4
+
5
+ class Base < Undestroy::Test::Base
6
+ desc 'Undestroy::Config::Field class'
7
+ subject { @subject_class }
8
+
9
+ setup do
10
+ @subject_class = Undestroy::Config::Field
11
+ end
12
+ end
13
+
14
+ class BasicInstance < Base
15
+ desc 'basic instance'
16
+ subject { @subject_class.new :foo, :foo, 'foo' }
17
+
18
+ should have_accessors(:name, :type, :raw_value)
19
+
20
+ end
21
+
22
+ class InitMethod < Base
23
+ desc 'initialize method'
24
+
25
+ should "have 2 required params" do
26
+ assert_raises(ArgumentError) { subject.new }
27
+ assert_not_raises { subject.new :foo, :bar, 'val' }
28
+ assert_not_raises { subject.new(:foo, :bar) { 'val' } }
29
+ end
30
+
31
+ should "store name in name attr" do
32
+ obj = subject.new :field, :string, 'val'
33
+ assert_equal :field, obj.name
34
+ end
35
+
36
+ should "store type in type attr" do
37
+ obj = subject.new :field, :string, 'val'
38
+ assert_equal :string, obj.type
39
+ end
40
+
41
+ should "store value in raw_value attr if present" do
42
+ obj = subject.new :field, :string, 'val'
43
+ assert_equal 'val', obj.raw_value
44
+ end
45
+
46
+ should "store optional block in raw_value" do
47
+ block = proc { "foo" }
48
+ obj = subject.new :field, :string, 'val', &block
49
+ assert_equal block, obj.raw_value
50
+ end
51
+
52
+ should "raise if a block and a value are not passed" do
53
+ assert_raises(ArgumentError) { subject.new :field, :string }
54
+ end
55
+ end
56
+
57
+ class ValueMethod < Base
58
+ desc 'value method'
59
+
60
+ should "return raw_value if not callable" do
61
+ obj = subject.new :field, :string, 'val'
62
+ assert_equal 'val', obj.value
63
+ end
64
+
65
+ should "evaluate raw_value with passed args" do
66
+ block = proc { |args| args }
67
+ block2 = proc { |arg1, arg2| [arg1, arg2] }
68
+ obj = subject.new :field, :string, &block
69
+ obj2 = subject.new :field, :string, &block2
70
+ assert_equal 1, obj.value(1)
71
+ assert_equal [1, 2], obj2.value(1, 2)
72
+ end
73
+ end
74
+
75
+ class SortOperator < Base
76
+ desc 'sort operator'
77
+
78
+ should "sort by alphanumeric field name" do
79
+ names = ["orange", :apple, "banana", "orange2"]
80
+ fields = names.collect { |name| Undestroy::Config::Field.new(name, :type, 'val') }
81
+ assert_equal names.collect(&:to_s).sort, fields.sort.collect { |f| f.name.to_s }
82
+ end
83
+ end
84
+
85
+ end
86
+