acts_as_audited_customized 1.2.2 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +6 -2
- data/VERSION +1 -1
- data/lib/acts_as_audited.rb +86 -2
- data/{generators/audit_model/templates/model.rb → lib/acts_as_audited/audit.rb} +27 -25
- data/lib/acts_as_audited/audit_sweeper.rb +11 -8
- data/rails/init.rb +1 -0
- metadata +5 -7
- data/generators/audit_model/USAGE +0 -9
- data/generators/audit_model/audit_model_generator.rb +0 -20
data/README
CHANGED
@@ -7,7 +7,7 @@ The purpose of this fork is to store both the previous values and the changed va
|
|
7
7
|
== Installation
|
8
8
|
|
9
9
|
* acts_as_audited can be installed as a gem:
|
10
|
-
|
10
|
+
|
11
11
|
# config/environment.rb
|
12
12
|
config.gem 'acts_as_audited', :lib => false, :source => 'http://gemcutter.org'
|
13
13
|
|
@@ -26,7 +26,7 @@ Declare <tt>acts_as_audited</tt> on your models:
|
|
26
26
|
class User < ActiveRecord::Base
|
27
27
|
acts_as_audited :except => [:password, :mistress]
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
Within a web request, will automatically record the user that made the change if your controller has a <tt>current_user</tt> method.
|
31
31
|
|
32
32
|
To record a user in the audits outside of a web request, you can use <tt>as_user</tt>:
|
@@ -44,6 +44,10 @@ If your model declares +attr_accessible+ after +acts_as_audited+, you need to se
|
|
44
44
|
attr_accessible :name
|
45
45
|
end
|
46
46
|
|
47
|
+
12/21/10:
|
48
|
+
|
49
|
+
There have been no tests written for any of the new features. There's just been no time.
|
50
|
+
|
47
51
|
== Compatability
|
48
52
|
|
49
53
|
acts_as_audited works with Rails 2.1 or later.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.1
|
data/lib/acts_as_audited.rb
CHANGED
@@ -37,6 +37,16 @@ module CollectiveIdea #:nodoc:
|
|
37
37
|
mattr_accessor :human_model
|
38
38
|
@@human_model = :user
|
39
39
|
|
40
|
+
# Any additional attributes you wish your model to write to. Default: []
|
41
|
+
mattr_accessor :additional_attributes
|
42
|
+
@@additional_attributes = []
|
43
|
+
|
44
|
+
# Whether or not you want to use the Observer to set current_<%= human_model %>.
|
45
|
+
# Set this to :false if your models have access to the current_<%= human_model %> method.
|
46
|
+
# Default :true
|
47
|
+
mattr_accessor :use_observer
|
48
|
+
@@use_observer = :true
|
49
|
+
|
40
50
|
class << self
|
41
51
|
# Call this method to modify defaults in your initializers.
|
42
52
|
#
|
@@ -46,6 +56,36 @@ module CollectiveIdea #:nodoc:
|
|
46
56
|
# end
|
47
57
|
def configure
|
48
58
|
yield self
|
59
|
+
|
60
|
+
modify_audit_model
|
61
|
+
end
|
62
|
+
|
63
|
+
def modify_audit_model
|
64
|
+
Audit.class_eval do
|
65
|
+
belongs_to @@human_model, :polymorphic => true
|
66
|
+
|
67
|
+
[@@additional_attributes].flatten.each do |attrib|
|
68
|
+
belongs_to attrib, :class_name => @@human_model.to_s.classify, :foreign_key => "#{attrib}_id"
|
69
|
+
end
|
70
|
+
|
71
|
+
alias_method :user_as_model=, "#{@@human_model}=".to_sym
|
72
|
+
alias_method "#{@@human_model}=".to_sym, :user_as_string=
|
73
|
+
|
74
|
+
alias_method :user_as_model, @@human_model
|
75
|
+
alias_method @@human_model, :user_as_string
|
76
|
+
|
77
|
+
def set_audit_user
|
78
|
+
self.send(@@human_model, Thread.current[:acts_as_audited_user]) if Thread.current[:acts_as_audited_user]
|
79
|
+
nil # prevent stopping callback chains
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if @@use_observer
|
84
|
+
::ActionController::Base.class_eval do
|
85
|
+
cache_sweeper :audit_sweeper
|
86
|
+
end
|
87
|
+
Audit.add_observer(AuditSweeper.instance)
|
88
|
+
end
|
49
89
|
end
|
50
90
|
|
51
91
|
def included(base) # :nodoc:
|
@@ -81,10 +121,14 @@ module CollectiveIdea #:nodoc:
|
|
81
121
|
# don't allow multiple calls
|
82
122
|
return if self.included_modules.include?(CollectiveIdea::Acts::Audited::InstanceMethods)
|
83
123
|
|
124
|
+
reserved_options = [:protect, :on, :create, :update, :destroy, :only, :except, :if, :unless]
|
84
125
|
options = {:protect => accessible_attributes.nil?}.merge(options)
|
85
126
|
|
86
127
|
class_inheritable_reader :non_audited_columns
|
87
128
|
class_inheritable_reader :auditing_enabled
|
129
|
+
class_inheritable_reader :manually_set_columns
|
130
|
+
class_inheritable_reader :if_condition
|
131
|
+
class_inheritable_reader :unless_condition
|
88
132
|
|
89
133
|
if options[:only]
|
90
134
|
except = self.column_names - options[:only].flatten.map(&:to_s)
|
@@ -94,6 +138,9 @@ module CollectiveIdea #:nodoc:
|
|
94
138
|
except |= Array(options[:except]).collect(&:to_s) if options[:except]
|
95
139
|
end
|
96
140
|
write_inheritable_attribute :non_audited_columns, except
|
141
|
+
write_inheritable_attribute :manually_set_columns, options.reject {|k, v| reserved_options.include?(k) }
|
142
|
+
write_inheritable_attribute :if_condition, options.delete(:if)
|
143
|
+
write_inheritable_attribute :unless_condition, options.delete(:unless)
|
97
144
|
|
98
145
|
has_many :audits, :as => :auditable, :order => "#{Audit.quoted_table_name}.version"
|
99
146
|
attr_protected :audit_ids if options[:protect]
|
@@ -187,6 +234,43 @@ module CollectiveIdea #:nodoc:
|
|
187
234
|
end
|
188
235
|
end
|
189
236
|
|
237
|
+
def evaluate(value)
|
238
|
+
case value
|
239
|
+
when Proc
|
240
|
+
value.arity > 0 ? value.call(self) : value.call
|
241
|
+
else
|
242
|
+
value
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def on_behalf_of_result
|
247
|
+
evaluate(on_behalf_of_call)
|
248
|
+
end
|
249
|
+
|
250
|
+
def set_manually_set_columns
|
251
|
+
manually_set_columns.inject(set_current_user) do |attrs, (attrib, value)|
|
252
|
+
attrs[attrib] = evaluate(value)
|
253
|
+
attrs
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def set_current_user
|
258
|
+
method = "current_#{CollectiveIdea::Acts::Audited.human_model}"
|
259
|
+
!CollectiveIdea::Acts::Audited.use_observer && self.class.respond_to?(method) ? { CollectiveIdea::Acts::Audited.human_model => self.class.send(method) } : {}
|
260
|
+
end
|
261
|
+
|
262
|
+
def eval_condition(condition)
|
263
|
+
evaluate(condition)
|
264
|
+
end
|
265
|
+
|
266
|
+
def eval_if_condition
|
267
|
+
!if_condition || eval_condition(if_condition)
|
268
|
+
end
|
269
|
+
|
270
|
+
def eval_unless_condition
|
271
|
+
!unless_condition || !eval_condition(unless_condition)
|
272
|
+
end
|
273
|
+
|
190
274
|
def audits_to(version = nil)
|
191
275
|
if version == :previous
|
192
276
|
version = if self.version
|
@@ -215,7 +299,8 @@ module CollectiveIdea #:nodoc:
|
|
215
299
|
end
|
216
300
|
|
217
301
|
def write_audit(attrs)
|
218
|
-
|
302
|
+
attrs = attrs.merge(set_manually_set_columns)
|
303
|
+
self.audits.create attrs if auditing_enabled && eval_if_condition && eval_unless_condition
|
219
304
|
end
|
220
305
|
end # InstanceMethods
|
221
306
|
|
@@ -251,7 +336,6 @@ module CollectiveIdea #:nodoc:
|
|
251
336
|
def audit_as( user, &block )
|
252
337
|
Audit.as_user( user, &block )
|
253
338
|
end
|
254
|
-
|
255
339
|
end
|
256
340
|
end
|
257
341
|
end
|
@@ -3,16 +3,17 @@ require 'set'
|
|
3
3
|
# Audit saves the changes to ActiveRecord models. It has the following attributes:
|
4
4
|
#
|
5
5
|
# * <tt>auditable</tt>: the ActiveRecord model that was changed
|
6
|
-
# * <tt
|
6
|
+
# * <tt>user</tt>: the user that performed the change; a string or an ActiveRecord model
|
7
7
|
# * <tt>action</tt>: one of create, update, or delete
|
8
8
|
# * <tt>changes</tt>: a serialized hash of all the changes
|
9
9
|
# * <tt>created_at</tt>: Time that the change was performed
|
10
10
|
#
|
11
11
|
class Audit < ActiveRecord::Base
|
12
12
|
belongs_to :auditable, :polymorphic => true
|
13
|
-
belongs_to
|
13
|
+
# belongs_to :user, :polymorphic => true
|
14
|
+
# belongs_to :on_behalf_of, :class_name => 'User', :foreign_key => 'on_behalf_of_id'
|
14
15
|
|
15
|
-
before_create :set_version_number, :
|
16
|
+
before_create :set_version_number, :set_audit_user
|
16
17
|
|
17
18
|
serialize :changes
|
18
19
|
|
@@ -28,19 +29,19 @@ class Audit < ActiveRecord::Base
|
|
28
29
|
# by +user+. This method is hopefully threadsafe, making it ideal
|
29
30
|
# for background operations that require audit information.
|
30
31
|
def as_user(user, &block)
|
31
|
-
Thread.current[:
|
32
|
+
Thread.current[:acts_as_audited_user] = user
|
32
33
|
yield
|
33
|
-
Thread.current[:
|
34
|
+
Thread.current[:acts_as_audited_user] = nil
|
34
35
|
end
|
35
36
|
|
36
|
-
def manual_audit(
|
37
|
+
def manual_audit(user, action, on_behalf_of = nil, auditable = nil)
|
37
38
|
attribs = { :action => action }
|
38
39
|
|
39
|
-
case
|
40
|
+
case user
|
40
41
|
when ActiveRecord::Base
|
41
|
-
attribs[
|
42
|
+
attribs[CollectiveIdea::Acts::Audited.human_model] = user
|
42
43
|
when String
|
43
|
-
attribs[:username] =
|
44
|
+
attribs[:username] = user
|
44
45
|
end
|
45
46
|
|
46
47
|
case auditable
|
@@ -50,26 +51,27 @@ class Audit < ActiveRecord::Base
|
|
50
51
|
attribs[:auditable_type] = auditable
|
51
52
|
end
|
52
53
|
|
54
|
+
attribs[:on_behalf_of] = on_behalf_of if on_behalf_of
|
53
55
|
Audit.create attribs
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
|
-
# Allows
|
58
|
-
def
|
59
|
+
# Allows user to be set to either a string or an ActiveRecord object
|
60
|
+
def user_as_string=(user) #:nodoc:
|
59
61
|
# reset both either way
|
60
|
-
self
|
61
|
-
|
62
|
-
self
|
63
|
-
self.username =
|
62
|
+
self.user_as_model = self.username = nil
|
63
|
+
user.is_a?(ActiveRecord::Base) ?
|
64
|
+
self.user_as_model = user :
|
65
|
+
self.username = user
|
64
66
|
end
|
65
|
-
alias_method
|
66
|
-
alias_method
|
67
|
+
# alias_method :user_as_model=, :user=
|
68
|
+
# alias_method :user=, :user_as_string=
|
67
69
|
|
68
|
-
def
|
69
|
-
self
|
70
|
+
def user_as_string #:nodoc:
|
71
|
+
self.user_as_model || self.username
|
70
72
|
end
|
71
|
-
alias_method
|
72
|
-
alias_method
|
73
|
+
# alias_method :user_as_model, :user
|
74
|
+
# alias_method :user, :user_as_string
|
73
75
|
|
74
76
|
def revision
|
75
77
|
clazz = auditable_type.constantize
|
@@ -131,9 +133,9 @@ private
|
|
131
133
|
self.version = max + 1
|
132
134
|
end
|
133
135
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
end
|
136
|
+
# def set_audit_user
|
137
|
+
# self.user = Thread.current[:acts_as_audited_user] if Thread.current[:acts_as_audited_user]
|
138
|
+
# nil # prevent stopping callback chains
|
139
|
+
# end
|
138
140
|
|
139
141
|
end
|
@@ -19,19 +19,22 @@ module CollectiveIdea #:nodoc:
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
ActionController::Base.class_eval do
|
23
|
+
extend CollectiveIdea::ActionController::Audited
|
24
|
+
end
|
25
|
+
|
22
26
|
class AuditSweeper < ActionController::Caching::Sweeper #:nodoc:
|
27
|
+
def current_user_method
|
28
|
+
"current_#{CollectiveIdea::Acts::Audited.human_model}".to_sym
|
29
|
+
end
|
30
|
+
|
23
31
|
def before_create(audit)
|
24
|
-
|
32
|
+
raise "Got here"
|
33
|
+
audit.send("#{CollectiveIdea::Acts::Audited.human_model}=".to_sym, current_user) unless audit.send(CollectiveIdea::Acts::Audited.human_model)
|
25
34
|
end
|
26
35
|
|
27
36
|
def current_user
|
28
|
-
|
29
|
-
controller.send(method) if controller.respond_to?(method, true)
|
37
|
+
controller.send current_user_method if controller.respond_to?(current_user_method, true)
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
|
-
ActionController::Base.class_eval do
|
34
|
-
extend CollectiveIdea::ActionController::Audited
|
35
|
-
cache_sweeper :audit_sweeper
|
36
|
-
end
|
37
|
-
Audit.add_observer(AuditSweeper.instance)
|
data/rails/init.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_audited_customized
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 3
|
9
|
+
- 1
|
10
|
+
version: 1.3.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Brandon Keepers
|
@@ -77,14 +77,12 @@ files:
|
|
77
77
|
- README
|
78
78
|
- Rakefile
|
79
79
|
- VERSION
|
80
|
-
- generators/audit_model/USAGE
|
81
|
-
- generators/audit_model/audit_model_generator.rb
|
82
|
-
- generators/audit_model/templates/model.rb
|
83
80
|
- generators/audited_migration/USAGE
|
84
81
|
- generators/audited_migration/audited_migration_generator.rb
|
85
82
|
- generators/audited_migration/templates/migration.rb
|
86
83
|
- init.rb
|
87
84
|
- lib/acts_as_audited.rb
|
85
|
+
- lib/acts_as_audited/audit.rb
|
88
86
|
- lib/acts_as_audited/audit_sweeper.rb
|
89
87
|
- rails/init.rb
|
90
88
|
- test/acts_as_audited_test.rb
|
@@ -1,9 +0,0 @@
|
|
1
|
-
Description:
|
2
|
-
The audit model generator creates the Audit model in app/models/ associating it with the correct "human" class ("user" by default).
|
3
|
-
|
4
|
-
Example:
|
5
|
-
./script/generate audit_model Person
|
6
|
-
|
7
|
-
This will create the Audit model in app/models/ associating it to the Person model.
|
8
|
-
|
9
|
-
You must generate the migration using the same "human" class.
|
@@ -1,20 +0,0 @@
|
|
1
|
-
class AuditModelGenerator < Rails::Generator::NamedBase
|
2
|
-
def initialize(runtime_args, runtime_options = {})
|
3
|
-
runtime_args << 'user' if runtime_args.empty?
|
4
|
-
super
|
5
|
-
@human_model = runtime_args[0] ? runtime_args[0].underscore : 'user'
|
6
|
-
end
|
7
|
-
|
8
|
-
def manifest
|
9
|
-
record do |m|
|
10
|
-
m.directory(File.join('app', 'models'))
|
11
|
-
m.template('model.rb', "app/models/audit.rb", :assigns => { :human_model => @human_model })
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
def banner
|
18
|
-
"Usage: #{$0} audit_model [human_model_name]"
|
19
|
-
end
|
20
|
-
end
|