paper_trail 4.0.0.rc1 → 4.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -58
  3. data/README.md +528 -346
  4. data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +6 -1
  5. data/lib/generators/paper_trail/templates/create_versions.rb +8 -1
  6. data/lib/paper_trail.rb +2 -2
  7. data/lib/paper_trail/config.rb +15 -4
  8. data/lib/paper_trail/frameworks/active_record.rb +2 -3
  9. data/lib/paper_trail/has_paper_trail.rb +32 -13
  10. data/lib/paper_trail/version_concern.rb +1 -1
  11. data/lib/paper_trail/version_number.rb +1 -1
  12. data/paper_trail.gemspec +1 -1
  13. data/spec/models/gadget_spec.rb +1 -1
  14. data/spec/models/kitchen/banana_spec.rb +14 -0
  15. data/spec/models/not_on_update_spec.rb +19 -0
  16. data/spec/models/skipper_spec.rb +17 -0
  17. data/spec/models/version_spec.rb +5 -8
  18. data/spec/models/widget_spec.rb +5 -8
  19. data/test/dummy/app/models/kitchen/banana.rb +5 -0
  20. data/test/dummy/app/models/not_on_update.rb +4 -0
  21. data/test/dummy/app/models/skipper.rb +6 -0
  22. data/test/dummy/app/versions/kitchen/banana_version.rb +5 -0
  23. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +25 -0
  24. data/test/paper_trail_test.rb +7 -0
  25. metadata +18 -24
  26. data/test/dummy/public/404.html +0 -26
  27. data/test/dummy/public/422.html +0 -26
  28. data/test/dummy/public/500.html +0 -26
  29. data/test/dummy/public/favicon.ico +0 -0
  30. data/test/dummy/public/javascripts/application.js +0 -2
  31. data/test/dummy/public/javascripts/controls.js +0 -965
  32. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  33. data/test/dummy/public/javascripts/effects.js +0 -1123
  34. data/test/dummy/public/javascripts/rails.js +0 -175
  35. data/test/dummy/public/stylesheets/.gitkeep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4a505b4f0b1cb4242e1a8eeb291fa5b95df54de
4
- data.tar.gz: 26ef2a95bf0a15137d707b52b7a8f1aeff207b46
3
+ metadata.gz: ae826d8b61e5942f6eb328508ea052e2c2dbd266
4
+ data.tar.gz: 89a118c9f2a5abfec9c5540c4d380c6e6949cac1
5
5
  SHA512:
6
- metadata.gz: 24bf40f194ddd24066e4ff0737eb35a603cb8b0e54b14060315a0d5ad57a98b53b8aaecbf7a07d69d65d3ab1d99ac6cead49b0396560a4d2f8545c3580d70041
7
- data.tar.gz: f7efc50eab66db98cea15c876201ebe5773c7b5f64c0320f98743b607819a1f8084438096e9e3849b1b233d3b0c2e9f86114be07e8de72d09f18ed24c04175f4
6
+ metadata.gz: 3585236f12597ab00a9092b978094c241028bcb138f247dd0cd7d62f175f38eb627497188da97233984f575b074682bfea3835529cf6530b8f5aa2104123b8c0
7
+ data.tar.gz: 7cacf0e8421af9477b5838c6276adb83ac6c52f7de77cdc97719109bbbba6a42e6647ba22bacb3e0fcddfc3a884056ae418375e6cffd7d3b51643b76368707c9
data/CHANGELOG.md CHANGED
@@ -1,69 +1,111 @@
1
1
  ## 4.0.0 (Unreleased)
2
2
 
3
- ##### Breaking change: if you use a custom initializer for PaperTrail in conjunction with Rails, you will need to add this line of code to the beginning of it:
4
- ```ruby
5
- PaperTrail::Rails::Engine.eager_load!
6
- ```
3
+ ### Changed
7
4
 
