acts_as_audited_collection 0.3

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