acts_as_revisionable 1.0.6 → 1.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.
@@ -1,176 +1,723 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe ActsAsRevisionable do
4
-
5
- before :all do
4
+
5
+ before(:all) do
6
6
  ActsAsRevisionable::Test.create_database
7
+ ActsAsRevisionable::RevisionRecord.create_table
8
+
9
+ ActiveRecord::Base.store_full_sti_class = true
10
+
11
+ class RevisionableTestSubThing < ActiveRecord::Base
12
+ connection.create_table(:revisionable_test_sub_things) do |t|
13
+ t.column :name, :string
14
+ t.column :revisionable_test_many_thing_id, :integer
15
+ end unless table_exists?
16
+ end
17
+
18
+ class RevisionableTestManyThing < ActiveRecord::Base
19
+ connection.create_table(:revisionable_test_many_things) do |t|
20
+ t.column :name, :string
21
+ t.column :revisionable_test_model_id, :integer
22
+ end unless table_exists?
23
+
24
+ has_many :sub_things, :class_name => 'RevisionableTestSubThing'
25
+ end
26
+
27
+ class RevisionableTestManyOtherThing < ActiveRecord::Base
28
+ connection.create_table(:revisionable_test_many_other_things) do |t|
29
+ t.column :name, :string
30
+ t.column :revisionable_test_model_id, :integer
31
+ end unless table_exists?
32
+ end
33
+
34
+ class RevisionableTestCompositeKeyThing < ActiveRecord::Base
35
+ connection.create_table(:revisionable_test_composite_key_things, :id => false) do |t|
36
+ t.column :name, :string
37
+ t.column :revisionable_test_model_id, :integer
38
+ t.column :other_id, :integer
39
+ end unless table_exists?
40
+ set_primary_keys :revisionable_test_model_id, :other_id
41
+ belongs_to :revisionable_test_model
42
+ end
43
+
44
+ class RevisionableTestOneThing < ActiveRecord::Base
45
+ connection.create_table(:revisionable_test_one_things) do |t|
46
+ t.column :name, :string
47
+ t.column :revisionable_test_model_id, :integer
48
+ end unless table_exists?
49
+ end
50
+
51
+ class NonRevisionableTestModel < ActiveRecord::Base
52
+ connection.create_table(:non_revisionable_test_models) do |t|
53
+ t.column :name, :string
54
+ end unless table_exists?
55
+ end
56
+
57
+ class NonRevisionableTestModelsRevisionableTestModel < ActiveRecord::Base
58
+ connection.create_table(:non_revisionable_test_models_revisionable_test_models, :id => false) do |t|
59
+ t.column :non_revisionable_test_model_id, :integer
60
+ t.column :revisionable_test_model_id, :integer
61
+ end unless table_exists?
62
+ end
63
+
64
+ class RevisionableTestModel < ActiveRecord::Base
65
+ connection.create_table(:revisionable_test_models) do |t|
66
+ t.column :name, :string
67
+ t.column :secret, :integer
68
+ end unless table_exists?
69
+
70
+ has_many :many_things, :class_name => 'RevisionableTestManyThing', :dependent => :destroy
71
+ has_many :many_other_things, :class_name => 'RevisionableTestManyOtherThing', :dependent => :destroy
72
+ has_one :one_thing, :class_name => 'RevisionableTestOneThing'
73
+ has_and_belongs_to_many :non_revisionable_test_models
74
+ has_many :composite_key_things, :class_name => 'RevisionableTestCompositeKeyThing', :dependent => :destroy
75
+
76
+ attr_protected :secret
77
+
78
+ acts_as_revisionable :limit => 3, :dependent => :keep, :associations => [:one_thing, :non_revisionable_test_models, {:many_things => :sub_things}, :composite_key_things]
79
+
80
+ def set_secret(val)
81
+ self.secret = val
82
+ end
83
+
84
+ private
85
+
86
+ def secret=(val)
87
+ self[:secret] = val
88
+ end
89
+ end
90
+
91
+ class OtherRevisionableTestModel < ActiveRecord::Base
92
+ connection.create_table(table_name) do |t|
93
+ t.column :name, :string
94
+ t.column :secret, :integer
95
+ end unless table_exists?
96
+
97
+ acts_as_revisionable :on_update => true
98
+ end
99
+
100
+ module ActsAsRevisionable
101
+ class RevisionableNamespaceModel < ActiveRecord::Base
102
+ connection.create_table(:revisionable_namespace_models) do |t|
103
+ t.column :name, :string
104
+ t.column :type_name, :string
105
+ end unless table_exists?
106
+
107
+ set_inheritance_column :type_name
108
+ acts_as_revisionable :dependent => :keep, :on_destroy => true, :encoding => :xml
109
+ self.store_full_sti_class = false
110
+ end
111
+
112
+ class RevisionableSubclassModel < RevisionableNamespaceModel
113
+ end
114
+ end
7
115
  end
8
-
116
+
9
117
  after :all do
10
118
  ActsAsRevisionable::Test.delete_database
