acts_as_audited_collection 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ # Released under the MIT license. See the LICENSE file for details
2
+
3
+ class CollectionAudit < ActiveRecord::Base
4
+ belongs_to :parent_record, :polymorphic => true
5
+ belongs_to :child_record, :polymorphic => true
6
+ belongs_to :user, :polymorphic => true
7
+
8
+ belongs_to :child_audit, :class_name => 'CollectionAudit'
9
+ has_many :parent_audits, :class_name => 'CollectionAudit', :foreign_key => :child_audit_id
10
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :acts_as_audited_collection do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,5 @@
1
+ # Released under the MIT license. See the LICENSE file for details
2
+
3
+ require 'acts_as_audited_collection'
4
+
5
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::AuditedCollection
@@ -0,0 +1,483 @@
1
+ # Released under the MIT license. See the LICENSE file for details
2
+
3
+ require File.dirname(__FILE__) + '/spec_helper'
4
+
5
+ describe 'Acts as audited collection plugin' do
6
+ it 'can be included in an ActiveRecord model' do
7
+ class A < ActiveRecord::Base
8
+ self
9
+ end.should respond_to :acts_as_audited_collection
10
+ end
11
+
12
+ it 'requires a parent to be specified' do
13
+ lambda {
14
+ class B < ActiveRecord::Base
15
+ acts_as_audited_collection
16
+ end
17
+ }.should raise_error ActiveRecord::ConfigurationError
18
+ end
19
+
20
+ it 'infers the collection name correctly from the class' do
21
+ class Person < ActiveRecord::Base
22
+ belongs_to :test_parent
23
+
24
+ acts_as_audited_collection :parent => :test_parent
25
+
26
+ audited_collections
27
+ end.should have_key :people
28
+ end
29
+
30
+ it 'allows the audited collection through a belongs_to relationship' do
31
+ class Person < ActiveRecord::Base
32
+ belongs_to :test_parent
33
+
34
+ acts_as_audited_collection :parent => :test_parent
35
+ end
36
+ end
37
+
38
+ it 'refuses the audited collection through a has_one relationship' do
39
+ lambda {
40
+ class Person < ActiveRecord::Base
41
+ has_one :test
42
+
43
+ acts_as_audited_collection :parent => :test
44
+ end
45
+ }.should raise_error ActiveRecord::ConfigurationError
46
+ end
47
+
48
+ it 'refuses an audited collection parent where no child relation is specified' do
49
+ lambda {
50
+ class Person < ActiveRecord::Base
51
+ acts_as_audited_collection_parent
52
+ end
53
+ }.should raise_error ActiveRecord::ConfigurationError
54
+ end
55
+
56
+ it 'refuses an audited collection parent through a belongs_to relationship' do
57
+ lambda {
58
+ class Person < ActiveRecord::Base
59
+ belongs_to :test
60
+
61
+ acts_as_audited_collection_parent :for => :test
62
+ end
63
+ }.should raise_error ActiveRecord::ConfigurationError
64
+ end
65
+
66
+ it 'allows an audited collection parent through a has_many relationship' do
67
+ class Person < ActiveRecord::Base
68
+ has_many :tests
69
+
70
+ acts_as_audited_collection_parent :for => :tests
71
+ end
72
+ end
73
+
74
+ it 'configures an audited collection for cascading when required' do
75
+ class Person < ActiveRecord::Base
76
+ belongs_to :test_parent
77
+
78
+ acts_as_audited_collection :parent => :test_parent, :cascade => true
79
+ end
80
+
81
+ Person.audited_collections.should have_key :people
82
+ Person.audited_collections[:people].should have_key :cascade
83
+ Person.audited_collections[:people][:cascade].should be_true
84
+ end
85
+
86
+ it 'configures an audited collection to track modificiations when required' do
87
+ class Person < ActiveRecord::Base
88
+ belongs_to :test_parent
89
+
90
+ acts_as_audited_collection :parent => :test_parent, :track_modifications => true
91
+ end
92
+
93
+ Person.audited_collections.should have_key :people
94
+ Person.audited_collections[:people].should have_key :track_modifications
95
+ Person.audited_collections[:people][:track_modifications].should be_true
96
+ end
97
+
98
+ it 'audits an object creation when relationships are defined' do
99
+ p = TestParent.create :name => 'test parent'
100
+ c = nil
101
+ lambda {
102
+ c = p.test_children.create :name => 'test child'
103
+ }.should change(CollectionAudit, :count).by(1)
104
+
105
+ CollectionAudit.last.child_record.should == c
106
+ CollectionAudit.last.parent_record.should == p
107
+ CollectionAudit.last.action.should == 'add'
108
+ end
109
+
110
+ it 'skips auditing on object creation when no relationships are defined' do
111
+ p = TestParent.create :name => 'test parent'
112
+ lambda {
113
+ c = TestChild.create :name => 'test child'
114
+ }.should_not change(CollectionAudit, :count)
115
+ end
116
+
117
+ it 'audits an object modification when a relation is created' do
118
+ c = TestChild.create :name => 'test child'
119
+ p = TestParent.create :name => 'test parent'
120
+
121
+ lambda {
122
+ c.test_parent = p
123
+ c.save!
124
+ }.should change(CollectionAudit, :count).by(1)
125
+
126
+ CollectionAudit.last.child_record.should == c
127
+ CollectionAudit.last.parent_record.should == p
128
+ CollectionAudit.last.action.should == 'add'
129
+ end
130
+
131
+ it 'skips auditing on object modification when no relationships are altered' do
132
+ c = TestChild.create :name => 'test child'
133
+ p = TestParent.create :name => 'test parent'
134
+
135
+ lambda {
136
+ c.name = 'new name'
137
+ c.save!
138
+ }.should_not change(CollectionAudit, :count)
139
+ end
140
+
141
+ it 'audits an object modification when a relationship is removed' do
142
+ p = TestParent.create :name => 'test parent'
143
+ c = p.test_children.create :name => 'test child'
144
+
145
+ lambda {
146
+ c.test_parent = nil
147
+ c.save!
148
+ }.should change(CollectionAudit, :count).by(1)
149
+
150
+ CollectionAudit.last.child_record.should == c
151
+ CollectionAudit.last.parent_record.should == p
152
+ CollectionAudit.last.action.should == 'remove'
153
+ end
154
+
155
+ it 'audits an object deletion when a relationship exists' do
156
+ p = TestParent.create :name => 'test parent'
157
+ c = p.test_children.create :name => 'test child'
158
+
159
+ lambda {
160
+ c.destroy.should be_true
161
+ }.should change(CollectionAudit, :count).by(1)
162
+
163
+ # child_record will be nil, because the record has been deleted.
164
+ CollectionAudit.last.child_record_id.should == c.id
165
+ CollectionAudit.last.parent_record.should == p
166
+ CollectionAudit.last.action.should == 'remove'
167
+ end
168
+
169
+ it 'skips auditing an object deletion when no relationships exist' do
170
+ c = TestChild.create :name => 'test child'
171
+
172
+ lambda {
173
+ c.destroy.should be_true
174
+ }.should_not change(CollectionAudit, :count)
175
+ end
176
+
177
+ it 'saves the collection name with the audit entry' do
178
+ p = TestParent.create :name => 'test parent'
179
+ c = p.test_children.create :name => 'test child'
180
+ CollectionAudit.last.association.should == 'test_children'
181
+ end
182
+
183
+ it 'makes the collection history available through the parent class' do
184
+ p = TestParent.create :name => 'test parent'
185
+ c = p.test_children.create :name => 'test child'
186
+
187
+ p.test_children_audits.should include CollectionAudit.last
188
+ end
189
+
190
+ it 'correctly audits a secondary collection' do
191
+ p = TestParent.create :name => 'test parent'
192
+ c = nil
193
+ lambda {
194
+ c = p.other_test_children.create :name => 'test child'
195
+ }.should change(CollectionAudit, :count).by(1)
196
+
197
+ # Basic sanity checking, to make sure the model stays valid
198
+ p.other_test_children.should include c
199
+ p.test_children.should be_empty
200
+ c.other_test_parent.should == p
201
+ c.test_parent.should be_nil
202
+
203
+ p.test_children_audits.should be_empty
204
+ p.other_test_children_audits.length.should == 1
205
+
206
+ p.other_test_children_audits.last.child_record.should == c
207
+ p.other_test_children_audits.last.parent_record.should == p
208
+ p.other_test_children_audits.last.action.should == 'add'
209
+ p.other_test_children_audits.last.association.should == 'other_test_children'
210
+ end
211
+
212
+ it 'correctly audits when a parent is reassociated through a secondary collection' do
213
+ p = TestParent.create :name => 'test parent'
214
+ c = p.test_children.create :name => 'test child'
215
+ lambda {
216
+ c.test_parent = nil
217
+ c.other_test_parent = p
218
+ c.save!
219
+ }.should change(CollectionAudit, :count).by(2)
220
+
221
+ # One from the initial creation
222
+ p.test_children_audits.length.should == 2
223
+ p.test_children_audits.should be_all { |a| a.child_record == c }
224
+ p.test_children_audits.first.action.should == 'add'
225
+ p.test_children_audits.last.action.should == 'remove'
226
+ p.other_test_children_audits.length.should == 1
227
+ p.other_test_children_audits.should be_all { |a| a.child_record == c && a.action == 'add' }
228
+ end
229
+
230
+ it 'correctly audits when a child is assigned to a new parent' do
231
+ p1 = TestParent.create :name => 'test parent'
232
+ c = p1.test_children.create :name => 'test child'
233
+ p2 = TestParent.create :name => 'another parent'
234
+
235
+ lambda {
236
+ c.test_parent = p2
237
+ c.save!
238
+ }.should change(CollectionAudit, :count).by(2)
239
+
240
+ # One from the initial creation
241
+ p1.test_children_audits.length.should == 2
242
+ p1.test_children_audits.should be_all { |a| a.child_record == c }
243
+ p1.test_children_audits.first.action.should == 'add'
244
+ p1.test_children_audits.last.action.should == 'remove'
245
+ p2.test_children_audits.length.should == 1
246
+ p2.test_children_audits.should be_all { |a| a.child_record == c && a.action == 'add' }
247
+ end
248
+
249
+ it 'disables collection auditing for a block passed to the without_collection_audit method' do
250
+ p = TestParent.create :name => 'test parent'
251
+ c = nil
252
+ lambda {
253
+ result = TestChild.without_collection_audit do
254
+ c = p.test_children.create :name => 'test child'
255
+ end
256
+
257
+ # Make sure we get the right return value
258
+ result.should == c
259
+ }.should_not change(CollectionAudit, :count)
260
+ end
261
+
262
+ it 'enables collection auditing after completion of a block passed to the without_collection_audit method' do
263
+ p = TestParent.create :name => 'test parent'
264
+ TestChild.without_collection_audit do
265
+ p.test_children.create :name => 'test child'
266
+ end
267
+ lambda {
268
+ p.test_children.create :name => 'another child'
269
+ }.should change(CollectionAudit, :count).by(1)
270
+ end
271
+
272
+ it 'tracks modifications through an auditing collection with modification tracking enabled' do
273
+ p = TestParent.create :name => 'test parent'
274
+ c = p.other_test_children.create :name => 'test child'
275
+
276
+ lambda {
277
+ c.name = 'new name'
278
+ c.save!
279
+ }.should change(CollectionAudit, :count).by(1)
280
+
281
+ CollectionAudit.last.child_record.should == c
282
+ CollectionAudit.last.parent_record.should == p
283
+ CollectionAudit.last.action.should == 'modify'
284
+ end
285
+
286
+ it 'tracks modifications to attributes listed in :only' do
287
+ p = TestParent.create :name => 'test parent'
288
+ c = p.test_children_with_only.create :name => 'test child'
289
+
290
+ lambda {
291
+ c.name = 'new name'
292
+ c.save!
293
+ }.should change(CollectionAudit, :count).by(1)
294
+
295
+ CollectionAudit.last.child_record.should == c
296
+ CollectionAudit.last.parent_record.should == p
297
+ CollectionAudit.last.action.should == 'modify'
298
+ end
299
+
300
+ it 'ignores modifications to attributes not listed in :only' do
301
+ p = TestParent.create :name => 'test parent'
302
+ c = p.test_children_with_only.create :name => 'test child'
303
+
304
+ lambda {
305
+ c.description = 'new description'
306
+ c.save!
307
+ }.should_not change(CollectionAudit, :count)
308
+ end
309
+
310
+ it 'ignores modifications to attributes listed in :except' do
311
+ p = TestParent.create :name => 'test parent'
312
+ c = p.test_children_with_except.create :name => 'test child'
313
+
314
+ lambda {
315
+ c.name = 'new name'
316
+ c.save!
317
+ }.should_not change(CollectionAudit, :count)
318
+ end
319
+
320
+ it 'tracks modifications to attributes not listed in :except' do
321
+ p = TestParent.create :name => 'test parent'
322
+ c = p.test_children_with_except.create :name => 'test child'
323
+
324
+ lambda {
325
+ c.description = 'new name'
326
+ c.save!
327
+ }.should change(CollectionAudit, :count).by(1)
328
+
329
+ CollectionAudit.last.child_record.should == c
330
+ CollectionAudit.last.parent_record.should == p
331
+ CollectionAudit.last.action.should == 'modify'
332
+ end
333
+
334
+ it 'tracks grandchild modifications through a cascading auditing collection' do
335
+ p = TestParent.create :name => 'test parent'
336
+ # other_test_children has track_modifications enabled
337
+ c = p.other_test_children.create :name => 'test child'
338
+ g = nil
339
+ lambda {
340
+ g = c.test_grandchildren.create :name => 'test grandchild'
341
+ }.should change(CollectionAudit, :count).by(2)
342
+
343
+ audits = CollectionAudit.find :all, :order => 'id desc', :limit => 2
344
+ # First the grandchild would have been logged ..
345
+ audits[1].child_record.should == g
346
+ audits[1].parent_record.should == c
347
+ audits[1].action.should == 'add'
348
+ # .. then the child would have been logged.
349
+ audits[0].child_record.should == c
350
+ audits[0].parent_record.should == p
351
+ audits[0].action.should == 'modify'
352
+ end
353
+
354
+ it 'tracks a grandchild being reassociated with a different child' do
355
+ p = TestParent.create :name => 'test parent'
356
+ # other_test_children has track_modifications enabled
357
+ c1 = p.other_test_children.create :name => 'test child'
358
+ c2 = p.other_test_children.create :name => 'another child'
359
+ g = c1.test_grandchildren.create :name => 'test grandchild'
360
+
361
+ lambda {
362
+ g.update_attributes :test_child => c2
363
+ }.should change(CollectionAudit, :count).by(4)
364
+
365
+ audits = CollectionAudit.find :all, :order => 'id desc', :limit => 4
366
+
367
+ # First the grandchild removal would have been logged ..
368
+ audits[3].child_record.should == g
369
+ audits[3].parent_record.should == c1
370
+ audits[3].action.should == 'remove'
371
+ # .. then the child 1 modification would have been logged ..
372
+ audits[2].child_record.should == c1
373
+ audits[2].parent_record.should == p
374
+ audits[2].action.should == 'modify'
375
+ # .. then the grandchild addition would have been logged ..
376
+ audits[1].child_record.should == g
377
+ audits[1].parent_record.should == c2
378
+ audits[1].action.should == 'add'
379
+ # .. then the child 2 modification would have been logged.
380
+ audits[0].child_record.should == c2
381
+ audits[0].parent_record.should == p
382
+ audits[0].action.should == 'modify'
383
+ end
384
+
385
+ it 'tracks great-grandchild modifications through a cascading auditing collection' do
386
+ p = TestParent.create :name => 'test parent'
387
+ c = p.other_test_children.create :name => 'test child'
388
+ g = c.test_grandchildren.create :name => 'test grandchild'
389
+ gg = nil
390
+
391
+ lambda {
392
+ gg = g.test_great_grandchildren.create :name => 'test great-grandchild'
393
+ }.should change(CollectionAudit, :count).by(3)
394
+
395
+ audits = CollectionAudit.find :all, :order => 'id desc', :limit => 3
396
+ # First the great-grandchild would have been logged ..
397
+ audits[2].child_record.should == gg
398
+ audits[2].parent_record.should == g
399
+ audits[2].action.should == 'add'
400
+ # .. then the grandchild would have been logged ..
401
+ audits[1].child_record.should == g
402
+ audits[1].parent_record.should == c
403
+ audits[1].action.should == 'modify'
404
+ # .. then the child would have been logged.
405
+ audits[0].child_record.should == c
406
+ audits[0].parent_record.should == p
407
+ audits[0].action.should == 'modify'
408
+ end
409
+
410
+ it 'tracks the child audit of a cascading audit' do
411
+ p = TestParent.create :name => 'test parent'
412
+ # other_test_children has track_modifications enabled
413
+ c = p.other_test_children.create :name => 'test child'
414
+ g = c.test_grandchildren.create :name => 'test grandchild'
415
+
416
+ pa = p.other_test_children_audits.last
417
+ ca = c.test_grandchildren_audits.last
418
+ pa.child_audit.should == ca
419
+ ca.parent_audits.should == [pa]
420
+ end
421
+
422
+ it 'reports a soft delete as if it were a delete' do
423
+ p = TestChild.create :name => 'test parent'
424
+ c = p.test_soft_delete_grandchildren.create :name => 'test child'
425
+ lambda {
426
+ c.update_attributes! :deleted => true
427
+ }.should change(CollectionAudit, :count).by(1)
428
+
429
+ CollectionAudit.last.action.should == 'remove'
430
+ CollectionAudit.last.parent_record.should == p
431
+ CollectionAudit.last.child_record.should == c
432
+ end
433
+
434
+ it 'ignores an object which is soft deleted when created' do
435
+ p = TestChild.create :name => 'test parent'
436
+ lambda {
437
+ c = p.test_soft_delete_grandchildren.create :name => 'test child',
438
+ :deleted => true
439
+ }.should_not change(CollectionAudit, :count)
440
+ end
441
+
442
+ it 'ignores a changed object which is soft deleted' do
443
+ p = TestChild.create :name => 'test parent'
444
+ c = p.test_soft_delete_grandchildren.create :name => 'test child',
445
+ :deleted => true
446
+ lambda {
447
+ c.update_attributes! :name => 'test child with new name'
448
+ }.should_not change(CollectionAudit, :count)
449
+ end
450
+
451
+ it 'reports a soft undelete as if it were an add' do
452
+ p = TestChild.create :name => 'test parent'
453
+ c = p.test_soft_delete_grandchildren.create :name => 'test child',
454
+ :deleted => true
455
+ lambda {
456
+ c.update_attributes! :deleted => false
457
+ }.should change(CollectionAudit, :count).by(1)
458
+
459
+ CollectionAudit.last.action.should == 'add'
460
+ CollectionAudit.last.parent_record.should == p
461
+ CollectionAudit.last.child_record.should == c
462
+ end
463
+
464
+ it 'cascades a soft delete upward as a normal delete' do
465
+ p = TestParent.create :name => 'test parent'
466
+ c = p.other_test_children.create :name => 'test child'
467
+ g = c.test_soft_delete_grandchildren.create :name => 'test grandchild'
468
+ lambda {
469
+ g.update_attributes! :deleted => true
470
+ }.should change(CollectionAudit, :count).by(2)
471
+
472
+ ca = CollectionAudit.last
473
+ ca.action.should == 'modify'
474
+ ca.child_record.should == c
475
+ ca.parent_record.should == p
476
+
477
+ ca = ca.child_audit
478
+ ca.should_not be_nil
479
+ ca.action.should == 'remove'
480
+ ca.child_record.should == g
481
+ ca.parent_record.should == c
482
+ end
483
+ end