auditor 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,6 +19,13 @@ Generate the migration and create the audits table
19
19
  rails generate auditor:migration
20
20
  rake db:migrate
21
21
 
22
+ = Upgrading
23
+
24
+ You will need to run the upgrade migration if coming from a version earlier than 2.1.0
25
+
26
+ rails generate auditor:upgrade
27
+ rake db:migrate
28
+
22
29
  = Setup
23
30
 
24
31
  Auditor needs to know who the current user is, but with no standard for doing so you'll have to do a little work to set things up. You simply need to set your current user model object as the Auditor current user before any CRUD operations are performed. For example, in a Rails application you could add the following to your application_controller.rb
@@ -46,6 +53,8 @@ All audit data is stored in a table named Audits, which is automatically created
46
53
 
47
54
  * auditable_id - the primary key of the table belonging to the audited model object
48
55
  * auditable_type - the class type of the audited model object
56
+ * owner_id - the primary key of the of the model that owns this audit record
57
+ * owner_type - the class type of the owner model object
49
58
  * user_id - the primary key of the table belonging to the user being audited
50
59
  * user_type - the class type of the model object representing users in your application
51
60
  * action - a string indicating the action that was audited (create, update, destroy, or find)
@@ -62,6 +71,9 @@ The audited_changes column automatically serializes the changes of any model att
62
71
  # Only audit edits to the title column when destroying/deleting
63
72
  audit(:destroy, :only => :title)
64
73
 
74
+ # Associate the audit records with a related model, which becomes the owner
75
+ audit(:update, :on => :book)
76
+
65
77
  = Make Auditing Important
66
78
 
67
79
  There's an alternate form of specifying your audit requirements that will cause the create, find, update, or destroy to fail if for some reason the audit record cannot be saved to the database. Instead of calling audit, call audit! instead.
@@ -12,12 +12,13 @@ Gem::Specification.new do |s|
12
12
  s.description = %q{Auditor allows you to declaratively specify what CRUD operations should be audited and save the audit data to the database.}
13
13
  s.license = "MIT"
14
14
 
15
- s.files = `git ls-files`.split("\n")
15
+ s.files = `git ls-files`.split("\n").reject { |path| path =~ /^(Gemfile|.gitignore|Rakefile)/ }
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
+ s.add_dependency('activerecord', '> 3.0.0')
21
+
20
22
  s.add_development_dependency('rspec', '2.5.0')
21
23
  s.add_development_dependency('sqlite3-ruby', '1.3.3')
22
- s.add_development_dependency('activerecord', '> 3.0.0')
23
24
  end
@@ -3,6 +3,7 @@ require 'auditor/config'
3
3
 
4
4
  class Audit < ActiveRecord::Base
5
5
  belongs_to :auditable, :polymorphic => true
6
+ belongs_to :owner, :polymorphic => true
6
7
  belongs_to :user, :polymorphic => true
7
8
 
8
9
  before_create :set_version_number
@@ -26,7 +26,13 @@ module Auditor
26
26
  audit.auditable_type = model.class.name
27
27
  audit.audited_changes = prepare_changes(model.changes) if changes_available?(action)
28
28
  audit.action = action
29
- audit.comment = @blk.call(model, user) if @blk
29
+ audit.comment = @blk.call(model, user, action) if @blk
30
+
31
+ without_auditing do
32
+ owner = @options[:on] ? model.send(@options[:on].to_sym) : model
33
+ audit.owner_id = owner.id
34
+ audit.owner_type = owner.class.name
35
+ end
30
36
 
31
37
  @options[:fail_on_error] ? audit.save! : audit.save
32
38
  end
@@ -1,3 +1,5 @@
1
+ require 'auditor/user'
2
+
1
3
  module Auditor
2
4
  module Status
3
5
 
@@ -1,3 +1,3 @@
1
1
  module Auditor
2
- VERSION = "2.0.3"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -3,6 +3,8 @@ class CreateAuditsTable < ActiveRecord::Migration
3
3
  create_table :audits, :force => true do |t|
4
4
  t.column :auditable_id, :integer, :null => false
5
5
  t.column :auditable_type, :string, :null => false
6
+ t.column :owner_id, :integer, :null => false
7
+ t.column :owner_type, :string, :null => false
6
8
  t.column :user_id, :integer, :null => false
7
9
  t.column :user_type, :string, :null => false
8
10
  t.column :action, :string, :null => false