11
119
  end
120
+
121
+ before :each do
122
+ RevisionableTestModel.delete_all
123
+ RevisionableTestManyThing.delete_all
124
+ RevisionableTestManyOtherThing.delete_all
125
+ RevisionableTestSubThing.delete_all
126
+ RevisionableTestOneThing.delete_all
127
+ NonRevisionableTestModelsRevisionableTestModel.delete_all
128
+ NonRevisionableTestModel.delete_all
129
+ ActsAsRevisionable::RevisionRecord.delete_all
130
+ ActsAsRevisionable::RevisionableNamespaceModel.delete_all
131
+ OtherRevisionableTestModel.delete_all
132
+ end
133
+
134
+ context "injected methods" do
135
+ it "should be able to inject revisionable behavior onto ActiveRecord::Base" do
136
+ ActiveRecord::Base.included_modules.should include(ActsAsRevisionable)
137
+ end
12
138
 
13
- class TestRevisionableModel
14
- include ActsAsRevisionable
15
-
16
- attr_accessor :id
17
-
18
- def update
19
- really_update
139
+ it "should add as has_many :record_revisions association" do
140
+ RevisionableTestModel.new.revision_records.should == []
20
141
  end
21
-
22
- def really_update
142
+
143
+ it "should parse the revisionable associations" do
144
+ RevisionableTestModel.revisionable_associations.should == {:composite_key_things=>true, :non_revisionable_test_models=>true, :one_thing=>true, :many_things=>{:sub_things=>true}}
23
145
  end
146
+ end
147
+
148
+ context "accessing revisions" do
149
+ let(:record_1){ RevisionableTestModel.create(:name => "record 1") }
150
+ let(:record_2){ OtherRevisionableTestModel.create(:name => "record 2") }
24
151
 
25
- def self.has_many (name, options)
26
- @associations ||= {}
27
- @associations[name] = options
152
+ it "should be able to get a revision for a model" do
153
+ revision_1 = ActsAsRevisionable::RevisionRecord.create(record_1)
154
+ revision_2 = ActsAsRevisionable::RevisionRecord.create(record_1)
155
+ revision_3 = ActsAsRevisionable::RevisionRecord.create(record_2)
156
+ record_1.revision(1).should == revision_1
157
+ record_1.revision(2).should == revision_2
158
+ record_1.revision(3).should == nil
159
+ record_2.revision(1).should == revision_3
28
160
  end
29
161
 
30
- def self.associations
31
- @associations
162
+ it "should be able to get a revision for an id" do
163
+ revision_1 = ActsAsRevisionable::RevisionRecord.create(record_1)
164
+ revision_2 = ActsAsRevisionable::RevisionRecord.create(record_1)
165
+ revision_3 = ActsAsRevisionable::RevisionRecord.create(record_2)
166
+ RevisionableTestModel.revision(record_1.id, 1).should == revision_1
167
+ RevisionableTestModel.revision(record_1.id, 2).should == revision_2
168
+ RevisionableTestModel.revision(record_1.id, 3).should == nil
169
+ OtherRevisionableTestModel.revision(record_2.id, 1).should == revision_3
32
170
  end
33
171
 
34
- private :update
172
+ it "should be able to get the last revision for a model" do
173
+ revision_1 = ActsAsRevisionable::RevisionRecord.create(record_1)
174
+ revision_2 = ActsAsRevisionable::RevisionRecord.create(record_1)
175
+ revision_3 = ActsAsRevisionable::RevisionRecord.create(record_2)
176
+ record_1.last_revision.should == revision_2
177
+ record_2.last_revision.should == revision_3
178
+ end
35
179
 
36
- acts_as_revisionable :limit => 10, :on_update => true, :associations => [:one, {:two => :two_1, :three => [:three_1, :three_2]}, {:four => :four_1}], :encoding => :encoding
37
- end
38
-
39
- it "should be able to inject revisionable behavior onto ActiveRecord::Base" do
40
- ActiveRecord::Base.included_modules.should include(ActsAsRevisionable)
180
+ it "should be able to get the last revision for an id" do
181
+ revision_1 = ActsAsRevisionable::RevisionRecord.create(record_1)
182
+ revision_2 = ActsAsRevisionable::RevisionRecord.create(record_1)
183
+ revision_3 = ActsAsRevisionable::RevisionRecord.create(record_2)
184
+ RevisionableTestModel.last_revision(record_1.id).should == revision_2
185
+ OtherRevisionableTestModel.last_revision(record_2.id).should == revision_3
186
+ end
41
187
  end
42
188
 
