has_audit_trail 0.0.0 → 0.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
- # HasAuditTrail
1
+ # HasAuditTrail [![Build Status](https://secure.travis-ci.org/andersonfreitas/has_audit_trail.png)](http://travis-ci.org/andersonfreitas/has_audit_trail)
2
2
 
3
- TODO: Write a gem description
3
+ ## Disclaimer
4
+
5
+ This is **VERY** experimental, please wait until the release 0.1.0 before using it in production!
4
6
 
5
7
  ## Installation
6
8
 
@@ -16,9 +18,46 @@ Or install it yourself as:
16
18
 
17
19
  $ gem install has_audit_trail
18
20
 
21
+ Create the migrations:
22
+
23
+ $ rails generate has_audit_trail:install
24
+
19
25
  ## Usage
20
26
 
21
- TODO: Write usage instructions here
27
+ ```ruby
28
+ class User < ActiveRecord::Base
29
+ has_audit_trail
30
+ end
31
+ ```
32
+
33
+ ```ruby
34
+ class User < ActiveRecord::Base
35
+ has_audit_trail :only => [ :name, :email ]
36
+ end
37
+ ```
38
+
39
+ ```ruby
40
+ class User < ActiveRecord::Base
41
+ has_audit_trail :only => [ :name, :email, :projects => { :audit => Proc.new { |p| p.name } } ]
42
+ end
43
+ ```
44
+
45
+ ```ruby
46
+ class Project < ActiveRecord::Base
47
+ has_many :tasks
48
+
49
+ accepts_nested_attributes_for :tasks
50
+
51
+ has_audit_trail(
52
+ :audit_nested => {
53
+ :tasks => {
54
+ :label => Proc.new { |task| task.name },
55
+ :value_print => Proc.new { |value| value }
56
+ }
57
+ }
58
+ )
59
+ end
60
+ ```
22
61
 
23
62
  ## Contributing
24
63
 
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
5
5
  gem.authors = ["Anderson Freitas"]
6
6
  gem.email = ["gems@andersonfreitas.com"]
7
7
  gem.description = %q{Simple audit trail for Rails models}
8
- gem.summary = %q{Audit trails for Rails models}
8
+ gem.summary = gem.description
9
9
  gem.homepage = ""
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -14,4 +14,12 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "has_audit_trail"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = HasAuditTrail::VERSION
17
+
18
+ gem.add_development_dependency 'activerecord', '~> 3.0'
19
+ gem.add_development_dependency 'rails', '~> 3.0'
20
+ gem.add_development_dependency 'rspec-rails', '~> 2.0'
21
+ gem.add_development_dependency 'sqlite3', '~> 1.0'
22
+
23
+ gem.add_dependency('activemodel', '~> 3.0')
24
+ gem.add_dependency('actionpack', '~> 3.0')
17
25
  end
