acts_as_audited_collection 0.4.1 → 1.0.1
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.
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -16
- data/README.md +23 -4
- data/Rakefile +3 -30
- data/acts_as_audited_collection.gemspec +23 -0
- data/lib/active_record/acts/audited_collection.rb +3 -2
- data/lib/acts_as_audited_collection.rb +1 -0
- data/lib/acts_as_audited_collection/collection_audit.rb +18 -0
- data/lib/acts_as_audited_collection/version.rb +3 -0
- data/{generators → lib/generators}/audited_collection_migration/USAGE +0 -0
- data/lib/generators/audited_collection_migration/audited_collection_migration_generator.rb +9 -0
- data/{generators → lib/generators}/audited_collection_migration/templates/migration.rb +0 -0
- data/lib/generators/audited_collection_upgrade/USAGE +10 -0
- data/lib/generators/audited_collection_upgrade/audited_collection_upgrade_generator.rb +20 -0
- data/lib/generators/audited_collection_upgrade/templates/migration.rb +27 -0
- data/spec/acts_as_audited_collection_spec.rb +80 -7
- data/spec/db/database.yml +5 -1
- data/spec/db/schema.rb +7 -0
- data/spec/models.rb +9 -1
- data/spec/spec_helper.rb +5 -6
- metadata +98 -17
- data/generators/audited_collection_migration/audited_collection_migration_generator.rb +0 -9
- data/spec/debug.log +0 -3591
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
CHANGED
@@ -1,19 +1,22 @@
|
|
1
|
-
Copyright (c) 2010 Shaun Mangelsdorf
|
1
|
+
Copyright (c) 2010-2012 Shaun Mangelsdorf
|
2
2
|
|
3
|
-
|
4
|
-
of this software and associated documentation files (the "Software"), to deal
|
5
|
-
in the Software without restriction, including without limitation the rights
|
6
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
copies of the Software, and to permit persons to whom the Software is
|
8
|
-
furnished to do so, subject to the following conditions:
|
3
|
+
MIT License
|
9
4
|
|
10
|
-
|
11
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
THE
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
acts\_as\_audited\_collection
|
2
2
|
==========================
|
3
3
|
|
4
|
-
|
4
|
+
acts\_as\_audited\_collection is a Rails plugin, which extends ActiveRecord to allow auditing of associations.
|
5
5
|
|
6
6
|
The basic feature set is:
|
7
7
|
|
@@ -19,7 +19,17 @@ This plugin is released under the MIT license, and was contributed to the Rails
|
|
19
19
|
Installation
|
20
20
|
============
|
21
21
|
|
22
|
-
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'acts_as_audited_collection'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install acts_as_audited_collection
|
23
33
|
|
24
34
|
Generating the migration
|
25
35
|
------------------------
|
@@ -94,7 +104,7 @@ Consider the following alternative "Person" model.
|
|
94
104
|
:track_modifications => true
|
95
105
|
end
|
96
106
|
|
97
|
-
With this, we can now see modifications from the parent (though we make no attempt to ascertain what the modifications were - if you need this, see [acts_as_audited](http://github.com/collectiveidea/
|
107
|
+
With this, we can now see modifications from the parent (though we make no attempt to ascertain what the modifications were - if you need this, see [acts_as_audited](http://github.com/collectiveidea/audited))
|
98
108
|
|
99
109
|
p = Person.first # Person name: 'Fred'
|
100
110
|
p.update_attributes :name => 'Freda' # Audit record is created
|
@@ -142,3 +152,12 @@ Temporarily disabling auditing
|
|
142
152
|
end
|
143
153
|
|
144
154
|
Keep in mind that this disables collection auditing completely in the current thread, not just for the `Person` model.
|
155
|
+
|
156
|
+
Contributing
|
157
|
+
============
|
158
|
+
|
159
|
+
1. Fork it
|
160
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
161
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
162
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
163
|
+
5. Create new Pull Request
|
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
1
4
|
require 'rake'
|
2
5
|
require 'rake/testtask'
|
3
|
-
require 'rdoc/task'
|
4
6
|
require 'rspec/core/rake_task'
|
5
7
|
|
6
8
|
desc 'Default: run unit tests.'
|
@@ -14,36 +16,7 @@ Rake::TestTask.new(:test) do |t|
|
|
14
16
|
t.verbose = true
|
15
17
|
end
|
16
18
|
|
17
|
-
desc 'Generate documentation for the acts_as_audited_collection plugin.'
|
18
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
19
|
-
rdoc.rdoc_dir = 'rdoc'
|
20
|
-
rdoc.title = 'ActsAsAuditedCollection'
|
21
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
-
rdoc.rdoc_files.include('README')
|
23
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
-
end
|
25
|
-
|
26
19
|
RSpec::Core::RakeTask.new do |t|
|
27
20
|
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
28
21
|
t.pattern = 'spec/*_spec.rb'
|
29
22
|
end
|
30
|
-
|
31
|
-
begin
|
32
|
-
require 'jeweler'
|
33
|
-
Jeweler::Tasks.new do |gem|
|
34
|
-
gem.name = 'acts_as_audited_collection'
|
35
|
-
gem.summary = 'Extends ActiveRecord to allow auditing of associations'
|
36
|
-
gem.description = 'Adds auditing capabilities to ActiveRecord associations, in a similar fashion to acts_as_audited.'
|
37
|
-
gem.files = Dir[
|
38
|
-
'[a-zA-Z]*',
|
39
|
-
'generators/**/*',
|
40
|
-
'lib/**/*',
|
41
|
-
'rails/**/*',
|
42
|
-
'spec/**/*'
|
43
|
-
]
|
44
|
-
gem.authors = ['Shaun Mangelsdorf']
|
45
|
-
gem.version = '0.4.1'
|
46
|
-
end
|
47
|
-
rescue LoadError
|
48
|
-
puts "Jeweler could not be sourced"
|
49
|
-
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/acts_as_audited_collection/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Shaun Mangelsdorf"]
|
6
|
+
gem.email = ["s.mangelsdorf@gmail.com"]
|
7
|
+
gem.description = %q{Adds auditing capabilities to ActiveRecord associations, in a similar fashion to acts_as_audited.}
|
8
|
+
gem.summary = %q{Extends ActiveRecord to allow auditing of associations}
|
9
|
+
gem.homepage = "https://github.com/smangelsdorf/acts_as_audited_collection"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "acts_as_audited_collection"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = ActsAsAuditedCollection::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'ruby-debug'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'rspec'
|
21
|
+
gem.add_development_dependency 'rails', '~>3.0'
|
22
|
+
gem.add_development_dependency 'sqlite3'
|
23
|
+
end
|
@@ -72,7 +72,8 @@ module ActiveRecord
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def define_acts_as_audited_collection(options)
|
75
|
-
|
75
|
+
key = "#{options[:parent_type]}##{options[:name]}"
|
76
|
+
yield(read_inheritable_attribute(:audited_collections)[key] ||= {})
|
76
77
|
end
|
77
78
|
|
78
79
|
def without_collection_audit
|
@@ -93,7 +94,7 @@ module ActiveRecord
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def collection_audit_update
|
96
|
-
audited_collections.each do |
|
97
|
+
audited_collections.each do |key, opts|
|
97
98
|
attributes = {opts[:foreign_key] => self.send(opts[:foreign_key])}
|
98
99
|
if collection_audit_is_soft_deleted?(opts)
|
99
100
|
collection_audit_write(
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# Released under the MIT license. See the LICENSE file for details
|
2
|
+
require 'active_record'
|
2
3
|
|
3
4
|
class CollectionAudit < ActiveRecord::Base
|
4
5
|
belongs_to :parent_record, :polymorphic => true
|
@@ -7,4 +8,21 @@ class CollectionAudit < ActiveRecord::Base
|
|
7
8
|
|
8
9
|
belongs_to :child_audit, :class_name => 'CollectionAudit'
|
9
10
|
has_many :parent_audits, :class_name => 'CollectionAudit', :foreign_key => :child_audit_id
|
11
|
+
|
12
|
+
before_create :set_as_current
|
13
|
+
|
14
|
+
scope :for_child, lambda{|c| where(:child_record_type => c.class.name, :child_record_id => c.id)}
|
15
|
+
|
16
|
+
private
|
17
|
+
def set_as_current
|
18
|
+
self.class.where(
|
19
|
+
:parent_record_type => parent_record_type,
|
20
|
+
:parent_record_id => parent_record_id,
|
21
|
+
:child_record_type => child_record_type,
|
22
|
+
:child_record_id => child_record_id,
|
23
|
+
:association => association,
|
24
|
+
:current => true
|
25
|
+
).update_all :current => false
|
26
|
+
self.current = true
|
27
|
+
end
|
10
28
|
end
|
File without changes
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Released under the MIT license. See the LICENSE file for details
|
2
|
+
|
3
|
+
class AuditedCollectionMigrationGenerator < Rails::Generators::NamedBase
|
4
|
+
source_root File.join(File.dirname(__FILE__), 'templates')
|
5
|
+
|
6
|
+
def manifest
|
7
|
+
migration_template 'migration.rb', "db/migrate/#{file_name}"
|
8
|
+
end
|
9
|
+
end
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Description:
|
2
|
+
Generates the migration to create a collection_audits table.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
./script/generate audited_collection_migration add_collection_audits_table
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
db/migrate/*_add_collection_audits_table.rb
|
9
|
+
|
10
|
+
Run "rake db:migrate" to update your database.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Released under the MIT license. See the LICENSE file for details
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/migration'
|
5
|
+
require 'rails/generators/active_record/migration'
|
6
|
+
|
7
|
+
class AuditedCollectionUpgradeGenerator < Rails::Generators::NamedBase
|
8
|
+
include Rails::Generators::Migration
|
9
|
+
extend ActiveRecord::Generators::Migration
|
10
|
+
attr_accessor :from_version
|
11
|
+
|
12
|
+
source_root File.join(File.dirname(__FILE__), 'templates')
|
13
|
+
|
14
|
+
argument :from, :type => :string, :default => '', :banner => 'VERSION'
|
15
|
+
|
16
|
+
def main_screen_turn_on
|
17
|
+
self.from_version = from.split('.').collect(&:to_i)
|
18
|
+
migration_template 'migration.rb', "db/migrate/#{file_name}"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class <%= class_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
<% if (from_version <=> [1,0,0]) < 0 %>
|
4
|
+
add_column :collection_audits, :current, :boolean, :default => true, :nullable => false
|
5
|
+
add_index :collection_audits, :current
|
6
|
+
|
7
|
+
execute %q{
|
8
|
+
update collection_audits ca_old
|
9
|
+
join collection_audits ca_new
|
10
|
+
on ca_old.id < ca_new.id
|
11
|
+
and ca_old.parent_record_type = ca_new.parent_record_type
|
12
|
+
and ca_old.parent_record_id = ca_new.parent_record_id
|
13
|
+
and ca_old.child_record_type = ca_new.child_record_type
|
14
|
+
and ca_old.child_record_id = ca_new.child_record_id
|
15
|
+
and ca_old.association = ca_new.association
|
16
|
+
set ca_old.current = false
|
17
|
+
}
|
18
|
+
<% end %>
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.down
|
22
|
+
<% if (from_version <=> [1,0,0]) < 0 %>
|
23
|
+
remove_index :collection_audits, :current
|
24
|
+
remove_column :collection_audits, :current
|
25
|
+
<% end %>
|
26
|
+
end
|
27
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Released under the MIT license. See the LICENSE file for details
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + '/spec_helper'
|
4
|
+
require 'acts_as_audited_collection'
|
4
5
|
|
5
6
|
describe 'Acts as audited collection plugin' do
|
6
7
|
it 'can be included in an ActiveRecord model' do
|
@@ -24,7 +25,7 @@ describe 'Acts as audited collection plugin' do
|
|
24
25
|
acts_as_audited_collection :parent => :test_parent
|
25
26
|
|
26
27
|
audited_collections
|
27
|
-
end.should have_key
|
28
|
+
end.should have_key "TestParent#people"
|
28
29
|
end
|
29
30
|
|
30
31
|
it 'allows the audited collection through a belongs_to relationship' do
|
@@ -78,9 +79,9 @@ describe 'Acts as audited collection plugin' do
|
|
78
79
|
acts_as_audited_collection :parent => :test_parent, :cascade => true
|
79
80
|
end
|
80
81
|
|
81
|
-
Person.audited_collections.should have_key
|
82
|
-
Person.audited_collections[
|
83
|
-
Person.audited_collections[
|
82
|
+
Person.audited_collections.should have_key 'TestParent#people'
|
83
|
+
Person.audited_collections['TestParent#people'].should have_key :cascade
|
84
|
+
Person.audited_collections['TestParent#people'][:cascade].should be_true
|
84
85
|
end
|
85
86
|
|
86
87
|
it 'configures an audited collection to track modificiations when required' do
|
@@ -90,9 +91,9 @@ describe 'Acts as audited collection plugin' do
|
|
90
91
|
acts_as_audited_collection :parent => :test_parent, :track_modifications => true
|
91
92
|
end
|
92
93
|
|
93
|
-
Person.audited_collections.should have_key
|
94
|
-
Person.audited_collections[
|
95
|
-
Person.audited_collections[
|
94
|
+
Person.audited_collections.should have_key 'TestParent#people'
|
95
|
+
Person.audited_collections['TestParent#people'].should have_key :track_modifications
|
96
|
+
Person.audited_collections['TestParent#people'][:track_modifications].should be_true
|
96
97
|
end
|
97
98
|
|
98
99
|
it 'audits an object creation when relationships are defined' do
|
@@ -166,6 +167,23 @@ describe 'Acts as audited collection plugin' do
|
|
166
167
|
CollectionAudit.last.action.should == 'remove'
|
167
168
|
end
|
168
169
|
|
170
|
+
it 'audits an object creation when two parent relationships exist with the same collection name' do
|
171
|
+
p = TestParent.create :name => 'test parent'
|
172
|
+
f = TestFakeParent.create :name => 'test fake parent'
|
173
|
+
|
174
|
+
c = nil
|
175
|
+
lambda {
|
176
|
+
c = TestChild.create :name => 'test child', :test_fake_parent => f, :test_parent => p
|
177
|
+
}.should change(CollectionAudit, :count).by(2)
|
178
|
+
|
179
|
+
p.test_children_audits.last.child_record_id.should == c.id
|
180
|
+
p.test_children_audits.last.parent_record.should == p
|
181
|
+
p.test_children_audits.last.child_record_id.should == c.id
|
182
|
+
f.test_children_audits.last.child_record_id.should == c.id
|
183
|
+
f.test_children_audits.last.parent_record.should == f
|
184
|
+
f.test_children_audits.last.child_record_id.should == c.id
|
185
|
+
end
|
186
|
+
|
169
187
|
it 'skips auditing an object deletion when no relationships exist' do
|
170
188
|
c = TestChild.create :name => 'test child'
|
171
189
|
|
@@ -480,4 +498,59 @@ describe 'Acts as audited collection plugin' do
|
|
480
498
|
ca.child_record.should == g
|
481
499
|
ca.parent_record.should == c
|
482
500
|
end
|
501
|
+
|
502
|
+
it 'assigns a new audit record as the current record' do
|
503
|
+
p = TestParent.create :name => 'test parent'
|
504
|
+
c = p.test_children.create :name => 'test child'
|
505
|
+
p.test_children_audits.last.should be_current
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'unassigns the previous current record when a new current record is created' do
|
509
|
+
p = TestParent.create :name => 'test parent'
|
510
|
+
c = p.other_test_children.create :name => 'test child'
|
511
|
+
previous = p.other_test_children_audits.last
|
512
|
+
lambda {
|
513
|
+
c.update_attributes! :name => 'updated child'
|
514
|
+
}.should change(CollectionAudit, :count).by(1)
|
515
|
+
previous.reload.should_not be_current
|
516
|
+
p.other_test_children_audits.last.should be_current
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'tracks the current audit record uniquely per association' do
|
520
|
+
p = TestParent.create :name => 'test parent'
|
521
|
+
c = p.test_children.create :name => 'test child'
|
522
|
+
c.test_parent_with_only = p
|
523
|
+
c.save
|
524
|
+
|
525
|
+
p.test_children_audits.last.should be_current
|
526
|
+
p.test_children_with_only_audits.last.should be_current
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'tracks the current audit record correctly per hierarchy' do
|
530
|
+
p = TestParent.create :name => 'test parent'
|
531
|
+
c = p.test_children.create :name => 'test child'
|
532
|
+
p2 = TestParent.create :name => 'test parent 2'
|
533
|
+
c2 = p2.test_children.create :name => 'test child 2'
|
534
|
+
|
535
|
+
p.test_children_audits.last.should be_current
|
536
|
+
p2.test_children_audits.last.should be_current
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'tracks the current audit record correctly per child object' do
|
540
|
+
p = TestParent.create :name => 'test parent'
|
541
|
+
c = p.test_children.create :name => 'test child'
|
542
|
+
c2 = p.test_children.create :name => 'test child 2'
|
543
|
+
|
544
|
+
p.test_children_audits.for_child(c).last.should be_current
|
545
|
+
p.test_children_audits.for_child(c2).last.should be_current
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'tracks the current audit record correctly through different parent objects' do
|
549
|
+
p = TestParent.create :name => 'test parent'
|
550
|
+
f = TestFakeParent.create :name => 'test fake parent'
|
551
|
+
c = TestChild.create :name => 'test child', :test_fake_parent => f, :test_parent => p
|
552
|
+
|
553
|
+
p.test_children_audits.last.should be_current
|
554
|
+
f.test_children_audits.last.should be_current
|
555
|
+
end
|
483
556
|
end
|