43
- it "should add as has_many :record_revisions association" do
44
- TestRevisionableModel.associations[:revision_records].should == {:as => :revisionable, :dependent => :destroy, :order=>"revision DESC", :class_name => "ActsAsRevisionable::RevisionRecord"}
45
- end
189
+ context "storing revisions" do
190
+ it "should not save a revision for a new record" do
191
+ record = RevisionableTestModel.new(:name => "test")
192
+ record.store_revision do
193
+ record.save!
194
+ end
195
+ ActsAsRevisionable::RevisionRecord.count.should == 0
196
+ end
197
+
198
+ it "should only store revisions when a record is updated in a store_revision block" do
199
+ record = RevisionableTestModel.create(:name => "test")
200
+ record.name = "new name"
201
+ record.save!
202
+ ActsAsRevisionable::RevisionRecord.count.should == 0
203
+ record.store_revision do
204
+ record.name = "newer name"
205
+ record.save!
206
+ end
207
+ ActsAsRevisionable::RevisionRecord.count.should == 1
208
+ end
46
209
 
47
- it "should parse the revisionable associations" do
48
- TestRevisionableModel.revisionable_associations.should == {:one => true, :two => {:two_1 => true}, :three => {:three_1 => true, :three_2 => true}, :four => {:four_1 => true}}
49
- end
210
+ it "should always store revisions whenever a record is saved if :on_update is true" do
211
+ record = OtherRevisionableTestModel.create(:name => "test")
212
+ record.name = "new name"
213
+ record.save!
214
+ ActsAsRevisionable::RevisionRecord.count.should == 1
215
+ record.store_revision do
216
+ record.name = "newer name"
217
+ record.save!
218
+ end
219
+ ActsAsRevisionable::RevisionRecord.count.should == 2
220
+ end
50
221
 
51
- it "should handle storing revisions in a block" do
52
- record = TestRevisionableModel.new
53
- record.id = 1
54
- record.stub!(:new_record?).and_return(nil)
55
- end
222
+ it "should only store revisions when a record is destroyed in a store_revision block" do
223
+ record_1 = RevisionableTestModel.create(:name => "test")
224
+ record_1.store_revision do
225
+ record_1.name = "newer name"
226
+ record_1.save!
227
+ end
228
+ record_2 = RevisionableTestModel.create(:name => "test")
229
+ record_2.store_revision do
230
+ record_2.name = "newer name"
231
+ record_2.save!
232
+ end
233
+ ActsAsRevisionable::RevisionRecord.count.should == 2
234
+ record_1.destroy
235
+ ActsAsRevisionable::RevisionRecord.count.should == 2
236
+ record_2.store_revision do
237
+ record_2.destroy
238
+ end
239
+ ActsAsRevisionable::RevisionRecord.count.should == 3
240
+ end
56
241
 
57
- it "should not store revisions for a new record" do
58
- record = TestRevisionableModel.new
59
- record.stub!(:new_record?).and_return(true)
60
- end
242
+ it "should always store revisions whenever a record is destroyed if :on_destroy is true" do
243
+ record_1 = ActsAsRevisionable::RevisionableNamespaceModel.create(:name => "test")
244
+ record_1.store_revision do
245
+ record_1.name = "newer name"
246
+ record_1.save!
247
+ end
248
+ record_2 = ActsAsRevisionable::RevisionableNamespaceModel.create(:name => "test")
249
+ record_2.store_revision do
250
+ record_2.name = "newer name"
251
+ record_2.save!
252
+ end
253
+ ActsAsRevisionable::RevisionRecord.count.should == 2
254
+ record_1.destroy
255
+ ActsAsRevisionable::RevisionRecord.count.should == 3
256
+ record_2.store_revision do
257
+ record_2.destroy
258
+ end
259
+ ActsAsRevisionable::RevisionRecord.count.should == 4
260
+ end
61
261
 
62
- it "should handle storing revisions" do
63
- record = TestRevisionableModel.new
64
- record.id = 1
65
- record.stub!(:new_record?).and_return(nil)
66
- record.stub!(:errors).and_return([])
67
- read_only_record = TestRevisionableModel.new
68
- TestRevisionableModel.should_receive(:find).with(1, :readonly => true).and_return(read_only_record)
69
- revision = mock(:revision)
70
- ActsAsRevisionable::RevisionRecord.should_receive(:transaction).and_yield
71
- read_only_record.should_receive(:create_revision!).and_return(revision)
72
- record.should_receive(:truncate_revisions!).with()
73
- record.should_receive(:really_update)
74
-
75
- record.store_revision do
76
- record.send(:update)
262
+ it "should be able to create a revision record" do
263
+ record_1 = RevisionableTestModel.create(:name => "test")
264
+ ActsAsRevisionable::RevisionRecord.count.should == 0
265
+ record_1.create_revision!
266
+ ActsAsRevisionable::RevisionRecord.count.should == 1
77
267
  end
78
- end
79
268
 
