paper_trail 1.4.6 → 1.5.0
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 +22 -3
- data/VERSION +1 -1
- data/lib/paper_trail.rb +31 -6
- data/lib/paper_trail/controller.rb +48 -4
- data/lib/paper_trail/has_paper_trail.rb +12 -5
- data/paper_trail.gemspec +1 -1
- data/test/paper_trail_controller_test.rb +14 -0
- data/test/schema.rb +4 -0
- data/test/test_helper.rb +6 -0
- metadata +1 -1
data/README.md
CHANGED
@@ -11,9 +11,10 @@ PaperTrail lets you track changes to your models' data. It's good for auditing
|
|
11
11
|
* Allows you to get at every version, including the original, even once destroyed.
|
12
12
|
* Allows you to get at every version even if the schema has since changed.
|
13
13
|
* Allows you to get at the version as of a particular time.
|
14
|
-
* Automatically records who was responsible
|
14
|
+
* Automatically records who was responsible via your controller. PaperTrail calls `current_user` by default, if it exists, but you can have it call any method you like.
|
15
15
|
* Allows you to set who is responsible at model-level (useful for migrations).
|
16
|
-
* Allows you to store arbitrary metadata with each version (useful for filtering versions).
|
16
|
+
* Allows you to store arbitrary model-level metadata with each version (useful for filtering versions).
|
17
|
+
* Allows you to store arbitrary controller-level information with each version, e.g. remote IP.
|
17
18
|
* Can be turned off/on per class (useful for migrations).
|
18
19
|
* Can be turned off/on globally (useful for testing).
|
19
20
|
* No configuration necessary.
|
@@ -146,6 +147,14 @@ If your `ApplicationController` has a `current_user` method, PaperTrail will sto
|
|
146
147
|
>> last_change = Widget.versions.last
|
147
148
|
>> user_who_made_the_change = User.find last_change.whodunnit.to_i
|
148
149
|
|
150
|
+
You may want PaperTrail to call a different method to find out who is responsible. To do so, override the `user_for_paper_trail` method in your controller like this:
|
151
|
+
|
152
|
+
class ApplicationController
|
153
|
+
def user_for_paper_trail
|
154
|
+
logged_in? ? current_member : 'Public user' # or whatever
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
149
158
|
In a migration or in `script/console` you can set who is responsible like this:
|
150
159
|
|
151
160
|
>> PaperTrail.whodunnit = 'Andy Stewart'
|
@@ -155,7 +164,7 @@ In a migration or in `script/console` you can set who is responsible like this:
|
|
155
164
|
|
156
165
|
## Storing metadata
|
157
166
|
|
158
|
-
You can store arbitrary metadata alongside each version like this:
|
167
|
+
You can store arbitrary model-level metadata alongside each version like this:
|
159
168
|
|
160
169
|
class Article < ActiveRecord::Base
|
161
170
|
belongs_to :author
|
@@ -169,6 +178,16 @@ Why would you do this? In this example, `author_id` is an attribute of `Article
|
|
169
178
|
|
170
179
|
Version.all(:conditions => ['author_id = ?', author_id])
|
171
180
|
|
181
|
+
You can also store any information you like from your controller. Just override the `info_for_paper_trail` method in your controller to return a hash whose keys correspond to columns in your `versions` table. E.g.:
|
182
|
+
|
183
|
+
class ApplicationController
|
184
|
+
def info_for_paper_trail
|
185
|
+
{ :ip => request.remote_ip, :user_agent => request.user_agent }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
Remember to add those extra columns to your `versions` table ;)
|
190
|
+
|
172
191
|
|
173
192
|
## Turning PaperTrail Off/On
|
174
193
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.5.0
|
data/lib/paper_trail.rb
CHANGED
@@ -18,14 +18,10 @@ module PaperTrail
|
|
18
18
|
!!PaperTrail.config.enabled
|
19
19
|
end
|
20
20
|
|
21
|
-
# Returns PaperTrail's configuration object.
|
22
|
-
def self.config
|
23
|
-
@@config ||= PaperTrail::Config.instance
|
24
|
-
end
|
25
21
|
|
26
22
|
# Returns who is reponsible for any changes that occur.
|
27
23
|
def self.whodunnit
|
28
|
-
|
24
|
+
paper_trail_store[:whodunnit]
|
29
25
|
end
|
30
26
|
|
31
27
|
# Sets who is responsible for any changes that occur.
|
@@ -33,7 +29,36 @@ module PaperTrail
|
|
33
29
|
# when working with models directly. In a controller it is set
|
34
30
|
# automatically to the `current_user`.
|
35
31
|
def self.whodunnit=(value)
|
36
|
-
|
32
|
+
paper_trail_store[:whodunnit] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns any information from the controller that you want
|
36
|
+
# PaperTrail to store.
|
37
|
+
#
|
38
|
+
# See `PaperTrail::Controller#info_for_paper_trail`.
|
39
|
+
def self.controller_info
|
40
|
+
paper_trail_store[:controller_info]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sets any information from the controller that you want PaperTrail
|
44
|
+
# to store. By default this is set automatically by a before filter.
|
45
|
+
def self.controller_info=(value)
|
46
|
+
paper_trail_store[:controller_info] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Thread-safe hash to hold PaperTrail's data.
|
53
|
+
#
|
54
|
+
# TODO: is this a memory leak?
|
55
|
+
def self.paper_trail_store
|
56
|
+
Thread.current[:paper_trail] ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns PaperTrail's configuration object.
|
60
|
+
def self.config
|
61
|
+
@@config ||= PaperTrail::Config.instance
|
37
62
|
end
|
38
63
|
|
39
64
|
end
|
@@ -2,15 +2,59 @@ module PaperTrail
|
|
2
2
|
module Controller
|
3
3
|
|
4
4
|
def self.included(base)
|
5
|
-
base.before_filter :
|
5
|
+
base.before_filter :set_paper_trail_whodunnit
|
6
|
+
base.before_filter :set_paper_trail_controller_info
|
6
7
|
end
|
7
8
|
|
8
9
|
protected
|
9
10
|
|
10
|
-
#
|
11
|
-
# `current_user
|
11
|
+
# Returns the user who is responsible for any changes that occur.
|
12
|
+
# By default this calls `current_user` and returns the result.
|
13
|
+
#
|
14
|
+
# Override this method in your controller to call a different
|
15
|
+
# method, e.g. `current_person`, or anything you like.
|
16
|
+
def user_for_paper_trail
|
17
|
+
current_user rescue nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns any information about the controller or request that you
|
21
|
+
# want PaperTrail to store alongside any changes that occur. By
|
22
|
+
# default this returns an empty hash.
|
23
|
+
#
|
24
|
+
# Override this method in your controller to return a hash of any
|
25
|
+
# information you need. The hash's keys must correspond to columns
|
26
|
+
# in your `versions` table, so don't forget to add any new columns
|
27
|
+
# you need.
|
28
|
+
#
|
29
|
+
# For example:
|
30
|
+
#
|
31
|
+
# {:ip => request.remote_ip, :user_agent => request.user_agent}
|
32
|
+
#
|
33
|
+
# The columns `ip` and `user_agent` must exist in your `versions` # table.
|
34
|
+
#
|
35
|
+
# Use the `:meta` option to `PaperTrail::Model::ClassMethods.has_paper_trail`
|
36
|
+
# to store any extra model-level data you need.
|
37
|
+
def info_for_paper_trail
|
38
|
+
{}
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Tells PaperTrail who is responsible for any changes that occur.
|
44
|
+
def set_paper_trail_whodunnit
|
45
|
+
::PaperTrail.whodunnit = user_for_paper_trail
|
46
|
+
end
|
47
|
+
|
48
|
+
# DEPRECATED: please use `set_paper_trail_whodunnit` instead.
|
12
49
|
def set_whodunnit
|
13
|
-
|
50
|
+
logger.warn '[PaperTrail]: the `set_whodunnit` controller method has been deprecated. Please rename to `set_paper_trail_whodunnit`.'
|
51
|
+
set_paper_trail_whodunnit
|
52
|
+
end
|
53
|
+
|
54
|
+
# Tells PaperTrail any information from the controller you want
|
55
|
+
# to store alongside any changes that occur.
|
56
|
+
def set_paper_trail_controller_info
|
57
|
+
::PaperTrail.controller_info = info_for_paper_trail
|
14
58
|
end
|
15
59
|
|
16
60
|
end
|
@@ -7,11 +7,16 @@ module PaperTrail
|
|
7
7
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
+
|
11
|
+
# Declare this in your model to track every create, update, and destroy. Each version of
|
12
|
+
# the model is available in the `versions` association.
|
13
|
+
#
|
10
14
|
# Options:
|
11
|
-
# :ignore an array of attributes for which a new
|
12
|
-
# :meta a hash of extra data to store. You must add a column to the versions table for each key.
|
13
|
-
# Values are objects or procs (which are called with
|
14
|
-
# trail).
|
15
|
+
# :ignore an array of attributes for which a new `Version` will not be created if only they change.
|
16
|
+
# :meta a hash of extra data to store. You must add a column to the `versions` table for each key.
|
17
|
+
# Values are objects or procs (which are called with `self`, i.e. the model with the paper
|
18
|
+
# trail). See `PaperTrail::Controller.info_for_paper_trail` for how to store data from
|
19
|
+
# the controller.
|
15
20
|
def has_paper_trail(options = {})
|
16
21
|
send :include, InstanceMethods
|
17
22
|
|
@@ -83,10 +88,12 @@ module PaperTrail
|
|
83
88
|
private
|
84
89
|
|
85
90
|
def merge_metadata(data)
|
91
|
+
# First we merge the model-level metadata in `meta`.
|
86
92
|
meta.each do |k,v|
|
87
93
|
data[k] = v.respond_to?(:call) ? v.call(self) : v
|
88
94
|
end
|
89
|
-
data
|
95
|
+
# Second we merge any extra data from the controller (if available).
|
96
|
+
data.merge(PaperTrail.controller_info || {})
|
90
97
|
end
|
91
98
|
|
92
99
|
def previous_version
|
data/paper_trail.gemspec
CHANGED
@@ -13,6 +13,10 @@ class ApplicationController < ActionController::Base
|
|
13
13
|
def current_user
|
14
14
|
153
|
15
15
|
end
|
16
|
+
|
17
|
+
def info_for_paper_trail
|
18
|
+
{:ip => request.remote_ip, :user_agent => request.user_agent}
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
class WidgetsController < ApplicationController
|
@@ -37,11 +41,17 @@ end
|
|
37
41
|
class PaperTrailControllerTest < ActionController::TestCase
|
38
42
|
tests WidgetsController
|
39
43
|
|
44
|
+
def setup
|
45
|
+
@request.env['REMOTE_ADDR'] = '127.0.0.1'
|
46
|
+
end
|
47
|
+
|
40
48
|
test 'create' do
|
41
49
|
post :create, :widget => { :name => 'Flugel' }
|
42
50
|
widget = assigns(:widget)
|
43
51
|
assert_equal 1, widget.versions.length
|
44
52
|
assert_equal 153, widget.versions.last.whodunnit.to_i
|
53
|
+
assert_equal '127.0.0.1', widget.versions.last.ip
|
54
|
+
assert_equal 'Rails Testing', widget.versions.last.user_agent
|
45
55
|
end
|
46
56
|
|
47
57
|
test 'update' do
|
@@ -51,6 +61,8 @@ class PaperTrailControllerTest < ActionController::TestCase
|
|
51
61
|
widget = assigns(:widget)
|
52
62
|
assert_equal 2, widget.versions.length
|
53
63
|
assert_equal 153, widget.versions.last.whodunnit.to_i
|
64
|
+
assert_equal '127.0.0.1', widget.versions.last.ip
|
65
|
+
assert_equal 'Rails Testing', widget.versions.last.user_agent
|
54
66
|
end
|
55
67
|
|
56
68
|
test 'destroy' do
|
@@ -60,5 +72,7 @@ class PaperTrailControllerTest < ActionController::TestCase
|
|
60
72
|
widget = assigns(:widget)
|
61
73
|
assert_equal 2, widget.versions.length
|
62
74
|
assert_equal 153, widget.versions.last.whodunnit.to_i
|
75
|
+
assert_equal '127.0.0.1', widget.versions.last.ip
|
76
|
+
assert_equal 'Rails Testing', widget.versions.last.user_agent
|
63
77
|
end
|
64
78
|
end
|
data/test/schema.rb
CHANGED
data/test/test_helper.rb
CHANGED