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 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 if your controller has a `current_user` method.
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.4.6
1
+ 1.5.0
@@ -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
- Thread.current[:whodunnit]
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
- Thread.current[:whodunnit] = value
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 :set_whodunnit
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
- # Sets who is responsible for any changes that occur: the controller's
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
- ::PaperTrail.whodunnit = self.send :current_user rescue nil
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 +Version+ will not be created if only they change.
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 +self+, i.e. the model with the paper
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
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{paper_trail}
8
- s.version = "1.4.6"
8
+ s.version = "1.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Andy Stewart"]
@@ -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
@@ -27,6 +27,10 @@ ActiveRecord::Schema.define(:version => 0) do
27
27
  t.integer :answer
28
28
  t.string :question
29
29
  t.integer :article_id
30
+
31
+ # Controller info columns.
32
+ t.string :ip
33
+ t.string :user_agent
30
34
  end
31
35
  add_index :versions, [:item_type, :item_id]
32
36
 
@@ -34,4 +34,10 @@ class ActiveRecord::Base
34
34
  end
35
35
  end
36
36
 
37
+ class ActionController::Base
38
+ def logger
39
+ @logger ||= Logger.new(nil)
40
+ end
41
+ end
42
+
37
43
  load_schema
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paper_trail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.6
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Stewart