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