@@ -0,0 +1,13 @@
1
+ class UpgradeAuditsTable < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :audits, :owner_id, :integer
4
+ add_column :audits, :owner_type, :string
5
+
6
+ add_index :audits, [:owner_id, :owner_type], :name => 'owner_index'
7
+ end
8
+
9
+ def self.down
10
+ remove_column :audits, :owner_type
11
+ remove_column :audits, :owner_id
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Auditor
5
+ module Generators
6
+ class UpgradeGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ desc "Create upgrade migration for Auditor audits table"
10
+
11
+ source_root File.expand_path("../templates", __FILE__)
12
+
13
+ def self.next_migration_number(dirname)
14
+ if ActiveRecord::Base.timestamped_migrations
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ else
17
+ "%.3d" % (current_migration_number(dirname) + 1)
18
+ end
19
+ end
20
+
21
+ def create_migration_file
22
+ migration_template 'upgrade.rb', 'db/migrate/upgrade_audits_table.rb'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,24 +8,24 @@ describe Audit do
8
8
  end
9
9
 
10
10
  it 'should set the version number on save' do
11
- audit = Audit.create(:auditable => @auditable, :audited_changes => { :name => [nil, 'new']}, :user => @user, :action => :create)
11
+ audit = Audit.create(:auditable => @auditable, :owner => @auditable, :audited_changes => { :name => [nil, 'new']}, :user => @user, :action => :create)
12
12
  audit.version.should == 1
13
13
  end
14
14
 
15
15
  it 'should provide access to the audited model object' do
16
- audit = Audit.create(:auditable => @auditable, :user => @user, :action => :create)
16
+ audit = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :create)
17
17
  audit.auditable.should == @auditable
18
18
  end
19
19
  it 'should provide access to the user associated with the audit' do
20
- audit = Audit.create(:auditable => @auditable, :user => @user, :action => :create)
20
+ audit = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :create)
21
21
  audit.user.should == @user
22
22
  end
23
23
 
24
24
  it 'should create a snapshot of the audited objects attributes at the time of the audit' do
25
- audit1 = Audit.create(:auditable => @auditable, :user => @user, :action => :create)
26
- audit2 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => [nil, 'n1'], 'value' => [nil, 'v1']})
27
- audit3 = Audit.create(:auditable => @auditable, :user => @user, :action => :find)
28
- audit4 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
25
+ audit1 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :create)
26
+ audit2 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => [nil, 'n1'], 'value' => [nil, 'v1']})
27
+ audit3 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :find)
28
+ audit4 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
29
29
 
30
30
  audit1.attribute_snapshot.should == {}
31
31
  audit2.attribute_snapshot.should == {'name' => 'n1', 'value' => 'v1'}
@@ -35,10 +35,10 @@ describe Audit do
35
35
 
36
36
  describe 'modifying scope' do
37
37
  it 'should return all audit records that were a result of modifying the audited object attributes' do
38
- audit1 = Audit.create(:auditable => @auditable, :user => @user, :action => :create, :audited_changes => {'name' => [nil, 'n0']})
39
- audit2 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => ['n0', 'n1'], 'value' => [nil, 'v1']})
40
- audit3 = Audit.create(:auditable => @auditable, :user => @user, :action => :find)
41
- audit4 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
38
+ audit1 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :create, :audited_changes => {'name' => [nil, 'n0']})
39
+ audit2 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => ['n0', 'n1'], 'value' => [nil, 'v1']})
40
+ audit3 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :find)
41
+ audit4 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
42
42
 
43
43
  Audit.modifying.should include(audit1, audit2, audit4)
44
44
  Audit.modifying.should_not include(audit3)
@@ -48,10 +48,10 @@ describe Audit do
48
48
  describe 'predecessors scope' do
49
49
  it 'should return all previous audit records for the same auditable' do
50
50
  auditable2 = Model.create
51
- audit1 = Audit.create(:auditable => @auditable, :user => @user, :action => :create)
52
- audit2 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => [nil, 'n1'], 'value' => [nil, 'v1']})
53
- audit3 = Audit.create(:auditable => auditable2, :user => @user, :action => :find)
54
- audit4 = Audit.create(:auditable => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
51
+ audit1 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :create)
52
+ audit2 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'name' => [nil, 'n1'], 'value' => [nil, 'v1']})
53
+ audit3 = Audit.create(:auditable => auditable2, :owner => auditable2, :user => @user, :action => :find)
54
+ audit4 = Audit.create(:auditable => @auditable, :owner => @auditable, :user => @user, :action => :update, :audited_changes => {'value' => [nil, 'v2']})
55
55
 
