acts_as_audited_collection 0.4.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.log
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/db/*.db
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in acts_as_audited_collection.gemspec
4
+ gemspec
data/LICENSE CHANGED
@@ -1,19 +1,22 @@
1
- Copyright (c) 2010 Shaun Mangelsdorf
1
+ Copyright (c) 2010-2012 Shaun Mangelsdorf
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
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
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
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
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- THE SOFTWARE.
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
- acts_as_audited_collection
1
+ acts\_as\_audited\_collection
2
2
  ==========================
3
3
 
4
- acts_as_audited_collection is a Rails plugin, which extends ActiveRecord to allow auditing of associations.
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
- TBD
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/acts_as_audited))
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
- yield(read_inheritable_attribute(:audited_collections)[options[:name]] ||= {})
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 |name, opts|
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
  require 'acts_as_audited_collection/collection_audit'
2
2
  require 'active_record/acts/audited_collection'
3
+ require 'acts_as_audited_collection/version'
3
4
 
4
5
  require 'acts_as_audited_collection/railtie' if defined? Rails
@@ -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
@@ -0,0 +1,3 @@
1
+ module ActsAsAuditedCollection
2
+ VERSION = '1.0.1'
3
+ end
@@ -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
@@ -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 :people
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 :people
82
- Person.audited_collections[:people].should have_key :cascade
83
- Person.audited_collections[:people][:cascade].should be_true
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 :people
94
- Person.audited_collections[:people].should have_key :track_modifications
95
- Person.audited_collections[:people][:track_modifications].should be_true
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