@@ -0,0 +1,28 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ require 'active_record'
4
+ require 'rails/generators/active_record'
5
+
6
+ module HasAuditTrail
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+
11
+ source_root File.expand_path("../templates", __FILE__)
12
+
13
+ def self.next_migration_number(dirname)
14
+ next_migration_number = current_migration_number(dirname) + 1
15
+ if ActiveRecord::Base.timestamped_migrations
16
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
17
+ else
18
+ "%.3d" % next_migration_number
19
+ end
20
+ end
21
+
22
+ def copy_migration
23
+ migration_template 'install_audit_trail.rb', 'db/migrate/install_has_audit_trail.rb'
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,27 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ create_table "audit_trails", :force => true do |t|
5
+ t.integer "user_id"
6
+
7
+ t.string "remote_address"
8
+ t.string "user_agent"
9
+
10
+ t.string "action"
11
+ t.string "object"
12
+ t.string "object_id"
13
+ t.string "property"
14
+ t.string "old_value"
15
+ t.string "new_value"
16
+ t.datetime "created_at", :null => false
17
+ end
18
+
19
+ add_index :audit_trails, :user_id
20
+ add_index :audit_trails, :created_at
21
+ end
22
+
23
+ def self.down
24
+ drop_table :audit_trails
25
+ end
26
+ end
27
+
@@ -0,0 +1,4 @@
1
+ class AuditTrail < ActiveRecord::Base
2
+ belongs_to :user
3
+ end
4
+
@@ -0,0 +1,107 @@
1
+ module HasAuditTrail
2
+ module AuditTrailInclude
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ VALID_OPTIONS = [:only, :audit_nested ]
8
+
9
+ module ClassMethods
10
+ def has_audit_trail(opts = {})
11
+ return if self.included_modules.include?(HasAuditTrail::AuditTrailInclude::InstanceMethods)
12
+
13
+ opts.assert_valid_keys HasAuditTrail::AuditTrailInclude::VALID_OPTIONS
14
+
15
+ class_attribute :audited_columns, :instance_writer => false
16
+ class_attribute :audited_associations, :instance_writer => false
17
+ class_attribute :audited_collections, :instance_writer => false
18
+ class_attribute :changed_collections, :instance_writer => false
19
+
20
+ self.audited_columns = opts[:only] || self.attribute_names.collect { |str| str.to_sym }
21
+ self.audited_columns -= opts[:except] if opts[:except]
22
+
23
+ self.audited_associations = opts[:audit_nested] if opts[:audit_nested]
24
+
25
+ # Discovering which attributes are associations...
26
+ self.audited_collections = []
27
+ self.audited_columns.each do |attr|
28
+ if self.respond_to? "after_add_for_#{attr}"
29
+ self.audited_collections << attr
30
+ self.audited_columns -= [attr]
31
+ end
32
+ end
33
+
34
+ self.changed_collections = {}
35
+ self.audited_collections.try(:each) do |attr|
36
+ self.changed_collections[attr] = { :added => [], :removed => [] }
37
+
38
+ self.send("after_add_for_#{attr}") << Proc.new { |owner, record| owner.changed_collections[attr.to_sym][:added] << record }
39
+ self.send("after_remove_for_#{attr}") << Proc.new { |owner, record| owner.changed_collections[attr.to_sym][:removed] << record }
40
+ end
41
+
42
+ after_create :audit_create
43
+ before_update :audit_update
44
+ before_destroy :audit_destroy
45
+
46
+ include HasAuditTrail::AuditTrailInclude::InstanceMethods
47
+ end
48
+ end
49
+ module InstanceMethods
50
+ def write_audit(attrs)
51
+ attrs.merge!(:object => self.class.model_name, :object_id => self.id)
52
+ AuditTrail.create!(attrs)
53
+ end
54
+
55
+ def audit_create
56
+ write_audit(:action => :create)
57
+ end
58
+
59
+ def audit_destroy
60
+ write_audit(:action => :destroy)
61
+ end
62
+
63
+ def audit_update
64
+ if changed?
65
+ changes.each do |field, change|
66
+ if self.audited_columns.include? field.to_sym
67
+ write_audit(:action => :update, :old_value => change[0].to_yaml, :new_value => change[1].to_yaml, :property => field)
68
+ end
69
+ end
70
+ end
71
+
72
+ # direct associations
73
+ audited_collections.try(:each) do |attr|
74
+ unless new_record?
75
+ if changed_collections[attr][:added].size > 0
76
+ old = self.send(attr) - changed_collections[attr][:added]
77
+
78
+ write_audit(:action => :update, :old_value => old.collect { |x| x.name }, :new_value => self.send(attr).collect {|x|x.name}, :property => attr)
79
+ changed_collections[attr][:added] = []
80
+ end
81
+ if changed_collections[attr][:removed].size > 0
82
+ old = self.send(attr) + changed_collections[attr][:removed]
83
+
84
+ write_audit(:action => :update, :old_value => old.collect { |x| x.name }, :new_value => self.send(attr).collect {|x|x.name}, :property => attr)
85
+ changed_collections[attr][:removed] = []
86
+ end
87
+ end
88
+ end
89
+
90
+ # accepts_nested_attributes_for
91
+ audited_associations.try(:each) do |attr, procs|
92
+ unless new_record?
93
+ self.send(attr).each do |c|
94
+ if c.changed?
95
+ c.changes.each do |field, change|
96
+ if change[0].to_s != change[1].to_s
97
+ write_audit(:action => :update, :old_value => change[0].to_yaml, :new_value => change[1].to_yaml, :property => procs[:label].call(c))
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,13 @@
1
+ class AuditTrailSweeper < ActionController::Caching::Sweeper
2
+ observe AuditTrail
3
+
4
+ def before_create(audit_trail)
5
+ audit_trail.user ||= current_user
6
+ audit_trail.remote_address = controller.try(:request).try(:ip)
7
+ end
8
+
9
+ def current_user
10
+ controller.send :current_user if controller.respond_to?(:current_user, true)
11
+ end
12
+ end
13
+
@@ -1,3 +1,3 @@
1
1
  module HasAuditTrail
2
- VERSION = "0.0.0"
2
+ VERSION = "0.0.1"
3
3
  end
@@ -1,5 +1,20 @@
1
- require "has_audit_trail/version"
1
+ require 'active_record'
2
+ require 'has_audit_trail/version'
3
+ require 'has_audit_trail/audit_trail'
4
+ require 'has_audit_trail/audit_trail_include'
2
5
 
3
- module HasAuditTrail
4
- # Your code goes here...
6
+ ActiveRecord::Base.send :include, HasAuditTrail::AuditTrailInclude
7
+
8
+ #ActionController::Base.class_eval do
9
+ #require_dependency 'has_audit_trail'
10
+ #end
11
+
12
+ if defined?(ActionController) and defined?(ActionController::Base)
13
+
14
+ require 'has_audit_trail/audit_trail_sweeper'
15
+
16
+ ActionController::Base.class_eval do
17
+ cache_sweeper :audit_trail_sweeper
18
+ end
5
19
  end
20
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_audit_trail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,104 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-23 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-05-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sqlite3
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: activemodel
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '3.0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '3.0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: actionpack
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '3.0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
14
110
  description: Simple audit trail for Rails models
15
111
  email:
16
112
  - gems@andersonfreitas.com
@@ -24,7 +120,12 @@ files:
24
120
  - README.md
25
121
  - Rakefile
26
122
  - has_audit_trail.gemspec
123
+ - lib/generators/has_audit_trail/install_generator.rb
124
+ - lib/generators/has_audit_trail/templates/install_audit_trail.rb
27
125
  - lib/has_audit_trail.rb
126
+ - lib/has_audit_trail/audit_trail.rb
127
+ - lib/has_audit_trail/audit_trail_include.rb
128
+ - lib/has_audit_trail/audit_trail_sweeper.rb
28
129
  - lib/has_audit_trail/version.rb
29
130
  homepage: ''
30
131
  licenses: []
@@ -46,8 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
147
  version: '0'
47
148
  requirements: []
48
149
  rubyforge_project:
49
- rubygems_version: 1.8.23
150
+ rubygems_version: 1.8.24
50
151
  signing_key:
51
152
  specification_version: 3
52
- summary: Audit trails for Rails models
153
+ summary: Simple audit trail for Rails models
53
154
  test_files: []