80
- it "should delete a revision if the update fails" do
81
- record = TestRevisionableModel.new
82
- record.id = 1
83
- record.stub!(:new_record?).and_return(nil)
84
- record.stub!(:errors).and_return([])
85
- read_only_record = TestRevisionableModel.new
86
- TestRevisionableModel.should_receive(:find).with(1, :readonly => true).and_return(read_only_record)
87
- revision = mock(:revision)
88
- ActsAsRevisionable::RevisionRecord.should_receive(:transaction).and_yield
89
- read_only_record.should_receive(:create_revision!).and_return(revision)
90
- record.should_receive(:truncate_revisions!).with()
91
- record.should_receive(:really_update).and_raise("update failed")
92
- revision.should_receive(:destroy)
93
-
94
- begin
269
+ it "should not create a revision entry if revisioning is disabled" do
270
+ record = RevisionableTestModel.create(:name => "test")
271
+ ActsAsRevisionable::RevisionRecord.count.should == 0
95
272
  record.store_revision do
96
- record.send(:update)
273
+ record.name = "new name"
274
+ record.save!
275
+ end
276
+ ActsAsRevisionable::RevisionRecord.count.should == 1
277
+ record.disable_revisioning do
278
+ record.store_revision do
279
+ record.name = "newer name"
280
+ record.save!
281
+ end
97
282
  end
98
- rescue
283
+ ActsAsRevisionable::RevisionRecord.count.should == 1
99
284
  end
100
- end
101
285
 
102
- it "should not error on deleting a revision if the update fails" do
103
- record = TestRevisionableModel.new
104
- record.id = 1
105
- record.stub!(:new_record?).and_return(nil)
106
- record.stub!(:errors).and_return([:error])
107
- read_only_record = TestRevisionableModel.new
108
- TestRevisionableModel.should_receive(:find).with(1, :readonly => true).and_return(read_only_record)
109
- revision = mock(:revision)
110
- ActsAsRevisionable::RevisionRecord.should_receive(:transaction).and_yield
111
- read_only_record.should_receive(:create_revision!).and_return(revision)
112
- record.should_receive(:truncate_revisions!).with()
113
- record.should_receive(:update).and_raise("update failed")
114
- revision.should_receive(:destroy).and_raise("destroy failed")
115
-
116
- record.store_revision do
117
- record.send(:update) rescue nil
286
+ it "should truncate the revisions when new ones are created" do
287
+ record = RevisionableTestModel.create(:name => "test")
288
+ 5.times do |i|
289
+ record.store_revision do
290
+ record.update_attribute(:name, "name #{i}")
291
+ end
292
+ end
293
+ ActsAsRevisionable::RevisionRecord.count.should == 3
294
+ record.revision_records.collect{|r| r.revision}.should == [5, 4, 3]
118
295
  end
119
- end
120
296
 
121
- it "should be able to create a revision record" do
122
- record = TestRevisionableModel.new
123
- revision = mock(:revision)
124
- ActsAsRevisionable::RevisionRecord.should_receive(:new).with(record, :encoding).and_return(revision)
125
- revision.should_receive(:save!)
126
- record.create_revision!.should == revision
297
+ it "should not save a revision if an update raises an exception" do
298
+ model = RevisionableTestModel.new(:name => 'test')
299
+ model.store_revision do
300
+ model.save!
301
+ end
302
+ model.reload
303
+ ActsAsRevisionable::RevisionRecord.count.should == 0
304
+
305
+ model.should_receive(:update).and_raise("update failed")
306
+ model.name = 'new_name'
307
+ begin
308
+ model.store_revision do
309
+ ActsAsRevisionable::RevisionRecord.count.should == 1
310
+ model.save
311
+ end
312
+ rescue
313
+ end
314
+ ActsAsRevisionable::RevisionRecord.count.should == 0
315
+ end
316
+
317
+ it "should not save a revision if an update fails with errors" do
318
+ model = RevisionableTestModel.new(:name => 'test')
319
+ model.store_revision do
320
+ model.save!
321
+ end
322
+ model.reload
323
+ ActsAsRevisionable::RevisionRecord.count.should == 0
324
+
325
+ model.name = 'new_name'
326
+ model.store_revision do
327
+ ActsAsRevisionable::RevisionRecord.count.should == 1
328
+ model.save!
329
+ model.errors.add(:name, "isn't right")
330
+ end
331
+ ActsAsRevisionable::RevisionRecord.count.should == 0
332
+ end
333
+
334
+ it "should mark the last revision for a deleted record as being trash" do
335
+ model = ActsAsRevisionable::RevisionableNamespaceModel.new(:name => 'test')
336
+ model.save!
337
+ model.store_revision do
338
+ model.name = "new name"
339
+ model.save!
340
+ end
341
+ model.destroy
342
+ ActsAsRevisionable::RevisionRecord.count.should == 2
343
+ ActsAsRevisionable::RevisionRecord.last_revision(ActsAsRevisionable::RevisionableNamespaceModel, model.id).should be_trash
344
+ end
127
345
  end