56
56
  Audit.trail(audit4).should include(audit1, audit2, audit4)
57
57
  Audit.trail(audit4).should_not include(audit3)
@@ -59,5 +59,3 @@ describe Audit do
59
59
  end
60
60
  end
61
61
 
62
- class Model < ActiveRecord::Base; end
63
- class User < ActiveRecord::Base; end
@@ -7,9 +7,14 @@ describe Auditor::Auditable do
7
7
 
8
8
  before(:each) do
9
9
  @user = User.create
10
+ @original_model = Model
10
11
  Auditor::User.current_user = @user
11
12
  end
12
13
 
14
+ after(:each) do
15
+ reset_model
16
+ end
17
+
13
18
  it 'should audit find' do
14
19
  redefine_model { audit!(:find) }
15
20
  m = without_auditing { Model.create }
@@ -105,7 +110,9 @@ describe Auditor::Auditable do
105
110
  Object.send :const_set, 'Model', clazz
106
111
  end
107
112
 
108
- class ::User < ActiveRecord::Base; end
109
- class ::Model < ActiveRecord::Base; end
113
+ def reset_model
114
+ Object.send :remove_const, 'Model'
115
+ Object.send :const_set, 'Model', @original_model
116
+ end
110
117
 
111
118
  end
@@ -35,6 +35,8 @@ describe Auditor::Recorder do
35
35
  audit.action.should == action.to_s
36
36
  audit.auditable_id.should == model.id
37
37
  audit.auditable_type.should == model.class.to_s
38
+ audit.owner_id.should == model.id
39
+ audit.owner_type.should == model.class.to_s
38
40
  audit.user_id.should == @user.id
39
41
  audit.user_type.should == @user.class.to_s
40
42
  audit.comment.should == 'comment'
@@ -42,6 +44,7 @@ describe Auditor::Recorder do
42
44
 
43
45
  audit.user.should == @user
44
46
  audit.auditable.should == model
47
+ audit.owner.should == model
45
48
  end
46
49
 
47
50
  it 'should set comment details to nil if they are not given' do
@@ -81,6 +84,32 @@ describe Auditor::Recorder do
81
84
  audit.audited_changes.should == {'name' => [nil, 'changed'] }
82
85
  end
83
86
 
84
- class Model < ActiveRecord::Base; end
85
- class User < ActiveRecord::Base; end
87
+ it 'should associate audit records with an owner' do
88
+ model = Model.create
89
+ config = Auditor::Config.new(:create)
90
+ recorder = Auditor::Recorder.new(config.options)
91
+ recorder.after_create(model)
92
+ audit = Audit.last
93
+ audit.owner.should == model
94
+
95
+ owner = User.create
96
+ model.user = owner
97
+ config = Auditor::Config.new(:create, :on => :user)
98
+ recorder = Auditor::Recorder.new(config.options)
99
+ recorder.after_create(model)
100
+ audit = Audit.last
101
+ audit.owner.should == owner
102
+ end
103
+
104
+ it 'should pass the model, user, and action to any supplied block' do
105
+ model = Model.create
106
+ config = Auditor::Config.new(:create)
107
+ recorder = Auditor::Recorder.new(config.options) do |model, user, action|
108
+ model.should == model
109
+ user.should == @user
110
+ action.should == :create
111
+ end
112
+ recorder.after_create(model)
113
+ end
114
+
86
115
  end
@@ -1,6 +1,7 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'active_record'
3
3
  require 'generators/auditor/migration/templates/migration'
4
+ require 'fileutils'
4
5
 
5
6
  tmpdir = File.join(File.dirname(__FILE__), '..', '..', 'tmp')
6
7
  FileUtils.mkdir(tmpdir) unless File.exist?(tmpdir)
@@ -36,6 +37,7 @@ class CreateModel < ActiveRecord::Migration
36
37
  create_table :models, :force => true do |t|
37
38
  t.column :name, :string
38
39
  t.column :value, :string
40
+ t.column :user_id, :integer
39
41
  end
40
42
  end
41
43
 
@@ -0,0 +1,4 @@
1
+ class User < ActiveRecord::Base; end
2
+ class Model < ActiveRecord::Base
3
+ belongs_to :user
4
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: auditor
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
+ - 1
8
9
  - 0
9
- - 3
10
- version: 2.0.3
10
+ version: 2.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeff Kunkle
@@ -15,55 +15,55 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-23 00:00:00 -04:00
18
+ date: 2011-06-20 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: rspec
22
+ name: activerecord
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - "="
27
+ - - ">"
28
28
  - !ruby/object:Gem::Version