8
- *Also*
5
+ - Using a Rails initializer to reopen PaperTrail::Version or otherwise extend
6
+ PaperTrail is no longer recommended. An alternative is described in the
7
+ readme. See https://github.com/airblade/paper_trail/pull/557 and
8
+ https://github.com/airblade/paper_trail/pull/492.
9
+ - If you depend on the `RSpec` or `Cucumber` helpers, you must
10
+ [require them in your test helper](https://github.com/airblade/paper_trail#testing).
9
11
 
10
- If you depend on the `RSpec` or `Cucumber` helpers, you will need to [manually load them into your test helper](https://github.com/airblade/paper_trail#testing).
12
+ ### Added
11
13
 
12
- - [#525](https://github.com/airblade/paper_trail/issues/525) / [#512](https://github.com/airblade/paper_trail/pull/512) -
14
+ - [#541](https://github.com/airblade/paper_trail/pull/541) -
15
+ `PaperTrail.config.enabled` should be Thread Safe
16
+ - [#525](https://github.com/airblade/paper_trail/issues/525) /
17
+ [#512](https://github.com/airblade/paper_trail/pull/512) -
13
18
  Support for virtual accessors and redefined setter and getter methods.
14
- - [#518](https://github.com/airblade/paper_trail/pull/518) - Support for querying against PostgreSQL's
15
- [`JSON` and `JSONB` column types](http://www.postgresql.org/docs/9.4/static/datatype-json.html) via
16
- `PaperTrail::VersionConcern#where_object` and `PaperTrail::VersionConcern#where_object_changes`
17
- - [#507](https://github.com/airblade/paper_trail/pull/507) - Support for opting out of saving changesets on models by choice
18
- when the `object_changes` column exists on the default `versions` table.
19
- - [#500](https://github.com/airblade/paper_trail/pull/500) - Support for passing `on: []` as an argument, with only manual
20
- versioning via calls to `touch_with_version`
21
- - [#494](https://github.com/airblade/paper_trail/issues/494) - The install generator will warn the user if the migration they are
22
- attempting to generate already exists.
19
+ - [#518](https://github.com/airblade/paper_trail/pull/518) - Support for
20
+ querying against PostgreSQL's
21
+ [`JSON` and `JSONB` column types](http://www.postgresql.org/docs/9.4/static/datatype-json.html)
22
+ via `PaperTrail::VersionConcern#where_object` and
23
+ `PaperTrail::VersionConcern#where_object_changes`
24
+ - [#507](https://github.com/airblade/paper_trail/pull/507) - Support for
25
+ opting out of saving changesets on models by choice when the
26
+ `object_changes` column exists on the default `versions` table.
27
+ - [#500](https://github.com/airblade/paper_trail/pull/500) - Support for
28
+ passing `on: []` as an argument, with only manual versioning via calls to
29
+ `touch_with_version`
30
+ - [#494](https://github.com/airblade/paper_trail/issues/494) - The install
31
+ generator will warn the user if the migration they are attempting to
32
+ generate already exists.
23
33
  - [#484](https://github.com/airblade/paper_trail/pull/484) - Support for
24
- [PostgreSQL's `JSONB` Type](http://www.postgresql.org/docs/9.4/static/datatype-json.html) for storing `object`
25
- and `object_changes`.
26
- - [#479](https://github.com/airblade/paper_trail/issues/479) - Deprecated `originator` method in favor of `paper_trail_originator`
27
- Deprecation warning informs users that the `originator` of the methods will be removed in version `4.0`
28
- - [#458](https://github.com/airblade/paper_trail/pull/458) - For `create` events, metadata pointing at attributes should attempt
29
- to grab the current value instead of looking at the value prior to the change (which would always be `nil`)
30
- - [#451](https://github.com/airblade/paper_trail/issues/451) - Fix `reify` method in context of model where the base class
31
- has a default scope, and the live instance is not scoped within that default scope
32
- - [#440](https://github.com/airblade/paper_trail/pull/440) - `versions` association should clear/reload after a transaction rollback.
33
- - [#439](https://github.com/airblade/paper_trail/pull/439) / [#12](https://github.com/airblade/paper_trail/issues/12) -
34
+ [PostgreSQL's `JSONB` Type](http://www.postgresql.org/docs/9.4/static/datatype-json.html)
35
+ for storing `object` and `object_changes`.
36
+ - [#479](https://github.com/airblade/paper_trail/issues/479) - Deprecated
37
+ `originator` method, use `paper_trail_originator`.
38
+ - [#458](https://github.com/airblade/paper_trail/pull/458) - For `create`
39
+ events, metadata pointing at attributes should attempt to grab the current
40
+ value instead of looking at the value prior to the change (which would
41
+ always be `nil`)
42
+ - [#451](https://github.com/airblade/paper_trail/issues/451) - Fix `reify`
43
+ method in context of model where the base class has a default scope, and the
44
+ live instance is not scoped within that default scope.
45
+ - [#440](https://github.com/airblade/paper_trail/pull/440) - `versions`
46
+ association should clear/reload after a transaction rollback.
47
+ - [#439](https://github.com/airblade/paper_trail/pull/439) /
48
+ [#12](https://github.com/airblade/paper_trail/issues/12) -
34
49
  Support for versioning of associations (Has Many, Has One, HABTM, etc.)
35
- - [#438](https://github.com/airblade/paper_trail/issues/438) - `ModelKlass.paper_trail_enabled_for_model?` should return `false` if
50
+ - [#438](https://github.com/airblade/paper_trail/issues/438) -
51
+ `ModelKlass.paper_trail_enabled_for_model?` should return `false` if
36
52
  `has_paper_trail` has not been declared on the class.
37
- - [#404](https://github.com/airblade/paper_trail/issues/404) / [#428](https://github.com/airblade/paper_trail/issues/428) -
38
- `model_instance.dup` does not need to be invoked when examining what the instance looked like before changes were persisted,
39
- which avoids issues if a 3rd party has overriden the `dup` behavior. Also fixes errors occuring when a user attempts to
40
- update the inheritance column on an STI model instance in `ActiveRecord` 4.1.x
41
- - [#427](https://github.com/airblade/paper_trail/pull/427) - Fix `reify` method in context of model where a column has been removed.
42
- - [#420](https://github.com/airblade/paper_trail/issues/420) - Add `VersionConcern#where_object_changes` instance method;
43
- acts as a helper for querying against the `object_changes` column in versions table.
44
- - [#416](https://github.com/airblade/paper_trail/issues/416) - Added a `config` option for enabling/disabling
45
- utilization of `serialized_attributes` for `ActiveRecord`, necessary because `serialized_attributes` has been
46
- deprecated in `ActiveRecord` version `4.2` and will be removed in version `5.0`
47
- - [#414](https://github.com/airblade/paper_trail/issues/414) - Fix functionality `ignore` argument to `has_paper_trail`
48
- in `ActiveRecord` 4.
49
- - [#413](https://github.com/airblade/paper_trail/issues/413) - Utilize [RequestStore](https://github.com/steveklabnik/request_store)
50
- to ensure that the `PaperTrail.whodunnit` is set in a thread safe manner within Rails & Sinatra.
51
- - [#399](https://github.com/airblade/paper_trail/pull/399) - Add `:dup` argument for options hash to `reify` which forces a new model instance.
52
- - [#394](https://github.com/airblade/paper_trail/pull/394) - Add RSpec matcher `have_a_version_with` for easier testing.
53
- - [#391](https://github.com/airblade/paper_trail/issues/391) - `object_changes` value should dump to `YAML` as a normal `Hash`
54
- instead of an `ActiveSupport::HashWithIndifferentAccess`.
55
- - [#381](https://github.com/airblade/paper_trail/issues/381) - `Rspec` and `Cucumber` helpers should not be loaded by
56
- default, regardless of whether those libraries are loaded.
57
- - [#375](https://github.com/airblade/paper_trail/pull/375) / [#374](https://github.com/airblade/paper_trail/issues/374) /
58
- [#354](https://github.com/airblade/paper_trail/issues/354) / [#131](https://github.com/airblade/paper_trail/issues/131) -
59
- Versions should be built with `after_` callbacks so the timestamp field for a version can be forced to match the
60
- corresponding timestamp in the database for the state persistence of a change to the base (versioned) model.
61
- - [#347](https://github.com/airblade/paper_trail/pull/347) - Autoload `ActiveRecord` models in via a `Rails::Engine` when
62
- the gem is used with `Rails`.
63
- - Methods handling serialized attributes should fallback to the currently set Serializer instead of always falling back
64
- to `PaperTrail::Serializers::YAML`.
65
- - Both `PaperTrail.config` and `PaperTrail.configure` are now identical, and will both return the `PaperTrail::Config`
66
- instance and also yield it if a block is provided.
53
+ - [#404](https://github.com/airblade/paper_trail/issues/404) /
54
+ [#428](https://github.com/airblade/paper_trail/issues/428) -
55
+ `model_instance.dup` does not need to be invoked when examining what the
56
+ instance looked like before changes were persisted, which avoids issues if a
57
+ 3rd party has overriden the `dup` behavior. Also fixes errors occuring when
58
+ a user attempts to update the inheritance column on an STI model instance in
59
+ `ActiveRecord` 4.1.x
60
+ - [#427](https://github.com/airblade/paper_trail/pull/427) - Fix `reify`
61
+ method in context of model where a column has been removed.
62
+ - [#420](https://github.com/airblade/paper_trail/issues/420) - Add
63
+ `VersionConcern#where_object_changes` instance method; acts as a helper for
64
+ querying against the `object_changes` column in versions table.
65
+ - [#416](https://github.com/airblade/paper_trail/issues/416) - Added a
66
+ `config` option for enabling/disabling utilization of
67
+ `serialized_attributes` for `ActiveRecord`, necessary because
68
+ `serialized_attributes` has been deprecated in `ActiveRecord` version `4.2`
69
+ and will be removed in version `5.0`
70
+ - [#414](https://github.com/airblade/paper_trail/issues/414) - Fix
71
+ functionality `ignore` argument to `has_paper_trail` in `ActiveRecord` 4.
72
+ - [#413](https://github.com/airblade/paper_trail/issues/413) - Utilize
73
+ [RequestStore](https://github.com/steveklabnik/request_store) to ensure that
74
+ the `PaperTrail.whodunnit` is set in a thread safe manner within Rails and
75
+ Sinatra.
76
+ - [#399](https://github.com/airblade/paper_trail/pull/399) - Add `:dup`
77
+ argument for options hash to `reify` which forces a new model instance.
78
+ - [#394](https://github.com/airblade/paper_trail/pull/394) - Add RSpec matcher
79
+ `have_a_version_with` for easier testing.
80
+ - [#391](https://github.com/airblade/paper_trail/issues/391) - `object_changes`
81
+ value should dump to `YAML` as a normal `Hash` instead of an
82
+ `ActiveSupport::HashWithIndifferentAccess`.
83
+ - [#381](https://github.com/airblade/paper_trail/issues/381) - `Rspec`
84
+ and `Cucumber` helpers should not be loaded by default, regardless of
85
+ whether those libraries are loaded.
86
+ - [#375](https://github.com/airblade/paper_trail/pull/375) /
87
+ [#374](https://github.com/airblade/paper_trail/issues/374) /
88
+ [#354](https://github.com/airblade/paper_trail/issues/354) /
89
+ [#131](https://github.com/airblade/paper_trail/issues/131) -
90
+ Versions should be built with `after_` callbacks so the timestamp field for
91
+ a version can be forced to match the corresponding timestamp in the database
92
+ for the state persistence of a change to the base (versioned) model.
93
+ - [#347](https://github.com/airblade/paper_trail/pull/347) - Autoload
94
+ `ActiveRecord` models in via a `Rails::Engine` when the gem is used with
95
+ `Rails`.
96
+ - Methods handling serialized attributes should fallback to the currently set
97
+ Serializer instead of always falling back to `PaperTrail::Serializers::YAML`.
98
+ - Both `PaperTrail.config` and `PaperTrail.configure` are now identical, and
99
+ will both return the `PaperTrail::Config` instance and also yield it if a
100
+ block is provided.
101
+
102
+ ### Fixed
103
+
104
+ - [#563](https://github.com/airblade/paper_trail/pull/563) - Fixed a bug in
105
+ `touch_with_version` so that it will still create a version even when the
106
+ `on` option is, e.g. `[:create]`.
107
+ - [#248](https://github.com/airblade/paper_trail/issues/248) - In MySQL, to
108
+ prevent truncation, generated migrations now use `longtext` instead of `text`.
67
109
 
68
110
  ## 3.0.8
69
111
 
@@ -168,7 +210,7 @@ in the `PaperTrail::Version` class through a `Rails::Engine` when the gem is use
168
210
  - [#216](https://github.com/airblade/paper_trail/pull/216) - Added helper & extension for [RSpec](https://github.com/rspec/rspec),
169
211
  and helper for [Cucumber](http://cukes.info).
170
212
  - [#212](https://github.com/airblade/paper_trail/pull/212) - Added `PaperTrail::Cleaner` module, useful for discarding draft versions.
171
- - [#207](https://github.com/airblade/paper_trail/issues/207) - Versions for `'create'` events are now created with `create!` instead of
213
+ - [#207](https://github.com/airblade/paper_trail/issues/207) - Versions for `'create'` events are now created with `create!` instead of
172
214
  `create` so that an exception gets raised if it is appropriate to do so.
173
215
  - [#199](https://github.com/airblade/paper_trail/pull/199) - Rails 4 compatibility.
174
216
  - [#165](https://github.com/airblade/paper_trail/pull/165) - Namespaced the `Version` class under the `PaperTrail` module.
data/README.md CHANGED
@@ -1,8 +1,30 @@
1
- # PaperTrail [![Build Status](https://img.shields.io/travis/airblade/paper_trail/master.svg)](https://travis-ci.org/airblade/paper_trail) [![Dependency Status](https://img.shields.io/gemnasium/airblade/paper_trail.svg)](https://gemnasium.com/airblade/paper_trail)
2
-
3
- PaperTrail lets you track changes to your models' data. It's good for auditing or versioning. You can see how a model looked at any stage in its lifecycle, revert it to any version, and even undelete it after it's been destroyed.
4
-
5
- There's an excellent [RailsCast on implementing Undo with Paper Trail](http://railscasts.com/episodes/255-undo-with-paper-trail).
1
+ # PaperTrail [![Build Status][4]][5] [![Dependency Status][6]][7]
2
+
3
+ PaperTrail lets you track changes to your models' data. It's good for auditing
4
+ or versioning. You can see how a model looked at any stage in its lifecycle,
5
+ revert it to any version, and even undelete it after it's been destroyed.
6
+
7
+ - [Features](#features)
8
+ - [Compatibility](#compatibility)
9
+ - [Installation](#installation)
10
+ - [API Summary](#api-summary)
11
+ - [Basic Usage](#basic-usage)
12
+ - [Choosing Lifecycle Events To Monitor](#choosing-lifecycle-events-to-monitor)
13
+ - [Choosing When To Save New Versions](#choosing-when-to-save-new-versions)
14
+ - [Choosing Attributes To Monitor](#choosing-attributes-to-monitor)
15
+ - [Reverting And Undeleting A Model](#reverting-and-undeleting-a-model)
16
+ - [Navigating Versions](#navigating-versions)
17
+ - [Finding Out Who Was Responsible For A Change](#finding-out-who-was-responsible-for-a-change)
18
+ - [Custom Version Classes](#custom-version-classes)
19
+ - [Associations](#associations)
20
+ - [Storing metadata](#storing-metadata)
21
+ - [Diffing Versions](#diffing-versions)
22
+ - [Turning PaperTrail Off/On](#turning-papertrail-offon)
23
+ - [Using a custom serializer](#using-a-custom-serializer)
24
+ - [SerializedAttributes support](#serializedattributes-support)
25
+ - [Limiting the Number of Versions Created](#limiting-the-number-of-versions-created)
26
+ - [Deleting Old Versions](#deleting-old-versions)
27
+ - [Testing](#testing)
6
28
 
7
29
  ## Features
8
30
 
@@ -32,11 +54,11 @@ There's an excellent [RailsCast on implementing Undo with Paper Trail](http://ra
32
54
 
33
55
  Works with `ActiveRecord` 3+. Note: this code is on the `master` branch and tagged `v4.x`.
34
56
 
35
- Version 3 is on the branch named [`3.0-stable`](https://github.com/airblade/paper_trail/tree/3.0-stable) and is tagged `v3.x`, and works ActiveRecord 4 and ActiveRecord 3.
57
+ Version 3 is on the branch named [`3.0-stable`][9] and is tagged `v3.x`, and works ActiveRecord 4 and ActiveRecord 3.
36
58
 
37
- Version 2 is on the branch named [`2.7-stable`](https://github.com/airblade/paper_trail/tree/2.7-stable) and is tagged `v2.x`, and works with Rails 3.
59
+ Version 2 is on the branch named [`2.7-stable`][10] and is tagged `v2.x`, and works with Rails 3.
38
60
 
39
- The Rails 2.3 code is on the [`rails2`](https://github.com/airblade/paper_trail/tree/rails2) branch and tagged `v1.x`. These branches are both stable with their respective versions of Rails but will not have new features added/backported to them.
61
+ The Rails 2.3 code is on the [`rails2`][11] branch and tagged `v1.x`. These branches are both stable with their respective versions of Rails but will not have new features added/backported to them.
40
62
 
41
63
  ## Installation
42
64
 
@@ -58,11 +80,12 @@ The Rails 2.3 code is on the [`rails2`](https://github.com/airblade/paper_trail/
58
80
 
59
81
  ### Sinatra
60
82
 
61
- In order to configure PaperTrail for usage with [Sinatra](http://www.sinatrarb.com),
62
- your `Sinatra` app must be using `ActiveRecord` 3 or 4. It is also recommended to use the
63
- [Sinatra ActiveRecord Extension](https://github.com/janko-m/sinatra-activerecord) or something similar for managing
64
- your applications `ActiveRecord` connection in a manner similar to the way `Rails` does. If using the aforementioned
65
- `Sinatra ActiveRecord Extension`, steps for setting up your app with PaperTrail will look something like this:
83
+ In order to configure PaperTrail for usage with [Sinatra][12], your `Sinatra`
84
+ app must be using `ActiveRecord` 3 or 4. It is also recommended to use the
85
+ [Sinatra ActiveRecord Extension][13] or something similar for managing your
86
+ applications `ActiveRecord` connection in a manner similar to the way `Rails`
87
+ does. If using the aforementioned `Sinatra ActiveRecord Extension`, steps for
88
+ setting up your app with PaperTrail will look something like this:
66
89
 
67
90
  1. Add PaperTrail to your `Gemfile`.
68
91
 
@@ -72,7 +95,7 @@ your applications `ActiveRecord` connection in a manner similar to the way `Rail
72
95
 
73
96
  `bundle exec rake db:create_migration NAME=create_versions`
74
97
 
75
- 3. Copy contents of [create_versions.rb](https://raw.github.com/airblade/paper_trail/master/lib/generators/paper_trail/templates/create_versions.rb)
98
+ 3. Copy contents of [create_versions.rb][14]
76
99
  into the `create_versions` migration that was generated into your `db/migrate` directory.
77
100
 
78
101
  4. Run the migration.
@@ -82,11 +105,15 @@ into the `create_versions` migration that was generated into your `db/migrate` d
82
105
  5. Add `has_paper_trail` to the models you want to track.
83
106
 
84
107
 
85
- PaperTrail provides a helper extension that acts similar to the controller mixin it provides for `Rails` applications.
108
+ PaperTrail provides a helper extension that acts similar to the controller mixin
109
+ it provides for `Rails` applications.
86
110
 
87
- It will set `PaperTrail.whodunnit` to whatever is returned by a method named `user_for_paper_trail` which you can define inside your Sinatra Application. (by default it attempts to invoke a method named `current_user`)
111
+ It will set `PaperTrail.whodunnit` to whatever is returned by a method named
112
+ `user_for_paper_trail` which you can define inside your Sinatra Application. (by
113
+ default it attempts to invoke a method named `current_user`)
88
114
 
89
- If you're using the modular [`Sinatra::Base`](http://www.sinatrarb.com/intro.html#Modular%20vs.%20Classic%20Style) style of application, you will need to register the extension:
115
+ If you're using the modular [`Sinatra::Base`][15] style of application, you will
116
+ need to register the extension:
90
117
 
91
118
  ```ruby
92
119
  # bleh_app.rb
@@ -195,7 +222,8 @@ info_for_paper_trail
195
222
 
196
223
  ## Basic Usage
197
224
 
198
- PaperTrail is simple to use. Just add 15 characters to a model to get a paper trail of every `create`, `update`, and `destroy`.
225
+ PaperTrail is simple to use. Just add 15 characters to a model to get a paper
226
+ trail of every `create`, `update`, and `destroy`.
199
227
 
200
228
  ```ruby
201
229
  class Widget < ActiveRecord::Base
@@ -203,41 +231,49 @@ class Widget < ActiveRecord::Base
203
231
  end
204
232
  ```
205
233
 
206
- This gives you a `versions` method which returns the paper trail of changes to your model.
234
+ This gives you a `versions` method which returns the paper trail of changes to
235
+ your model.
207
236
 
208
237
  ```ruby
209
- >> widget = Widget.find 42
210
- >> widget.versions # [<PaperTrail::Version>, <PaperTrail::Version>, ...]
238
+ widget = Widget.find 42
239
+ widget.versions
240
+ # [<PaperTrail::Version>, <PaperTrail::Version>, ...]
211
241
  ```
212
242
 
213
243
  Once you have a version, you can find out what happened:
214
244
 
215
245
  ```ruby
216
- >> v = widget.versions.last
217
- >> v.event # 'update' (or 'create' or 'destroy')
218
- >> v.whodunnit # '153' (if the update was via a controller and
219
- # the controller has a current_user method,
220
- # here returning the id of the current user)
221
- >> v.created_at # when the update occurred
222
- >> widget = v.reify # the widget as it was before the update;
223
- # would be nil for a create event
246
+ v = widget.versions.last
247
+ v.event # 'update' (or 'create' or 'destroy')
248
+ v.whodunnit # '153' (if the update was via a controller and
249
+ # the controller has a current_user method,
250
+ # here returning the id of the current user)
251
+ v.created_at # when the update occurred
252
+ widget = v.reify # the widget as it was before the update;
253
+ # would be nil for a create event
224
254
  ```
225
255
 
226
- PaperTrail stores the pre-change version of the model, unlike some other auditing/versioning plugins, so you can retrieve the original version. This is useful when you start keeping a paper trail for models that already have records in the database.
256
+ PaperTrail stores the pre-change version of the model, unlike some other
257
+ auditing/versioning plugins, so you can retrieve the original version. This is
258
+ useful when you start keeping a paper trail for models that already have records
259
+ in the database.
227
260
 
228
261
  ```ruby
229
- >> widget = Widget.find 153
230
- >> widget.name # 'Doobly'
262
+ widget = Widget.find 153
263
+ widget.name # 'Doobly'
231
264
 
232
265
  # Add has_paper_trail to Widget model.
233
266
 
234
- >> widget.versions # []
235
- >> widget.update_attributes :name => 'Wotsit'
236
- >> widget.versions.last.reify.name # 'Doobly'
237
- >> widget.versions.last.event # 'update'
267
+ widget.versions # []
268
+ widget.update_attributes :name => 'Wotsit'
269
+ widget.versions.last.reify.name # 'Doobly'
270
+ widget.versions.last.event # 'update'
238
271
  ```
239
272
 
240
- This also means that PaperTrail does not waste space storing a version of the object as it currently stands. The `versions` method gives you previous versions; to get the current one just call a finder on your `Widget` model as usual.
273
+ This also means that PaperTrail does not waste space storing a version of the
274
+ object as it currently stands. The `versions` method gives you previous
275
+ versions; to get the current one just call a finder on your `Widget` model as
276
+ usual.
241
277
 
242
278
  Here's a helpful table showing what PaperTrail stores:
243
279
 
@@ -263,12 +299,14 @@ Here's a helpful table showing what PaperTrail stores:
263
299
  </tr>
264
300
  </table>
265
301
 
266
- PaperTrail stores the values in the Model Before column. Most other auditing/versioning plugins store the After column.
302
+ PaperTrail stores the values in the Model Before column. Most other
303
+ auditing/versioning plugins store the After column.
267
304
 
268
305
 
269
306
  ## Choosing Lifecycle Events To Monitor
270
307
 
271
- You can choose which events to track with the `on` option. For example, to ignore `create` events:
308
+ You can choose which events to track with the `on` option. For example, to
309
+ ignore `create` events:
272
310
 
273
311
  ```ruby
274
312
  class Article < ActiveRecord::Base
@@ -276,27 +314,31 @@ class Article < ActiveRecord::Base
276
314
  end
277
315
  ```
278
316
 
279
- You may also have the `PaperTrail::Version` model save a custom string in it's `event` field instead of the typical `create`, `update`, `destroy`.
280
- PaperTrail supplies a custom accessor method called `paper_trail_event`, which it will attempt to use to fill the `event` field before
281
- falling back on one of the default events.
317
+ You may also have the `PaperTrail::Version` model save a custom string in it's
318
+ `event` field instead of the typical `create`, `update`, `destroy`. PaperTrail
319
+ supplies a custom accessor method called `paper_trail_event`, which it will
320
+ attempt to use to fill the `event` field before falling back on one of the
321
+ default events.
282
322
 
283
323
  ```ruby
284
- >> a = Article.create
285
- >> a.versions.size # 1
286
- >> a.versions.last.event # 'create'
287
- >> a.paper_trail_event = 'update title'
288
- >> a.update_attributes :title => 'My Title'
289
- >> a.versions.size # 2
290
- >> a.versions.last.event # 'update title'
291
- >> a.paper_trail_event = nil
292
- >> a.update_attributes :title => "Alternate"
293
- >> a.versions.size # 3
294
- >> a.versions.last.event # 'update'
324
+ a = Article.create
325
+ a.versions.size # 1
326
+ a.versions.last.event # 'create'
327
+ a.paper_trail_event = 'update title'
328
+ a.update_attributes :title => 'My Title'
329
+ a.versions.size # 2
330
+ a.versions.last.event # 'update title'
331
+ a.paper_trail_event = nil
332
+ a.update_attributes :title => "Alternate"
333
+ a.versions.size # 3
334
+ a.versions.last.event # 'update'
295
335
  ```
296
336
 
297
337
  ## Choosing When To Save New Versions
298
338
 
299
- You can choose the conditions when to add new versions with the `if` and `unless` options. For example, to save versions only for US non-draft translations:
339
+ You can choose the conditions when to add new versions with the `if` and
340
+ `unless` options. For example, to save versions only for US non-draft
341
+ translations:
300
342
 
301
343
  ```ruby
302
344
  class Translation < ActiveRecord::Base
@@ -316,16 +358,19 @@ class Article < ActiveRecord::Base
316
358
  end
317
359
  ```
318
360
 
319
- This means that changes to just the `title` or `rating` will not store another version of the article. It does not mean that the `title` and `rating` attributes will be ignored if some other change causes a new `PaperTrail::Version` to be created. For example:
361
+ This means that changes to just the `title` or `rating` will not store another
362
+ version of the article. It does not mean that the `title` and `rating`
363
+ attributes will be ignored if some other change causes a new
364
+ `PaperTrail::Version` to be created. For example:
320
365
 
321
366
  ```ruby
322
- >> a = Article.create
323
- >> a.versions.length # 1
324
- >> a.update_attributes :title => 'My Title', :rating => 3
325
- >> a.versions.length # 1
326
- >> a.update_attributes :title => 'Greeting', :content => 'Hello'
327
- >> a.versions.length # 2
328
- >> a.previous_version.title # 'My Title'
367
+ a = Article.create
368
+ a.versions.length # 1
369
+ a.update_attributes :title => 'My Title', :rating => 3
370
+ a.versions.length # 1
371
+ a.update_attributes :title => 'Greeting', :content => 'Hello'
372
+ a.versions.length # 2
373
+ a.previous_version.title # 'My Title'
329
374
  ```
330
375
 
331
376
  Or, you can specify a list of all attributes you care about:
@@ -339,13 +384,13 @@ end
339
384
  This means that only changes to the `title` will save a version of the article:
340
385
 
341
386
  ```ruby
342
- >> a = Article.create
343
- >> a.versions.length # 1
344
- >> a.update_attributes :title => 'My Title'
345
- >> a.versions.length # 2
346
- >> a.update_attributes :content => 'Hello'
347
- >> a.versions.length # 2
348
- >> a.previous_version.content # nil
387
+ a = Article.create
388
+ a.versions.length # 1
389
+ a.update_attributes :title => 'My Title'
390
+ a.versions.length # 2
391
+ a.update_attributes :content => 'Hello'
392
+ a.versions.length # 2
393
+ a.previous_version.content # nil
349
394
  ```
350
395
 
351
396
  The `:ignore` and `:only` options can also accept `Hash` arguments, where the :
@@ -356,26 +401,31 @@ class Article < ActiveRecord::Base
356
401
  end
357
402
  ```
358
403
 
359
- This means that if the `title` is not blank, then only changes to the `title` will save a version of the article:
404
+ This means that if the `title` is not blank, then only changes to the `title`
405
+ will save a version of the article:
360
406
 
361
407
  ```ruby
362
- >> a = Article.create
363
- >> a.versions.length # 1
364
- >> a.update_attributes :content => 'Hello'
365
- >> a.versions.length # 2
366
- >> a.update_attributes :title => 'My Title'
367
- >> a.versions.length # 3
368
- >> a.update_attributes :content => 'Hai'
369
- >> a.versions.length # 3
370
- >> a.previous_version.content # "Hello"
371
- >> a.update_attributes :title => 'Dif Title'
372
- >> a.versions.length # 4
373
- >> a.previous_version.content # "Hai"
408
+ a = Article.create
409
+ a.versions.length # 1
410
+ a.update_attributes :content => 'Hello'
411
+ a.versions.length # 2
412
+ a.update_attributes :title => 'My Title'
413
+ a.versions.length # 3
414
+ a.update_attributes :content => 'Hai'
415
+ a.versions.length # 3
416
+ a.previous_version.content # "Hello"
417
+ a.update_attributes :title => 'Dif Title'
418
+ a.versions.length # 4
419
+ a.previous_version.content # "Hai"
374
420
  ```
375
421
 
376
- Passing both `:ignore` and `:only` options will result in the article being saved if a changed attribute is included in `:only` but not in `:ignore`.
422
+ Passing both `:ignore` and `:only` options will result in the article being
423
+ saved if a changed attribute is included in `:only` but not in `:ignore`.
377
424
 
378
- You can skip fields altogether with the `:skip` option. As with `:ignore`, updates to these fields will not create a new `PaperTrail::Version`. In addition, these fields will not be included in the serialized version of the object whenever a new `PaperTrail::Version` is created.
425
+ You can skip fields altogether with the `:skip` option. As with `:ignore`,
426
+ updates to these fields will not create a new `PaperTrail::Version`. In
427
+ addition, these fields will not be included in the serialized version of the
428
+ object whenever a new `PaperTrail::Version` is created.
379
429
 
380
430
  For example:
381
431
 
@@ -390,97 +440,103 @@ end
390
440
  PaperTrail makes reverting to a previous version easy:
391
441
 
392
442
  ```ruby
393
- >> widget = Widget.find 42
394
- >> widget.update_attributes :name => 'Blah blah'
443
+ widget = Widget.find 42
444
+ widget.update_attributes :name => 'Blah blah'
395
445
  # Time passes....
396
- >> widget = widget.previous_version # the widget as it was before the update
397
- >> widget.save # reverted
446
+ widget = widget.previous_version # the widget as it was before the update
447
+ widget.save # reverted
398
448
  ```
399
449
 
400
450
  Alternatively you can find the version at a given time:
401
451
 
402
452
  ```ruby
403
- >> widget = widget.version_at(1.day.ago) # the widget as it was one day ago
404
- >> widget.save # reverted
453
+ widget = widget.version_at(1.day.ago) # the widget as it was one day ago
454
+ widget.save # reverted
405
455
  ```
406
456
 
407
- Note `version_at` gives you the object, not a version, so you don't need to call `reify`.
457
+ Note `version_at` gives you the object, not a version, so you don't need to call
458
+ `reify`.
408
459
 
409
460
  Undeleting is just as simple:
410
461
 
411
462
  ```ruby
412
- >> widget = Widget.find 42
413
- >> widget.destroy
463
+ widget = Widget.find 42
464
+ widget.destroy
414
465
  # Time passes....
415
- >> widget = PaperTrail::Version.find(153).reify # the widget as it was before destruction
416
- >> widget.save # the widget lives!
466
+ widget = PaperTrail::Version.find(153).reify # the widget as it was before destruction
467
+ widget.save # the widget lives!
417
468
  ```
418
469
 
419
- In fact you could use PaperTrail to implement an undo system, though I haven't had the opportunity yet to do it myself. However [Ryan Bates has](http://railscasts.com/episodes/255-undo-with-paper-trail)!
470
+ You could even use PaperTrail to implement an undo system, [Ryan Bates has!][3]
420
471
 
472
+ If your model uses [optimistic locking][1] don't forget to [increment your
473
+ `lock_version`][2] before saving or you'll get a `StaleObjectError`.
421
474
 
422
475
  ## Navigating Versions
423
476
 
424
- You can call `previous_version` and `next_version` on an item to get it as it was/became. Note that these methods reify the item for you.
477
+ You can call `previous_version` and `next_version` on an item to get it as it
478
+ was/became. Note that these methods reify the item for you.
425
479
 
426
480
  ```ruby
427
- >> live_widget = Widget.find 42
428
- >> live_widget.versions.length # 4 for example
429
- >> widget = live_widget.previous_version # => widget == live_widget.versions.last.reify
430
- >> widget = widget.previous_version # => widget == live_widget.versions[-2].reify
431
- >> widget = widget.next_version # => widget == live_widget.versions.last.reify
432
- >> widget.next_version # live_widget
481
+ live_widget = Widget.find 42
482
+ live_widget.versions.length # 4 for example
483
+ widget = live_widget.previous_version # => widget == live_widget.versions.last.reify
484
+ widget = widget.previous_version # => widget == live_widget.versions[-2].reify
485
+ widget = widget.next_version # => widget == live_widget.versions.last.reify
486
+ widget.next_version # live_widget
433
487
  ```
434
488
 
435
- If instead you have a particular `version` of an item you can navigate to the previous and next versions.
489
+ If instead you have a particular `version` of an item you can navigate to the
490
+ previous and next versions.
436
491
 
437
492
  ```ruby
438
- >> widget = Widget.find 42
439
- >> version = widget.versions[-2] # assuming widget has several versions
440
- >> previous = version.previous
441
- >> next = version.next
493
+ widget = Widget.find 42
494
+ version = widget.versions[-2] # assuming widget has several versions
495
+ previous = version.previous
496
+ next = version.next
442
497
  ```
443
498
 
444
499
  You can find out which of an item's versions yours is:
445
500
 
446
501
  ```ruby
447
- >> current_version_number = version.index # 0-based
502
+ current_version_number = version.index # 0-based
448
503
  ```
449
504
 
450
- If you got an item by reifying one of its versions, you can navigate back to the version it came from:
505
+ If you got an item by reifying one of its versions, you can navigate back to the
506
+ version it came from:
451
507
 
452
508
  ```ruby
453
- >> latest_version = Widget.find(42).versions.last
454
- >> widget = latest_version.reify
455
- >> widget.version == latest_version # true
509
+ latest_version = Widget.find(42).versions.last
510
+ widget = latest_version.reify
511
+ widget.version == latest_version # true
456
512
  ```
457
513
 
458
- You can find out whether a model instance is the current, live one -- or whether it came instead from a previous version -- with `live?`:
514
+ You can find out whether a model instance is the current, live one -- or whether
515
+ it came instead from a previous version -- with `live?`:
459
516
 
460
517
  ```ruby
461
- >> widget = Widget.find 42
462
- >> widget.live? # true
463
- >> widget = widget.previous_version
464
- >> widget.live? # false
518
+ widget = Widget.find 42
519
+ widget.live? # true
520
+ widget = widget.previous_version
521
+ widget.live? # false
465
522
  ```
466
523
 
467
524
  And you can perform `WHERE` queries for object versions based on attributes:
468
525
 
469
526
  ```ruby
470
- >> # All versions that meet these criteria.
471
- >> PaperTrail::Version.where_object(content: "Hello", title: "Article")
527
+ # All versions that meet these criteria.
528
+ PaperTrail::Version.where_object(content: "Hello", title: "Article")
472
529
  ```
473
530
 
474
531
  ## Finding Out Who Was Responsible For A Change
475
532
 
476
- If your `ApplicationController` has a `current_user` method, PaperTrail will attempt to store the value returned by `current_user.id` in the version's `whodunnit` column (if it returns). Note that this column is of type `String`, so you will have to convert it to an integer if it's an id and you want to look up the user later on:
477
-
478
- ```ruby
479
- >> last_change = widget.versions.last
480
- >> user_who_made_the_change = User.find last_change.whodunnit.to_i
481
- ```
533
+ If your `ApplicationController` has a `current_user` method, PaperTrail will
534
+ attempt to store the value returned by `current_user.id` in the version's
535
+ `whodunnit` column.
482
536
 
483
- 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:
537
+ You may want PaperTrail to call a different method to find out who is
538
+ responsible. To do so, override the `user_for_paper_trail` method in your
539
+ controller like this:
484
540
 
485
541
  ```ruby
486
542
  class ApplicationController
@@ -493,59 +549,54 @@ end
493
549
  In a console session you can manually set who is responsible like this:
494
550
 
495
551
  ```ruby
496
- >> PaperTrail.whodunnit = 'Andy Stewart'
497
- >> widget.update_attributes :name => 'Wibble'
498
- >> widget.versions.last.whodunnit # Andy Stewart
552
+ PaperTrail.whodunnit = 'Andy Stewart'
553
+ widget.update_attributes :name => 'Wibble'
554
+ widget.versions.last.whodunnit # Andy Stewart
499
555
  ```
500
556
 
501
- You can avoid having to do this manually by setting your initializer to pick up the username of the current user from the OS, like this:
502
-
503
- ```ruby
504
- # config/initializers/paper_trail.rb
557
+ See also: [Setting whodunnit in the rails console][33]
505
558
 
506
- # the following line is required for PaperTrail >= 4.0.0 with Rails
507
- PaperTrail::Rails::Engine.eager_load!
508
-
509
- if defined?(::Rails::Console)
510
- PaperTrail.whodunnit = "#{`whoami`.strip}: console"
511
- elsif File.basename($0) == "rake"
512
- PaperTrail.whodunnit = "#{`whoami`.strip}: rake #{ARGV.join ' '}"
513
- end
514
- ```
515
-
516
- Sometimes you want to define who is responsible for a change in a small scope without overwriting value of `PaperTrail.whodunnit`. It is possible to define the `whodunnit` value for an operation inside a block like this:
559
+ Sometimes you want to define who is responsible for a change in a small scope
560
+ without overwriting value of `PaperTrail.whodunnit`. It is possible to define
561
+ the `whodunnit` value for an operation inside a block like this:
517
562
 
518
563
  ```ruby
519
- >> PaperTrail.whodunnit = 'Andy Stewart'
520
- >> widget.whodunnit('Lucas Souza') do
521
- >> widget.update_attributes :name => 'Wibble'
522
- >> end
523
- >> widget.versions.last.whodunnit # Lucas Souza
524
- >> widget.update_attributes :name => 'Clair'
525
- >> widget.versions.last.whodunnit # Andy Stewart
526
- >> widget.whodunnit('Ben Atkins') { |w| w.update_attributes :name => 'Beth' } # this syntax also works
527
- >> widget.versions.last.whodunnit # Ben Atkins
528
- ```
529
-
530
- A version's `whodunnit` records who changed the object causing the `version` to be stored. Because a version stores the object as it looked before the change (see the table above), `whodunnit` returns who stopped the object looking like this -- not who made it look like this. Hence `whodunnit` is aliased as `terminator`.
531
-
532
- To find out who made a version's object look that way, use `version.paper_trail_originator`. And to find out who made a "live" object look like it does, call `paper_trail_originator` on the object.
533
-
534
- ```ruby
535
- >> widget = Widget.find 153 # assume widget has 0 versions
536
- >> PaperTrail.whodunnit = 'Alice'
537
- >> widget.update_attributes :name => 'Yankee'
538
- >> widget..paper_trail_originator # 'Alice'
539
- >> PaperTrail.whodunnit = 'Bob'
540
- >> widget.update_attributes :name => 'Zulu'
541
- >> widget.paper_trail_originator # 'Bob'
542
- >> first_version, last_version = widget.versions.first, widget.versions.last
543
- >> first_version.whodunnit # 'Alice'
544
- >> first_version.paper_trail_originator # nil
545
- >> first_version.terminator # 'Alice'
546
- >> last_version.whodunnit # 'Bob'
547
- >> last_version.paper_trail_originator # 'Alice'
548
- >> last_version.terminator # 'Bob'
564
+ PaperTrail.whodunnit = 'Andy Stewart'
565
+ widget.whodunnit('Lucas Souza') do
566
+ widget.update_attributes :name => 'Wibble'
567
+ end
568
+ widget.versions.last.whodunnit # Lucas Souza
569
+ widget.update_attributes :name => 'Clair'
570
+ widget.versions.last.whodunnit # Andy Stewart
571
+ widget.whodunnit('Ben Atkins') { |w| w.update_attributes :name => 'Beth' } # this syntax also works
572
+ widget.versions.last.whodunnit # Ben Atkins
573
+ ```
574
+
575
+ A version's `whodunnit` records who changed the object causing the `version` to
576
+ be stored. Because a version stores the object as it looked before the change
577
+ (see the table above), `whodunnit` returns who stopped the object looking like
578
+ this -- not who made it look like this. Hence `whodunnit` is aliased as
579
+ `terminator`.
580
+
581
+ To find out who made a version's object look that way, use
582
+ `version.paper_trail_originator`. And to find out who made a "live" object look
583
+ like it does, call `paper_trail_originator` on the object.
584
+
585
+ ```ruby
586
+ widget = Widget.find 153 # assume widget has 0 versions
587
+ PaperTrail.whodunnit = 'Alice'
588
+ widget.update_attributes :name => 'Yankee'
589
+ widget..paper_trail_originator # 'Alice'
590
+ PaperTrail.whodunnit = 'Bob'
591
+ widget.update_attributes :name => 'Zulu'
592
+ widget.paper_trail_originator # 'Bob'
593
+ first_version, last_version = widget.versions.first, widget.versions.last
594
+ first_version.whodunnit # 'Alice'
595
+ first_version.paper_trail_originator # nil
596
+ first_version.terminator # 'Alice'
597
+ last_version.whodunnit # 'Bob'
598
+ last_version.paper_trail_originator # 'Alice'
599
+ last_version.terminator # 'Bob'
549
600
  ```
550
601
 
551
602
  ## Custom Version Classes
@@ -563,9 +614,18 @@ class Post < ActiveRecord::Base
563
614
  end
564
615
  ```
565
616
 
566
- This allows you to store each model's versions in a separate table, which is useful if you have a lot of versions being created.
617
+ Unlike ActiveRecord's `class_name`, you'll have to supply the complete module path to the class (e.g. `Foo::BarVersion` if your class is inside the module `Foo`).
618
+
619
+ ### Advantages
567
620
 
568
- If you are using Postgres, you should also define the sequence that your custom version class will use:
621
+ 1. For models which have a lot of versions, storing each model's versions in a
622
+ separate table can improve the performance of certain database queries.
623
+ 1. Store different version [metadata](#storing-metadata) for different models.
624
+
625
+ ### Configuration
626
+
627
+ If you are using Postgres, you should also define the sequence that your custom
628
+ version class will use:
569
629
 
570
630
  ```ruby
571
631
  class PostVersion < PaperTrail::Version
@@ -574,26 +634,23 @@ class PostVersion < PaperTrail::Version
574
634
  end
575
635
  ```
576
636
 
577
- Alternatively you could store certain metadata for one type of version, and other metadata for other versions.
578
-
579
- If you only use custom version classes and don't use PaperTrail's built-in one, on Rails `>= 3.2` you must:
580
-
581
- - either declare the `PaperTrail::Version` class to be abstract like this (in an initializer):
637
+ If you only use custom version classes and don't have a `versions` table, you
638
+ must let ActiveRecord know that the `PaperTrail::Version` class is an
639
+ `abstract_class`.
582
640
 
583
641
  ```ruby
584
- # config/initializers/paper_trail.rb
585
-
586
- # the following line is required for PaperTrail >= 4.0.0 with Rails
587
- PaperTrail::Rails::Engine.eager_load!
588
-
589
- PaperTrail::Version.module_eval do
590
- self.abstract_class = true
642
+ # app/models/paper_trail/version.rb
643
+ module PaperTrail
644
+ class Version < ActiveRecord::Base
645
+ include PaperTrail::VersionConcern
646
+ self.abstract_class = true
647
+ end
591
648
  end
592
649
  ```
593
650
 
594
- - or create a `versions` table in the database so Rails can instantiate the `PaperTrail::Version` superclass.
595
-
596
- You can also specify custom names for the versions and version associations. This is useful if you already have `versions` or/and `version` methods on your model. For example:
651
+ You can also specify custom names for the versions and version associations.
652
+ This is useful if you already have `versions` or/and `version` methods on your
653
+ model. For example:
597
654
 
598
655
  ```ruby
599
656
  class Post < ActiveRecord::Base
@@ -613,9 +670,21 @@ end
613
670
 
614
671
  ## Associations
615
672
 
616
- PaperTrail can restore three types of associations: Has-One, Has-Many, and Has-Many-Through. In order to do this, you will need to create a `version_associations` table, either at installation time with the `rails generate paper_trail:install --with-associations` option or manually. PaperTrail will store in that table additional information to correlate versions of the association and versions of the model when the associated record is changed. When reifying the model, PaperTrail can use this table, together with the `transaction_id` to find the correct version of the association and reify it. The `transaction_id` is a unique id for version records created in the same transaction. It is used to associate the version of the model and the version of the association that are created in the same transaction.
673
+ PaperTrail can restore three types of associations: Has-One, Has-Many, and
674
+ Has-Many-Through. In order to do this, you will need to create a
675
+ `version_associations` table, either at installation time with the `rails
676
+ generate paper_trail:install --with-associations` option or manually. PaperTrail
677
+ will store in that table additional information to correlate versions of the
678
+ association and versions of the model when the associated record is changed.
679
+ When reifying the model, PaperTrail can use this table, together with the
680
+ `transaction_id` to find the correct version of the association and reify it.
681
+ The `transaction_id` is a unique id for version records created in the same
682
+ transaction. It is used to associate the version of the model and the version of
683
+ the association that are created in the same transaction.
617
684
 
618
- To restore Has-One associations as they were at the time, pass option `:has_one => true` to `reify`. To restore Has-Many and Has-Many-Through associations, use option `:has_many => true`. For example:
685
+ To restore Has-One associations as they were at the time, pass option `:has_one
686
+ => true` to `reify`. To restore Has-Many and Has-Many-Through associations, use
687
+ option `:has_many => true`. For example:
619
688
 
620
689
  ```ruby
621
690
  class Location < ActiveRecord::Base
@@ -628,34 +697,42 @@ class Treasure < ActiveRecord::Base
628
697
  has_paper_trail
629
698
  end
630
699
 
631
- >> treasure.amount # 100
632
- >> treasure.location.latitude # 12.345
700
+ treasure.amount # 100
701
+ treasure.location.latitude # 12.345
633
702
 
634
- >> treasure.update_attributes :amount => 153
635
- >> treasure.location.update_attributes :latitude => 54.321
703
+ treasure.update_attributes :amount => 153
704
+ treasure.location.update_attributes :latitude => 54.321
636
705
 
637
- >> t = treasure.versions.last.reify(:has_one => true)
638
- >> t.amount # 100
639
- >> t.location.latitude # 12.345
706
+ t = treasure.versions.last.reify(:has_one => true)
707
+ t.amount # 100
708
+ t.location.latitude # 12.345
640
709
  ```
641
710
 
642
- If the parent and child are updated in one go, PaperTrail can use the aforementioned `transaction_id` to reify the models as they were before the transaction (instead of before the update to the model).
711
+ If the parent and child are updated in one go, PaperTrail can use the
712
+ aforementioned `transaction_id` to reify the models as they were before the
713
+ transaction (instead of before the update to the model).
643
714
 
644
715
  ```ruby
645
- >> treasure.amount # 100
646
- >> treasure.location.latitude # 12.345
716
+ treasure.amount # 100
717
+ treasure.location.latitude # 12.345
647
718
 
648
- >> Treasure.transaction do
649
- >> treasure.location.update_attributes :latitude => 54.321
650
- >> treasure.update_attributes :amount => 153
651
- >> end
719
+ Treasure.transaction do
720
+ treasure.location.update_attributes :latitude => 54.321
721
+ treasure.update_attributes :amount => 153
722
+ end
652
723
 
653
- >> t = treasure.versions.last.reify(:has_one => true)
654
- >> t.amount # 100
655
- >> t.location.latitude # 12.345, instead of 54.321
724
+ t = treasure.versions.last.reify(:has_one => true)
725
+ t.amount # 100
726
+ t.location.latitude # 12.345, instead of 54.321
656
727
  ```
657
728
 
658
- By default, PaperTrail excludes an associated record from the reified parent model if the associated record exists in the live model but did not exist as at the time the version was created. This is usually what you want if you just want to look at the reified version. But if you want to persist it, it would be better to pass in option `:mark_for_destruction => true` so that the associated record is included and marked for destruction. Note that `mark_for_destruction` only has [an effect on associations marked with `autosave: true`](http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction).
729
+ By default, PaperTrail excludes an associated record from the reified parent
730
+ model if the associated record exists in the live model but did not exist as at
731
+ the time the version was created. This is usually what you want if you just want
732
+ to look at the reified version. But if you want to persist it, it would be
733
+ better to pass in option `:mark_for_destruction => true` so that the associated
734
+ record is included and marked for destruction. Note that `mark_for_destruction`
735
+ only has [an effect on associations marked with `autosave: true`][32].
659
736
 
660
737
  ```ruby
661
738
  class Widget < ActiveRecord::Base
@@ -668,26 +745,33 @@ class Wotsit < ActiveRecord::Base
668
745
  belongs_to :widget
669
746
  end
670
747
 
671
- >> widget = Widget.create(:name => 'widget_0')
672
- >> widget.update_attributes(:name => 'widget_1')
673
- >> widget.create_wotsit(:name => 'wotsit')
748
+ widget = Widget.create(:name => 'widget_0')
749
+ widget.update_attributes(:name => 'widget_1')
750
+ widget.create_wotsit(:name => 'wotsit')
674
751
 
675
- >> widget_0 = widget.versions.last.reify(:has_one => true)
676
- >> widget_0.wotsit # nil
752
+ widget_0 = widget.versions.last.reify(:has_one => true)
753
+ widget_0.wotsit # nil
677
754
 
678
- >> widget_0 = widget.versions.last.reify(:has_one => true, :mark_for_destruction => true)
679
- >> widget_0.wotsit.marked_for_destruction? # true
680
- >> widget_0.save!
681
- >> widget.reload.wotsit # nil
755
+ widget_0 = widget.versions.last.reify(:has_one => true, :mark_for_destruction => true)
756
+ widget_0.wotsit.marked_for_destruction? # true
757
+ widget_0.save!
758
+ widget.reload.wotsit # nil
682
759
  ```
683
760
 
684
761
  **Caveats:**
685
762
 
686
- 1. PaperTrail can't restore an association properly if the association record can be updated to replace its parent model (by replacing the foreign key)
687
- 2. Currently PaperTrail only support single `version_associations` table. The implication is that you can only use a single table to store the versions for all related models. Sorry for those who use multiple version tables.
688
- 3. PaperTrail only reifies the first level of associations, i.e., it does not reify any associations of its associations, and so on.
689
- 4. PaperTrail relies on the callbacks on the association model (and the :through association model for Has-Many-Through associations) to record the versions and the relationship between the versions. If the association is changed without invoking the callbacks, Reification won't work. Below are some examples:
690
-
763
+ 1. PaperTrail can't restore an association properly if the association record
764
+ can be updated to replace its parent model (by replacing the foreign key)
765
+ 2. Currently PaperTrail only support single `version_associations` table. The
766
+ implication is that you can only use a single table to store the versions for
767
+ all related models. Sorry for those who use multiple version tables.
768
+ 3. PaperTrail only reifies the first level of associations, i.e., it does not
769
+ reify any associations of its associations, and so on.
770
+ 4. PaperTrail relies on the callbacks on the association model (and the :through
771
+ association model for Has-Many-Through associations) to record the versions
772
+ and the relationship between the versions. If the association is changed
773
+ without invoking the callbacks, Reification won't work. Below are some
774
+ examples:
691
775
 
692
776
  Given these models:
693
777
 
@@ -714,22 +798,23 @@ end
714
798
  Then each of the following will store authorship versions:
715
799
 
716
800
  ```ruby
717
- >> @book.authors << @dostoyevsky
718
- >> @book.authors.create :name => 'Tolstoy'
719
- >> @book.authorships.last.destroy
720
- >> @book.authorships.clear
721
- >> @book.author_ids = [@solzhenistyn.id, @dostoyevsky.id]
801
+ @book.authors << @dostoyevsky
802
+ @book.authors.create :name => 'Tolstoy'
803
+ @book.authorships.last.destroy
804
+ @book.authorships.clear
805
+ @book.author_ids = [@solzhenistyn.id, @dostoyevsky.id]
722
806
  ```
723
807
 
724
808
  But none of these will:
725
809
 
726
810
  ```ruby
727
- >> @book.authors.delete @tolstoy
728
- >> @book.author_ids = []
729
- >> @book.authors = []
811
+ @book.authors.delete @tolstoy
812
+ @book.author_ids = []
813
+ @book.authors = []
730
814
  ```
731
815
 
732
- Having said that, you can apparently get all these working (I haven't tested it myself) with this patch:
816
+ Having said that, you can apparently get all these working (I haven't tested it
817
+ myself) with this patch:
733
818
 
734
819
  ```ruby
735
820
  # In config/initializers/active_record_patch.rb
@@ -748,10 +833,9 @@ module ActiveRecord
748
833
  end
749
834
  ```
750
835
 
751
- See [issue 113](https://github.com/airblade/paper_trail/issues/113) for a discussion about this.
836
+ See [issue 113][16] for a discussion about this.
752
837
 
753
-
754
- ## Storing metadata
838
+ ## Storing Metadata
755
839
 
756
840
  You can store arbitrary model-level metadata alongside each version like this:
757
841
 
@@ -767,37 +851,28 @@ class Article < ActiveRecord::Base
767
851
  end
768
852
  ```
769
853
 
770
- PaperTrail will call your proc with the current article and store the result in the `author_id` column of the `versions` table.
771
-
772
- N.B. You must also:
773
-
774
- * Add your metadata columns to the `versions` table.
775
- * Declare your metadata columns using `attr_accessible`. (If you are using `ActiveRecord 3`, or `ActiveRecord 4` with the [ProtectedAttributes](https://github.com/rails/protected_attributes) gem)
776
-
777
- For example:
778
-
779
- ```ruby
780
- # config/initializers/paper_trail.rb
781
-
782
- # the following line is required for PaperTrail >= 4.0.0 with Rails
783
- PaperTrail::Rails::Engine.eager_load!
854
+ PaperTrail will call your proc with the current article and store the result in
855
+ the `author_id` column of the `versions` table.
784
856
 
785
- module PaperTrail
786
- class Version < ActiveRecord::Base
787
- attr_accessible :author_id, :word_count, :answer
788
- end
789
- end
790
- ```
857
+ ### Advantages of Metadata
791
858
 
792
- Why would you do this? In this example, `author_id` is an attribute of `Article` and PaperTrail will store it anyway in a serialized form in the `object` column of the `version` record. But let's say you wanted to pull out all versions for a particular author; without the metadata you would have to deserialize (reify) each `version` object to see if belonged to the author in question. Clearly this is inefficient. Using the metadata you can find just those versions you want:
859
+ Why would you do this? In this example, `author_id` is an attribute of
860
+ `Article` and PaperTrail will store it anyway in a serialized form in the
861
+ `object` column of the `version` record. But let's say you wanted to pull out
862
+ all versions for a particular author; without the metadata you would have to
863
+ deserialize (reify) each `version` object to see if belonged to the author in
864
+ question. Clearly this is inefficient. Using the metadata you can find just
865
+ those versions you want:
793
866
 
794
867
  ```ruby
795
868
  PaperTrail::Version.where(:author_id => author_id)
796
869
  ```
797
870
 
798
- Note you can pass a symbol as a value in the `meta` hash to signal a method to call.
871
+ ### Metadata from Controllers
799
872
 
800
- 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.:
873
+ You can also store any information you like from your controller. Override
874
+ the `info_for_paper_trail` method in your controller to return a hash whose keys
875
+ correspond to columns in your `versions` table.
801
876
 
802
877
  ```ruby
803
878
  class ApplicationController
@@ -807,60 +882,99 @@ class ApplicationController
807
882
  end
808
883
  ```
809
884
 
810
- Remember to add those extra columns to your `versions` table and use `attr_accessible` ;)
885
+ ### Protected Attributes and Metadata
811
886
 
812
- **NOTE FOR RAILS 4:** If you're using [Strong Parameters](https://github.com/rails/strong_parameters) in Rails 4 and have *not* included the `protected_attributes` gem, there's no need to declare your metadata columns using `attr_accessible`.
887
+ If you are using rails 3 or the [protected_attributes][17] gem you must declare
888
+ your metadata columns to be `attr_accessible`.
889
+
890
+ ```ruby
891
+ # app/models/paper_trail/version.rb
892
+ module PaperTrail
893
+ class Version < ActiveRecord::Base
894
+ include PaperTrail::VersionConcern
895
+ attr_accessible :author_id, :word_count, :answer
896
+ end
897
+ end
898
+ ```
813
899
 
900
+ If you're using [strong_parameters][18] instead of [protected_attributes][17]
901
+ then there is no need to use `attr_accessible`.
814
902
 
815
903
  ## Diffing Versions
816
904
 
817
- There are two scenarios: diffing adjacent versions and diffing non-adjacent versions.
905
+ There are two scenarios: diffing adjacent versions and diffing non-adjacent
906
+ versions.
818
907
 
819
- The best way to diff adjacent versions is to get PaperTrail to do it for you. If you add an `object_changes` text column to your `versions` table, either at installation time with the `rails generate paper_trail:install --with-changes` option or manually, PaperTrail will store the `changes` diff (excluding any attributes PaperTrail is ignoring) in each `update` version. You can use the `version.changeset` method to retrieve it. For example:
908
+ The best way to diff adjacent versions is to get PaperTrail to do it for you.
909
+ If you add an `object_changes` text column to your `versions` table, either at
910
+ installation time with the `rails generate paper_trail:install --with-changes`
911
+ option or manually, PaperTrail will store the `changes` diff (excluding any
912
+ attributes PaperTrail is ignoring) in each `update` version. You can use the
913
+ `version.changeset` method to retrieve it. For example:
820
914
 
821
915
  ```ruby
822
- >> widget = Widget.create :name => 'Bob'
823
- >> widget.versions.last.changeset # {'name' => [nil, 'Bob']}
824
- >> widget.update_attributes :name => 'Robert'
825
- >> widget.versions.last.changeset # {'name' => ['Bob', 'Robert']}
826
- >> widget.destroy
827
- >> widget.versions.last.changeset # {}
916
+ widget = Widget.create :name => 'Bob'
917
+ widget.versions.last.changeset # {'name' => [nil, 'Bob']}
918
+ widget.update_attributes :name => 'Robert'
919
+ widget.versions.last.changeset # {'name' => ['Bob', 'Robert']}
920
+ widget.destroy
921
+ widget.versions.last.changeset # {}
828
922
  ```
829
923
 
830
- Note PaperTrail only stores the changes for creation and updates; it doesn't store anything when an object is destroyed.
924
+ Note PaperTrail only stores the changes for creation and updates; it doesn't
925
+ store anything when an object is destroyed.
831
926
 
832
- Please be aware that PaperTrail doesn't use diffs internally. When I designed PaperTrail I wanted simplicity and robustness so I decided to make each version of an object self-contained. A version stores all of its object's data, not a diff from the previous version. This means you can delete any version without affecting any other.
927
+ Please be aware that PaperTrail doesn't use diffs internally. When I designed
928
+ PaperTrail I wanted simplicity and robustness so I decided to make each version
929
+ of an object self-contained. A version stores all of its object's data, not a
930
+ diff from the previous version. This means you can delete any version without
931
+ affecting any other.
833
932
 
834
- To diff non-adjacent versions you'll have to write your own code. These libraries may help:
933
+ To diff non-adjacent versions you'll have to write your own code. These
934
+ libraries may help:
835
935
 
836
936
  For diffing two strings:
837
937
 
838
- * [htmldiff](http://github.com/myobie/htmldiff): expects but doesn't require HTML input and produces HTML output. Works very well but slows down significantly on large (e.g. 5,000 word) inputs.
839
- * [differ](http://github.com/pvande/differ): expects plain text input and produces plain text/coloured/HTML/any output. Can do character-wise, word-wise, line-wise, or arbitrary-boundary-string-wise diffs. Works very well on non-HTML input.
840
- * [diff-lcs](https://github.com/halostatue/diff-lcs): old-school, line-wise diffs.
938
+ * [htmldiff][19]: expects but doesn't require HTML input and produces HTML
939
+ output. Works very well but slows down significantly on large (e.g. 5,000
940
+ word) inputs.
941
+ * [differ][20]: expects plain text input and produces plain
942
+ text/coloured/HTML/any output. Can do character-wise, word-wise, line-wise,
943
+ or arbitrary-boundary-string-wise diffs. Works very well on non-HTML input.
944
+ * [diff-lcs][21]: old-school, line-wise diffs.
841
945
 
842
946
  For diffing two ActiveRecord objects:
843
947
 
844
- * [Jeremy Weiskotten's PaperTrail fork](http://github.com/jeremyw/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb#L151-156): uses ActiveSupport's diff to return an array of hashes of the changes.
845
- * [activerecord-diff](http://github.com/tim/activerecord-diff): rather like ActiveRecord::Dirty but also allows you to specify which columns to compare.
948
+ * [Jeremy Weiskotten's PaperTrail fork][22]: uses ActiveSupport's diff to return
949
+ an array of hashes of the changes.
950
+ * [activerecord-diff][23]: rather like ActiveRecord::Dirty but also allows you
951
+ to specify which columns to compare.
846
952
 
847
- If you wish to selectively record changes for some models but not others you can opt out of recording changes by passing `:save_changes => false` to your `has_paper_trail` method declaration.
953
+ If you wish to selectively record changes for some models but not others you
954
+ can opt out of recording changes by passing `:save_changes => false` to your
955
+ `has_paper_trail` method declaration.
848
956
 
849
957
  ## Turning PaperTrail Off/On
850
958
 
851
- Sometimes you don't want to store changes. Perhaps you are only interested in changes made by your users and don't need to store changes you make yourself in, say, a migration -- or when testing your application.
959
+ Sometimes you don't want to store changes. Perhaps you are only interested in
960
+ changes made by your users and don't need to store changes you make yourself in,
961
+ say, a migration -- or when testing your application.
852
962
 
853
- You can turn PaperTrail on or off in three ways: globally, per request, or per class.
963
+ You can turn PaperTrail on or off in three ways: globally, per request, or per
964
+ class.
854
965
 
855
966
  ### Globally
856
967
 
857
968
  On a global level you can turn PaperTrail off like this:
858
969
 
859
970
  ```ruby
860
- >> PaperTrail.enabled = false
971
+ PaperTrail.enabled = false
861
972
  ```
862
973
 
863
- For example, you might want to disable PaperTrail in your Rails application's test environment to speed up your tests. This will do it (note: this gets done automatically for `RSpec` and `Cucumber`, please see the [Testing section](#testing)):
974
+ For example, you might want to disable PaperTrail in your Rails application's
975
+ test environment to speed up your tests. This will do it (note: this gets done
976
+ automatically for `RSpec` and `Cucumber`, please see the [Testing
977
+ section](#testing)):
864
978
 
865
979
  ```ruby
866
980
  # in config/environments/test.rb
@@ -869,7 +983,8 @@ config.after_initialize do
869
983
  end
870
984
  ```
871
985
 
872
- If you disable PaperTrail in your test environment but want to enable it for specific tests, you can add a helper like this to your test helper:
986
+ If you disable PaperTrail in your test environment but want to enable it for
987
+ specific tests, you can add a helper like this to your test helper:
873
988
 
874
989
  ```ruby
875
990
  # in test/test_helper.rb
@@ -899,7 +1014,9 @@ end
899
1014
 
900
1015
  ### Per request
901
1016
 
902
- You can turn PaperTrail on or off per request by adding a `paper_trail_enabled_for_controller` method to your controller which returns `true` or `false`:
1017
+ You can turn PaperTrail on or off per request by adding a
1018
+ `paper_trail_enabled_for_controller` method to your controller which returns
1019
+ `true` or `false`:
903
1020
 
904
1021
  ```ruby
905
1022
  class ApplicationController < ActionController::Base
@@ -911,21 +1028,23 @@ end
911
1028
 
912
1029
  ### Per class
913
1030
 
914
- If you are about to change some widgets and you don't want a paper trail of your changes, you can turn PaperTrail off like this:
1031
+ If you are about to change some widgets and you don't want a paper trail of your
1032
+ changes, you can turn PaperTrail off like this:
915
1033
 
916
1034
  ```ruby
917
- >> Widget.paper_trail_off!
1035
+ Widget.paper_trail_off!
918
1036
  ```
919
1037
 
920
1038
  And on again like this:
921
1039
 
922
1040
  ```ruby
923
- >> Widget.paper_trail_on!
1041
+ Widget.paper_trail_on!
924
1042
  ```
925
1043
 
926
1044
  ### Per method call
927
1045
 
928
- You can call a method without creating a new version using `without_versioning`. It takes either a method name as a symbol:
1046
+ You can call a method without creating a new version using `without_versioning`.
1047
+ It takes either a method name as a symbol:
929
1048
 
930
1049
  ```ruby
931
1050
  @widget.without_versioning :destroy
@@ -941,22 +1060,24 @@ end
941
1060
 
942
1061
  ## Using a custom serializer
943
1062
 
944
- By default, PaperTrail stores your changes as a `YAML` dump. You can override this with the serializer config option:
1063
+ By default, PaperTrail stores your changes as a `YAML` dump. You can override
1064
+ this with the serializer config option:
945
1065
 
946
1066
  ```ruby
947
- >> PaperTrail.serializer = MyCustomSerializer
1067
+ PaperTrail.serializer = MyCustomSerializer
948
1068
  ```
949
1069
 
950
- A valid serializer is a `module` (or `class`) that defines a `load` and `dump` method. These serializers are included in the gem for your convenience:
1070
+ A valid serializer is a `module` (or `class`) that defines a `load` and `dump`
1071
+ method. These serializers are included in the gem for your convenience:
951
1072
 
952
- * [PaperTrail::Serializers::YAML](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/yaml.rb) - Default
953
- * [PaperTrail::Serializers::JSON](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/json.rb)
1073
+ * [PaperTrail::Serializers::YAML][24] - Default
1074
+ * [PaperTrail::Serializers::JSON][25]
954
1075
 
955
1076
  ### PostgreSQL JSON column type support
956
1077
 
957
- If you use PostgreSQL, and would like to store your `object` (and/or `object_changes`) data in a column of
958
- [type `JSON` or type `JSONB`](http://www.postgresql.org/docs/9.4/static/datatype-json.html),
959
- specify `json` instead of `text` for these columns in your migration:
1078
+ If you use PostgreSQL, and would like to store your `object` (and/or
1079
+ `object_changes`) data in a column of [type `JSON` or type `JSONB`][26], specify
1080
+ `json` instead of `text` for these columns in your migration:
960
1081
 
961
1082
  ```ruby
962
1083
  create_table :versions do |t|
@@ -967,61 +1088,67 @@ create_table :versions do |t|
967
1088
  end
968
1089
  ```
969
1090
 
970
- Note: You don't need to use a particular serializer for the PostgreSQL `JSON` column type.
1091
+ Note: You don't need to use a particular serializer for the PostgreSQL `JSON`
1092
+ column type.
971
1093
 
972
1094
  ## SerializedAttributes support
973
1095
 
974
- PaperTrail has a config option that can be used to enable/disable whether PaperTrail attempts to utilize
975
- `ActiveRecord`'s `serialized_attributes` feature. Note: This is enabled by default when PaperTrail is used
976
- with `ActiveRecord` version < `4.2`, and disabled by default when used with ActiveRecord `4.2.x`. Since
977
- `serialized_attributes` will be removed in `ActiveRecord` version `5.0`, this configuration value
978
- has no functionality when PaperTrail is used with version `5.0` or greater.
1096
+ PaperTrail has a config option that can be used to enable/disable whether
1097
+ PaperTrail attempts to utilize `ActiveRecord`'s `serialized_attributes` feature.
1098
+ Note: This is enabled by default when PaperTrail is used with `ActiveRecord`
1099
+ version < `4.2`, and disabled by default when used with ActiveRecord `4.2.x`.
1100
+ Since `serialized_attributes` will be removed in `ActiveRecord` version `5.0`,
1101
+ this configuration value has no functionality when PaperTrail is used with
1102
+ version `5.0` or greater.
979
1103
 
980
1104
  ```ruby
981
1105
  # Enable support
982
- >> PaperTrail.config.serialized_attributes = true
1106
+ PaperTrail.config.serialized_attributes = true
983
1107
  # Disable support
984
- >> PaperTrail.config.serialized_attributes = false
1108
+ PaperTrail.config.serialized_attributes = false
985
1109
  # Get current setting
986
- >> PaperTrail.serialized_attributes?
1110
+ PaperTrail.serialized_attributes?
987
1111
  ```
988
1112
 
989
- ## Limiting the number of versions created per object instance
1113
+ ## Limiting the Number of Versions Created
990
1114
 
991
- If you are wary of your `versions` table growing to an unwieldy size, or just don't care to track more than a certain number of versions per object,
992
- there is a configuration option that can be set to cap the number of versions saved per object. Note that this value must be numeric, and it only applies to
993
- versions other than `create` events (which will always be preserved if they are stored).
1115
+ Configure `version_limit` to cap the number of versions saved per record. This
1116
+ does not apply to `create` events.
994
1117
 
995
1118
  ```ruby
996
- # will make it so that a maximum of 4 versions will be stored for each object
997
- # (the 3 most recent ones plus a `create` event)
998
- >> PaperTrail.config.version_limit = 3
999
- # disables/removes the version limit
1000
- >> PaperTrail.config.version_limit = nil
1119
+ # Limit: 4 versions per record (3 most recent, plus a `create` event)
1120
+ PaperTrail.config.version_limit = 3
1121
+ # Remove the limit
1122
+ PaperTrail.config.version_limit = nil
1001
1123
  ```
1002
1124
 
1003
1125
  ## Deleting Old Versions
1004
1126
 
1005
- Over time your `versions` table will grow to an unwieldy size. Because each version is self-contained (see the Diffing section above for more) you can simply delete any records you don't want any more. For example:
1127
+ Over time your `versions` table will grow to an unwieldy size. Because each
1128
+ version is self-contained (see the Diffing section above for more) you can
1129
+ simply delete any records you don't want any more. For example:
1006
1130
 
1007
1131
  ```sql
1008
1132
  sql> delete from versions where created_at < 2010-06-01;
1009
1133
  ```
1010
1134
 
1011
1135
  ```ruby
1012
- >> PaperTrail::Version.delete_all ["created_at < ?", 1.week.ago]
1136
+ PaperTrail::Version.delete_all ["created_at < ?", 1.week.ago]
1013
1137
  ```
1014
1138
 
1015
1139
  ## Testing
1016
1140
 
1017
- You may want to turn PaperTrail off to speed up your tests. See the [Turning PaperTrail Off/On](#turning-papertrail-offon) section above for tips on usage with `Test::Unit`.
1141
+ You may want to turn PaperTrail off to speed up your tests. See the [Turning
1142
+ PaperTrail Off/On](#turning-papertrail-offon) section above for tips on usage
1143
+ with `Test::Unit`.
1018
1144
 
1019
1145
  ### RSpec
1020
1146
 
1021
- PaperTrail provides a helper that works with [RSpec](https://github.com/rspec/rspec)
1022
- to make it easier to control when `PaperTrail` is enabled during testing.
1147
+ PaperTrail provides a helper that works with [RSpec][27] to make it easier to
1148
+ control when `PaperTrail` is enabled during testing.
1023
1149
 
1024
- If you wish to use the helper, you will need to require it in your RSpec test helper like so:
1150
+ If you wish to use the helper, you will need to require it in your RSpec test
1151
+ helper like so:
1025
1152
 
1026
1153
  ```ruby
1027
1154
  # spec/rails_helper.rb
@@ -1034,8 +1161,10 @@ require 'rspec/rails'
1034
1161
  require 'paper_trail/frameworks/rspec'
1035
1162
  ```
1036
1163
 
1037
- When the helper is loaded, PaperTrail will be turned off for all tests by default.
1038
- When you wish to enable PaperTrail for a test you can either wrap the test in a `with_versioning` block, or pass in `:versioning => true` option to a spec block, like so:
1164
+ When the helper is loaded, PaperTrail will be turned off for all tests by
1165
+ default. When you wish to enable PaperTrail for a test you can either wrap the
1166
+ test in a `with_versioning` block, or pass in `:versioning => true` option to a
1167
+ spec block, like so:
1039
1168
 
1040
1169
  ```ruby
1041
1170
  describe "RSpec test group" do
@@ -1055,10 +1184,13 @@ describe "RSpec test group" do
1055
1184
  end
1056
1185
  ```
1057
1186
 
1058
- The helper will also reset the `PaperTrail.whodunnit` value to `nil` before each test to help prevent data spillover between tests.
1059
- If you are using PaperTrail with Rails, the helper will automatically set the `PaperTrail.controller_info` value to `{}` as well, again, to help prevent data spillover between tests.
1187
+ The helper will also reset the `PaperTrail.whodunnit` value to `nil` before each
1188
+ test to help prevent data spillover between tests. If you are using PaperTrail
1189
+ with Rails, the helper will automatically set the `PaperTrail.controller_info`
1190
+ value to `{}` as well, again, to help prevent data spillover between tests.
1060
1191
 
1061
- There is also a `be_versioned` matcher provided by PaperTrail's RSpec helper which can be leveraged like so:
1192
+ There is also a `be_versioned` matcher provided by PaperTrail's RSpec helper
1193
+ which can be leveraged like so:
1062
1194
 
1063
1195
  ```ruby
1064
1196
  class Widget < ActiveRecord::Base
@@ -1083,7 +1215,8 @@ describe Widget do
1083
1215
  end
1084
1216
  ```
1085
1217
 
1086
- It is also possible to do assertions on the versions using `have_a_version_with` matcher
1218
+ It is also possible to do assertions on the versions using `have_a_version_with`
1219
+ matcher
1087
1220
 
1088
1221
  ```
1089
1222
  describe '`have_a_version_with` matcher' do
@@ -1104,8 +1237,9 @@ It is also possible to do assertions on the versions using `have_a_version_with`
1104
1237
 
1105
1238
  ### Cucumber
1106
1239
 
1107
- PaperTrail provides a helper for [Cucumber](http://cukes.info) that works similar to the RSpec helper.
1108
- If you wish to use the helper, you will need to require in your cucumber helper like so:
1240
+ PaperTrail provides a helper for [Cucumber][28] that works similar to the RSpec
1241
+ helper.If you wish to use the helper, you will need to require in your cucumber
1242
+ helper like so:
1109
1243
 
1110
1244
  ```ruby
1111
1245
  # features/support/env.rb
@@ -1116,8 +1250,10 @@ require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
1116
1250
  require 'paper_trail/frameworks/cucumber'
1117
1251
  ```
1118
1252
 
1119
- When the helper is loaded, PaperTrail will be turned off for all scenarios by a `before` hook added by the helper by default.
1120
- When you wish to enable PaperTrail for a scenario, you can wrap code in a `with_versioning` block in a step, like so:
1253
+ When the helper is loaded, PaperTrail will be turned off for all scenarios by a
1254
+ `before` hook added by the helper by default. When you wish to enable PaperTrail
1255
+ for a scenario, you can wrap code in a `with_versioning` block in a step, like
1256
+ so:
1121
1257
 
1122
1258
  ```ruby
1123
1259
  Given /I want versioning on my model/ do
@@ -1127,13 +1263,16 @@ Given /I want versioning on my model/ do
1127
1263
  end
1128
1264
  ```
1129
1265
 
1130
- The helper will also reset the `PaperTrail.whodunnit` value to `nil` before each test to help prevent data spillover between tests.
1131
- If you are using PaperTrail with Rails, the helper will automatically set the `PaperTrail.controller_info` value to `{}` as well, again, to help prevent data spillover between tests.
1266
+ The helper will also reset the `PaperTrail.whodunnit` value to `nil` before each
1267
+ test to help prevent data spillover between tests. If you are using PaperTrail
1268
+ with Rails, the helper will automatically set the `PaperTrail.controller_info`
1269
+ value to `{}` as well, again, to help prevent data spillover between tests.
1132
1270
 
1133
1271
  ### Spork
1134
1272
 
1135
- If you wish to use the `RSpec` or `Cucumber` helpers with [Spork](https://github.com/sporkrb/spork), you will need to
1136
- manually require the helper(s) in your `prefork` block on your test helper, like so:
1273
+ If you wish to use the `RSpec` or `Cucumber` helpers with [Spork][29], you will
1274
+ need to manually require the helper(s) in your `prefork` block on your test
1275
+ helper, like so:
1137
1276
 
1138
1277
  ```ruby
1139
1278
  # spec/rails_helper.rb
@@ -1154,8 +1293,9 @@ end
1154
1293
 
1155
1294
  ### Zeus or Spring
1156
1295
 
1157
- If you wish to use the `RSpec` or `Cucumber` helpers with [Zeus](https://github.com/burke/zeus) or [Spring](https://github.com/rails/spring), you will need to
1158
- manually require the helper(s) in your test helper, like so:
1296
+ If you wish to use the `RSpec` or `Cucumber` helpers with [Zeus][30] or
1297
+ [Spring][31], you will need to manually require the helper(s) in your test
1298
+ helper, like so:
1159
1299
 
1160
1300
  ```ruby
1161
1301
  # spec/rails_helper.rb
@@ -1169,9 +1309,13 @@ require 'paper_trail/frameworks/rspec'
1169
1309
 
1170
1310
  ## Testing PaperTrail
1171
1311
 
1172
- Paper Trail has facilities to test against Postgres, Mysql and SQLite. To switch between DB engines you will need to export the DB variable for the engine you wish to test aganist.
1312
+ Paper Trail has facilities to test against Postgres, Mysql and SQLite. To switch
1313
+ between DB engines you will need to export the DB variable for the engine you
1314
+ wish to test aganist.
1173
1315
 
1174
- Though be aware we do not have the abilty to create the db's (except sqlite) for you. You can look at .travis.yml before_script for an example of how to create the db's needed.
1316
+ Though be aware we do not have the abilty to create the db's (except sqlite) for
1317
+ you. You can look at .travis.yml before_script for an example of how to create
1318
+ the db's needed.
1175
1319
 
1176
1320
  ```
1177
1321
  export DB=postgres
@@ -1181,12 +1325,17 @@ export DB=sqlite # this is default
1181
1325
 
1182
1326
  ## Articles
1183
1327
 
1184
- * [Jutsu #8 - Version your RoR models with PaperTrail](http://samurails.com/gems/papertrail/), [Thibault](http://samurails.com/about-me/), 29th September 2014
1185
- * [Versioning with PaperTrail](http://www.sitepoint.com/versioning-papertrail), [Ilya Bodrov](http://www.sitepoint.com/author/ibodrov), 10th April 2014
1186
- * [Using PaperTrail to track stack traces](http://rubyrailsexpert.com/?p=36), T James Corcoran's blog, 1st October 2013.
1187
- * [RailsCast #255 - Undo with PaperTrail](http://railscasts.com/episodes/255-undo-with-paper-trail), 28th February 2011.
1188
- * [Keep a Paper Trail with PaperTrail](http://www.linux-mag.com/id/7528), Linux Magazine, 16th September 2009.
1189
-
1328
+ * [Jutsu #8 - Version your RoR models with PaperTrail](http://samurails.com/gems/papertrail/),
1329
+ [Thibault](http://samurails.com/about-me/), 29th September 2014
1330
+ * [Versioning with PaperTrail](http://www.sitepoint.com/versioning-papertrail),
1331
+ [Ilya Bodrov](http://www.sitepoint.com/author/ibodrov), 10th April 2014
1332
+ * [Using PaperTrail to track stack traces](http://rubyrailsexpert.com/?p=36),
1333
+ T James Corcoran's blog, 1st October 2013.
1334
+ * [RailsCast #255 - Undo with Paper Trail][3], Feb 28, 2011
1335
+ * [RailsCast #255 - Undo with PaperTrail](http://railscasts.com/episodes/255-undo-with-paper-trail),
1336
+ 28th February 2011.
1337
+ * [Keep a Paper Trail with PaperTrail](http://www.linux-mag.com/id/7528),
1338
+ Linux Magazine, 16th September 2009.
1190
1339
 
1191
1340
  ## Problems
1192
1341
 
@@ -1261,3 +1410,36 @@ Many thanks to:
1261
1410
 
1262
1411
  Copyright (c) 2011 Andy Stewart (boss@airbladesoftware.com).
1263
1412
  Released under the MIT licence.
1413
+
1414
+ [1]: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
1415
+ [2]: https://github.com/airblade/paper_trail/issues/163
1416
+ [3]: http://railscasts.com/episodes/255-undo-with-paper-trail
1417
+ [4]: https://img.shields.io/travis/airblade/paper_trail/master.svg
1418
+ [5]: https://travis-ci.org/airblade/paper_trail
1419
+ [6]: https://img.shields.io/gemnasium/airblade/paper_trail.svg
1420
+ [7]: https://gemnasium.com/airblade/paper_trail
1421
+ [9]: https://github.com/airblade/paper_trail/tree/3.0-stable
1422
+ [10]: https://github.com/airblade/paper_trail/tree/2.7-stable
1423
+ [11]: https://github.com/airblade/paper_trail/tree/rails2
1424
+ [12]: http://www.sinatrarb.com
1425
+ [13]: https://github.com/janko-m/sinatra-activerecord
1426
+ [14]: https://raw.github.com/airblade/paper_trail/master/lib/generators/paper_trail/templates/create_versions.rb
1427
+ [15]: http://www.sinatrarb.com/intro.html#Modular%20vs.%20Classic%20Style
1428
+ [16]: https://github.com/airblade/paper_trail/issues/113
1429
+ [17]: https://github.com/rails/protected_attributes
1430
+ [18]: https://github.com/rails/strong_parameters
1431
+ [19]: http://github.com/myobie/htmldiff
1432
+ [20]: http://github.com/pvande/differ
1433
+ [21]: https://github.com/halostatue/diff-lcs
1434
+ [22]: http://github.com/jeremyw/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb#L151-156
1435
+ [23]: http://github.com/tim/activerecord-diff
1436
+ [24]: https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/yaml.rb
1437
+ [25]: https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/json.rb
1438
+ [26]: http://www.postgresql.org/docs/9.4/static/datatype-json.html
1439
+ [27]: https://github.com/rspec/rspec
1440
+ [28]: http://cukes.info
1441
+ [29]: https://github.com/sporkrb/spork
1442
+ [30]: https://github.com/burke/zeus
1443
+ [31]: https://github.com/rails/spring
1444
+ [32]: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction
1445
+ [33]: https://github.com/airblade/paper_trail/wiki/Setting-whodunnit-in-the-rails-console