346
+
347
+ context "restoring revisions" do
348
+ it "should restore a record without associations" do
349
+ model = RevisionableTestModel.new(:name => 'test')
350
+ model.set_secret(1234)
351
+ model.store_revision do
352
+ model.save!
353
+ end
354
+ model.reload
355
+ ActsAsRevisionable::RevisionRecord.count.should == 0
356
+
357
+ model.name = 'new_name'
358
+ model.set_secret(5678)
359
+ model.store_revision do
360
+ model.save!
361
+ end
362
+ model.reload
363
+ ActsAsRevisionable::RevisionRecord.count.should == 1
364
+ model.name.should == 'new_name'
365
+ model.secret.should == 5678
366
+
367
+ restored = model.restore_revision(1)
368
+ restored.name.should == 'test'
369
+ restored.secret.should == 1234
370
+ restored.id.should == model.id
371
+
372
+ restored.store_revision do
373
+ restored.save!
374
+ end
375
+ RevisionableTestModel.count.should == 1
376
+ ActsAsRevisionable::RevisionRecord.count.should == 2
377
+ restored_model = RevisionableTestModel.find(model.id)
378
+ restored_model.name.should == restored.name
379
+ restored_model.secret.should == restored.secret
380
+ end
381
+
382
+ it "should restore a record with has_many associations" do
383
+ many_thing_1 = RevisionableTestManyThing.new(:name => 'many_thing_1')
384
+ many_thing_1.sub_things.build(:name => 'sub_thing_1')
385
+ many_thing_1.sub_things.build(:name => 'sub_thing_2')
386
+
387
+ model = RevisionableTestModel.new(:name => 'test')
388
+ model.many_things << many_thing_1
389
+ model.many_things.build(:name => 'many_thing_2')
390
+ model.many_other_things.build(:name => 'many_other_thing_1')
391
+ model.many_other_things.build(:name => 'many_other_thing_2')
392
+ model.save!
393
+ model.reload
394
+ RevisionableTestManyThing.count.should == 2
395
+ RevisionableTestSubThing.count.should == 2
396
+ RevisionableTestManyOtherThing.count.should == 2
397
+ ActsAsRevisionable::RevisionRecord.count.should == 0
398
+
399
+ model.store_revision do
400
+ model.name = 'new_name'
401
+ many_thing_1 = model.many_things.detect{|t| t.name == 'many_thing_1'}
402
+ many_thing_1.name = 'new_many_thing_1'
403
+ sub_thing_1 = many_thing_1.sub_things.detect{|t| t.name == 'sub_thing_1'}
404
+ sub_thing_1.name = 'new_sub_thing_1'
405
+ sub_thing_2 = many_thing_1.sub_things.detect{|t| t.name == 'sub_thing_2'}
406
+ many_thing_1.sub_things.build(:name => 'sub_thing_3')
407
+ many_thing_1.sub_things.delete(sub_thing_2)
408
+ many_thing_2 = model.many_things.detect{|t| t.name == 'many_thing_2'}
409
+ model.many_things.delete(many_thing_2)
410
+ model.many_things.build(:name => 'many_thing_3')
411
+ many_other_thing_1 = model.many_other_things.detect{|t| t.name == 'many_other_thing_1'}
412
+ many_other_thing_1.name = 'new_many_other_thing_1'
413
+ many_other_thing_2 = model.many_other_things.detect{|t| t.name == 'many_other_thing_2'}
414
+ model.many_other_things.delete(many_other_thing_2)
415
+ model.many_other_things.build(:name => 'many_other_thing_3')
416
+ model.save!
417
+ many_thing_1.save!
418
+ sub_thing_1.save!
419
+ many_other_thing_1.save!
420
+ end
421
+
422
+ model.reload
423
+ ActsAsRevisionable::RevisionRecord.count.should == 1
424
+ RevisionableTestManyThing.count.should == 2
425
+ RevisionableTestSubThing.count.should == 3
426
+ RevisionableTestManyOtherThing.count.should == 2
427
+ model.name.should == 'new_name'
428
+ model.many_things.collect{|t| t.name}.sort.should == ['many_thing_3', 'new_many_thing_1']
429
+ model.many_things.detect{|t| t.name == 'new_many_thing_1'}.sub_things.collect{|t| t.name}.sort.should == ['new_sub_thing_1', 'sub_thing_3']
430
+ model.many_other_things.collect{|t| t.name}.sort.should == ['many_other_thing_3', 'new_many_other_thing_1']
431
+
432
+ # restore to memory
433
+ restored = model.restore_revision(1)
434
+ restored.name.should == 'test'
435
+ restored.id.should == model.id
436
+ restored.many_things.collect{|t| t.name}.sort.should == ['many_thing_1', 'many_thing_2']
437
+ restored.many_things.detect{|t| t.name == 'many_thing_1'}.sub_things.collect{|t| t.name}.sort.should == ['sub_thing_1', 'sub_thing_2']
438
+ restored.many_other_things.collect{|t| t.name}.sort.should == ['many_other_thing_3', 'new_many_other_thing_1']
439
+ restored.valid?.should == true
440
+
441
+ # make the restore to memory didn't affect the database
442
+ model.reload
443
+ model.name.should == 'new_name'
444
+ model.many_things(true).collect{|t| t.name}.sort.should == ['many_thing_3', 'new_many_thing_1']
445
+ model.many_things.detect{|t| t.name == 'new_many_thing_1'}.sub_things.collect{|t| t.name}.sort.should == ['new_sub_thing_1', 'sub_thing_3']
446
+ model.many_other_things.collect{|t| t.name}.sort.should == ['many_other_thing_3', 'new_many_other_thing_1']
447
+
448
+ model.restore_revision!(1)
449
+ RevisionableTestModel.count.should == 1
450
+ RevisionableTestManyThing.count.should == 2
451
+ RevisionableTestSubThing.count.should == 3
452
+ RevisionableTestManyOtherThing.count.should == 2
453
+ ActsAsRevisionable::RevisionRecord.count.should == 2
454
+ restored_model = RevisionableTestModel.find(model.id)
455
+ restored_model.name.should == 'test'
456
+ restored.many_things.collect{|t| t.name}.sort.should == ['many_thing_1', 'many_thing_2']
457
+ restored.many_things.detect{|t| t.name == 'many_thing_1'}.sub_things.collect{|t| t.name}.sort.should == ['sub_thing_1', 'sub_thing_2']
458
+ restored.many_things.detect{|t| t.name == 'many_thing_2'}.sub_things.collect{|t| t.name}.sort.should == []
459
+ restored.many_other_things.collect{|t| t.name}.sort.should == ['many_other_thing_3', 'new_many_other_thing_1']
460
+ end
461
+
462
+ it "should restore a record with has_one associations" do
463
+ model = RevisionableTestModel.new(:name => 'test')
464
+ model.build_one_thing(:name => 'other')
465
+ model.store_revision do
466
+ model.save!
467
+ end
468
+ model.reload
469
+ ActsAsRevisionable::RevisionRecord.count.should == 0
470
+ RevisionableTestOneThing.count.should == 1
128
471
 