29
- hash: 27
29
+ hash: 7
30
30
  segments:
31
- - 2
32
- - 5
31
+ - 3
33
32
  - 0
34
- version: 2.5.0
35
- type: :development
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
- name: sqlite3-ruby
38
+ name: rspec
39
39
  prerelease: false
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - "="
44
44
  - !ruby/object:Gem::Version
45
- hash: 29
45
+ hash: 27
46
46
  segments:
47
- - 1
48
- - 3
49
- - 3
50
- version: 1.3.3
47
+ - 2
48
+ - 5
49
+ - 0
50
+ version: 2.5.0
51
51
  type: :development
52
52
  version_requirements: *id002
53
53
  - !ruby/object:Gem::Dependency
54
- name: activerecord
54
+ name: sqlite3-ruby
55
55
  prerelease: false
56
56
  requirement: &id003 !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
- - - ">"
59
+ - - "="
60
60
  - !ruby/object:Gem::Version
61
- hash: 7
61
+ hash: 29
62
62
  segments:
63
+ - 1
63
64
  - 3
64
- - 0
65
- - 0
66
- version: 3.0.0
65
+ - 3
66
+ version: 1.3.3
67
67
  type: :development
68
68
  version_requirements: *id003
69
69
  description: Auditor allows you to declaratively specify what CRUD operations should be audited and save the audit data to the database.
@@ -75,11 +75,8 @@ extensions: []
75
75
  extra_rdoc_files: []
76
76
 
77
77
  files:
78
- - .gitignore
79
- - Gemfile
80
78
  - LICENSE
81
79
  - README.rdoc
82
- - Rakefile
83
80
  - auditor.gemspec
84
81
  - init.rb
85
82
  - lib/auditor.rb
@@ -94,6 +91,8 @@ files:
94
91
  - lib/generators/auditor.rb
95
92
  - lib/generators/auditor/migration/migration_generator.rb
96
93
  - lib/generators/auditor/migration/templates/migration.rb
94
+ - lib/generators/auditor/upgrade/templates/upgrade.rb
95
+ - lib/generators/auditor/upgrade/upgrade_generator.rb
97
96
  - spec/audit_spec.rb
98
97
  - spec/auditable_spec.rb
99
98
  - spec/config_spec.rb
@@ -101,6 +100,7 @@ files:
101
100
  - spec/spec_helper.rb
102
101
  - spec/status_spec.rb
103
102
  - spec/support/db_setup.rb
103
+ - spec/support/model_setup.rb
104
104
  - spec/support/transactional_specs.rb
105
105
  - spec/user_spec.rb
106
106
  has_rdoc: true
@@ -145,5 +145,6 @@ test_files:
145
145
  - spec/spec_helper.rb
146
146
  - spec/status_spec.rb
147
147
  - spec/support/db_setup.rb
148
+ - spec/support/model_setup.rb
148
149
  - spec/support/transactional_specs.rb
149
150
  - spec/user_spec.rb
data/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- coverage
2
- rdoc
3
- *.gem
4
- .bundle
5
- Gemfile.lock
6
- pkg/*
7
- tmp/*
8
- .DS_Store
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in auditor.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,33 +0,0 @@
1
- $:.unshift File.expand_path("../lib", __FILE__)
2
-
3
- require 'rake'
4
- require 'rake/rdoctask'
5
- require 'rspec/core/rake_task'
6
- require 'bundler'
7
-
8
- Bundler::GemHelper.install_tasks
9
-
10
- desc 'Default: run specs'
11
- task :default => :spec
12
-
13
- desc "Run specs"
14
- RSpec::Core::RakeTask.new do |t|
15
- t.rspec_opts = %w(-fs --color)
16
- end
17
-
18
- desc "Run specs with RCov"
19
- RSpec::Core::RakeTask.new(:rcov) do |t|
20
- t.rspec_opts = %w(-fs --color)
21
- t.rcov = true
22
- t.rcov_opts = %w(--exclude "spec/*,gems/*")
23
- end
24
-
25
- desc 'Generate documentation for the gem.'
26
- Rake::RDocTask.new(:rdoc) do |rdoc|
27
- rdoc.rdoc_dir = 'rdoc'
28
- rdoc.title = 'Auditor'
29
- rdoc.options << '--line-numbers' << '--inline-source'
30
- rdoc.rdoc_files.include('README.rdoc')
31
- rdoc.rdoc_files.include('lib/**/*.rb')
32
- end
33
-