undestroy 0.0.2 → 0.1.0

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.
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
+