129
- it "should create a revision entry when a model is updated if :on_update is true" do
130
- record = TestRevisionableModel.new
131
- record.should_receive(:new_record?).and_return(false)
132
- record.should_receive(:errors).and_return([])
133
- record.should_receive(:really_update).and_return(:retval)
134
- record.send(:update).should == :retval
135
- end
472
+ model.name = 'new_name'
473
+ model.one_thing.name = 'new_other'
474
+ model.store_revision do
475
+ model.one_thing.save!
476
+ model.save!
477
+ end
136
478
 
137
- it "should not create a revision entry if revisioning is disabled" do
138
- record = TestRevisionableModel.new
139
- record.stub!(:new_record?).and_return(nil)
140
- TestRevisionableModel.should_not_receive(:find)
141
- ActsAsRevisionable::RevisionRecord.should_not_receive(:transaction)
142
- record.should_not_receive(:create_revision!)
143
- record.should_not_receive(:truncate_revisions!)
144
- record.should_receive(:update)
145
-
146
- record.disable_revisioning do
147
- record.store_revision do
148
- record.send(:update)
479
+ model.reload
480
+ ActsAsRevisionable::RevisionRecord.count.should == 1
481
+ model.name.should == 'new_name'
482
+ model.one_thing.name.should == 'new_other'
483
+
484
+ # restore to memory
485
+ restored = model.restore_revision(1)
486
+ restored.name.should == 'test'
487
+ restored.one_thing.name.should == 'other'
488
+ restored.one_thing.id.should == model.one_thing.id
489
+
490
+ # make sure restore to memory didn't affect the database
491
+ model.reload
492
+ ActsAsRevisionable::RevisionRecord.count.should == 1
493
+ model.name.should == 'new_name'
494
+ model.one_thing(true).name.should == 'new_other'
495
+
496
+ model.restore_revision!(1)
497
+ RevisionableTestModel.count.should == 1
498
+ RevisionableTestOneThing.count.should == 1
499
+ ActsAsRevisionable::RevisionRecord.count.should == 2
500
+ restored_model = RevisionableTestModel.find(model.id)
501
+ restored_model.name.should == 'test'
502
+ restored_model.one_thing.name.should == 'other'
503
+ restored_model.one_thing.id.should == model.one_thing.id
504
+ end
505
+
506
+ it "should restore a record with has_and_belongs_to_many associations" do
507
+ other_1 = NonRevisionableTestModel.create(:name => 'one')
508
+ other_2 = NonRevisionableTestModel.create(:name => 'two')
509
+ model = RevisionableTestModel.new(:name => 'test')
510
+ model.non_revisionable_test_models = [other_1, other_2]
511
+ model.store_revision do
512
+ model.save!
513
+ end
514
+ model.reload
515
+ ActsAsRevisionable::RevisionRecord.count.should == 0
516
+ NonRevisionableTestModel.count.should == 2
517
+
518
+ model.name = 'new_name'
519
+ other_1.name = '111'
520
+ other_3 = NonRevisionableTestModel.create(:name => '333')
521
+ model.store_revision do
522
+ model.non_revisionable_test_models = [other_1, other_3]
523
+ other_1.save!
524
+ model.save!
525
+ end
526
+
527
+ model.reload
528
+ ActsAsRevisionable::RevisionRecord.count.should == 1
529
+ NonRevisionableTestModel.count.should == 3
530
+ model.name.should == 'new_name'
531
+ model.non_revisionable_test_models.collect{|r| r.name}.sort.should == ['111', '333']
532
+
533
+ # restore to memory
534
+ restored = model.restore_revision(1)
535
+ restored.name.should == 'test'
536
+ restored.non_revisionable_test_models.collect{|r| r.name}.sort.should == ['111', 'two']
537
+
538
+ # make sure the restore to memory didn't affect the database
539
+ model.reload
540
+ model.name.should == 'new_name'
541
+ model.non_revisionable_test_models(true).collect{|r| r.name}.sort.should == ['111', '333']
542
+
543
+ model.restore_revision!(1)
544
+ NonRevisionableTestModelsRevisionableTestModel.count.should == 2
545
+ RevisionableTestModel.count.should == 1
546
+ NonRevisionableTestModel.count.should == 3
547
+ ActsAsRevisionable::RevisionRecord.count.should == 2
548
+ restored_model = RevisionableTestModel.find(model.id)
549
+ restored_model.name.should == 'test'
550
+ restored_model.non_revisionable_test_models.collect{|r| r.name}.sort.should == ['111', 'two']
551
+ end
552
+
553
+ it "should handle namespaces and single table inheritance" do
554
+ model = ActsAsRevisionable::RevisionableNamespaceModel.new(:name => 'test')
555
+ model.store_revision do
556
+ model.save!
557
+ end
558
+ model.reload
559
+ ActsAsRevisionable::RevisionRecord.count.should == 0
560
+
561
+ model.name = 'new_name'
562
+ model.store_revision do
563
+ model.save!
149
564
  end
