acts_as_audited_collection 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.md +144 -0
- data/Rakefile +45 -0
- data/generators/audited_collection_migration/USAGE +10 -0
- data/generators/audited_collection_migration/audited_collection_migration_generator.rb +9 -0
- data/generators/audited_collection_migration/templates/migration.rb +21 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/acts_as_audited_collection.rb +230 -0
- data/lib/acts_as_audited_collection/collection_audit.rb +10 -0
- data/lib/tasks/acts_as_audited_collection_tasks.rake +4 -0
- data/rails/init.rb +5 -0
- data/spec/acts_as_audited_collection_spec.rb +483 -0
- data/spec/db/acts_as_audited_collection.sqlite3.db +0 -0
- data/spec/db/database.yml +3 -0
- data/spec/db/schema.rb +47 -0
- data/spec/debug.log +56964 -0
- data/spec/models.rb +69 -0
- data/spec/spec_helper.rb +19 -0
- data/uninstall.rb +1 -0
- metadata +82 -0
@@ -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
|
data/rails/init.rb
ADDED
@@ -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
|