565
+ model.reload
566
+ ActsAsRevisionable::RevisionRecord.count.should == 1
567
+ model.name.should == 'new_name'
568
+
569
+ restored = model.restore_revision(1)
570
+ restored.class.should == ActsAsRevisionable::RevisionableNamespaceModel
571
+ restored.name.should == 'test'
572
+ restored.id.should == model.id
150
573
  end
151
- end
152
574
 
153
- it "should truncate the revisions" do
154
- record = TestRevisionableModel.new
155
- record.stub!(:id).and_return(1)
156
- ActsAsRevisionable::RevisionRecord.should_receive(:truncate_revisions).with(TestRevisionableModel, 1, {:limit => 20, :minimum_age => 2.weeks})
157
- record.truncate_revisions!(:limit => 20, :minimum_age => 2.weeks)
158
- end
575
+ it "should handle single table inheritance" do
576
+ model = ActsAsRevisionable::RevisionableSubclassModel.new(:name => 'test')
577
+ model.store_revision do
578
+ model.save!
579
+ end
580
+ model.reload
581
+ ActsAsRevisionable::RevisionRecord.count.should == 0
159
582
 
160
- it "should be able to restore a revision by id and revision" do
161
- revision = mock(:revision)
162
- record = mock(:record)
163
- ActsAsRevisionable::RevisionRecord.should_receive(:find_revision).with(TestRevisionableModel, 1, 5).and_return(revision)
164
- revision.should_receive(:restore).and_return(record)
165
- TestRevisionableModel.restore_revision(1, 5).should == record
166
- end
583
+ model.name = 'new_name'
584
+ model.store_revision do
585
+ model.save!
586
+ end
587
+ model.reload
588
+ ActsAsRevisionable::RevisionRecord.count.should == 1
589
+ model.name.should == 'new_name'
590
+
591
+ restored = model.restore_revision(1)
592
+ restored.class.should == ActsAsRevisionable::RevisionableSubclassModel
593
+ restored.name.should == 'test'
594
+ restored.id.should == model.id
595
+ restored.type_name.should == 'RevisionableSubclassModel'
596
+ end
597
+
598
+ it "should handle composite primary keys" do
599
+ thing_1 = RevisionableTestCompositeKeyThing.new(:name => 'thing_1')
600
+ thing_1.other_id = 1
601
+ thing_2 = RevisionableTestCompositeKeyThing.new(:name => 'thing_2')
602
+ thing_2.other_id = 2
603
+ thing_3 = RevisionableTestCompositeKeyThing.new(:name => 'thing_3')
604
+ thing_3.other_id = 3
605
+
606
+ model = RevisionableTestModel.new(:name => 'test')
607
+ model.composite_key_things << thing_1
608
+ model.composite_key_things << thing_2
609
+ model.save!
610
+ model.reload
611
+ RevisionableTestCompositeKeyThing.count.should == 2
612
+ ActsAsRevisionable::RevisionRecord.count.should == 0
613
+
614
+ model.store_revision do
615
+ thing_1 = model.composite_key_things.detect{|t| t.name == 'thing_1'}
616
+ thing_1.name = 'new_thing_1'
617
+ thing_2 = model.composite_key_things.detect{|t| t.name == 'thing_2'}
618
+ model.composite_key_things.delete(thing_2)
619
+ model.composite_key_things << thing_3
620
+ model.save!
621
+ thing_1.save!
622
+ end
623
+
624
+ model.reload
625
+ ActsAsRevisionable::RevisionRecord.count.should == 1
626
+ RevisionableTestCompositeKeyThing.count.should == 2
627
+ model.composite_key_things.collect{|t| t.name}.sort.should == ['new_thing_1', 'thing_3']
628
+
629
+ # restore to memory
630
+ restored = model.restore_revision(1)
631
+ restored.composite_key_things.collect{|t| t.name}.sort.should == ['thing_1', 'thing_2']
632
+ restored.valid?.should == true
633
+
634
+ # make sure the restore to memory didn't affect the database
635
+ model.reload
636
+ model.composite_key_things(true).collect{|t| t.name}.sort.should == ['new_thing_1', 'thing_3']
637
+ RevisionableTestCompositeKeyThing.count.should == 2
167
638
 
168
- it "should be able to restore a revision by id and revision and save it" do
169
- record = mock(:record)
170
- TestRevisionableModel.should_receive(:restore_revision).with(1, 5).and_return(record)
171
- record.should_receive(:store_revision).and_yield
172
- TestRevisionableModel.should_receive(:save_restorable_associations).with(record, TestRevisionableModel.revisionable_associations)
173
- TestRevisionableModel.restore_revision!(1, 5).should == record
639
+ model.restore_revision!(1)
640
+ RevisionableTestModel.count.should == 1
641
+ RevisionableTestCompositeKeyThing.count.should == 3
642
+ restored_model = RevisionableTestModel.find(model.id)
643
+ restored_model.name.should == 'test'
644
+ restored.composite_key_things.collect{|t| t.name}.sort.should == ['thing_1', 'thing_2']
645
+ end
646
+
647
+ it "should restore a deleted record" do
648
+ model = ActsAsRevisionable::RevisionableNamespaceModel.new(:name => 'test')
649
+ model.save!
650
+ model.store_revision do
651
+ model.name = "new name"
652
+ model.save!
653
+ end
654
+ model.destroy
655
+ ActsAsRevisionable::RevisionRecord.count.should == 2
656
+ ActsAsRevisionable::RevisionableNamespaceModel.restore_last_revision!(model.id)
657
+ end
174
658
  end
175
659
 
660
+ context "cleaning up revisions" do
661
+ it "should destroy revisions if :dependent => :keep was not specified" do
662
+ model = OtherRevisionableTestModel.create(:name => 'test')
663
+ ActsAsRevisionable::RevisionRecord.count.should == 0
664
+
665
+ model.name = 'new_name'
666
+ model.store_revision do
667
+ model.save!
668
+ end
669
+ model.reload
670
+ ActsAsRevisionable::RevisionRecord.count.should == 1
671
+ model.name.should == 'new_name'
672
+
673
+ model.destroy
674
+ ActsAsRevisionable::RevisionRecord.count.should == 0
675
+ end
676
+
677
+ it "should not destroy revisions if :dependent => :keep was specified" do
678
+ model = ActsAsRevisionable::RevisionableSubclassModel.new(:name => 'test')
679
+ model.store_revision do
680
+ model.save!
681
+ end
682
+ model.reload
683
+ ActsAsRevisionable::RevisionRecord.count.should == 0
684
+
685
+ model.name = 'new_name'
686
+ model.store_revision do
687
+ model.save!
688
+ end
689
+ model.reload
690
+ ActsAsRevisionable::RevisionRecord.count.should == 1
691
+ model.name.should == 'new_name'
692
+
693
+ # Destroy adds a revision in this model
694
+ model.destroy
695
+ ActsAsRevisionable::RevisionRecord.count.should == 2
696
+ end
697
+
698
+ it "should empty the trash by deleting all revisions for records that have been deleted for a specified period" do
699
+ record_1 = ActsAsRevisionable::RevisionableNamespaceModel.create(:name => 'test')
700
+ record_1.store_revision do
701
+ record_1.update_attribute(:name, "new")
702
+ end
703
+ record_1.store_revision do
704
+ record_1.update_attribute(:name, "newer")
705
+ end
706
+ record_1.store_revision do
707
+ record_1.destroy
708
+ end
709
+ record_2 = ActsAsRevisionable::RevisionableNamespaceModel.create(:name => 'test 2')
710
+ record_2.store_revision do
711
+ record_2.update_attribute(:name, "new 2")
712
+ end
713
+
714
+ now = Time.now
715
+ ActsAsRevisionable::RevisionRecord.count.should == 4
716
+ ActsAsRevisionable::RevisionableNamespaceModel.empty_trash(60)
717
+ ActsAsRevisionable::RevisionRecord.count.should == 4
718
+ Time.stub(:now => now + 61)
719
+ ActsAsRevisionable::RevisionableNamespaceModel.empty_trash(60)
720
+ ActsAsRevisionable::RevisionRecord.count.should == 1
721
+ end
722
+ end
176
723
  end