paper_trail 4.0.0.rc1 → 4.0.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -58
- data/README.md +528 -346
- data/lib/generators/paper_trail/templates/add_object_changes_to_versions.rb +6 -1
- data/lib/generators/paper_trail/templates/create_versions.rb +8 -1
- data/lib/paper_trail.rb +2 -2
- data/lib/paper_trail/config.rb +15 -4
- data/lib/paper_trail/frameworks/active_record.rb +2 -3
- data/lib/paper_trail/has_paper_trail.rb +32 -13
- data/lib/paper_trail/version_concern.rb +1 -1
- data/lib/paper_trail/version_number.rb +1 -1
- data/paper_trail.gemspec +1 -1
- data/spec/models/gadget_spec.rb +1 -1
- data/spec/models/kitchen/banana_spec.rb +14 -0
- data/spec/models/not_on_update_spec.rb +19 -0
- data/spec/models/skipper_spec.rb +17 -0
- data/spec/models/version_spec.rb +5 -8
- data/spec/models/widget_spec.rb +5 -8
- data/test/dummy/app/models/kitchen/banana.rb +5 -0
- data/test/dummy/app/models/not_on_update.rb +4 -0
- data/test/dummy/app/models/skipper.rb +6 -0
- data/test/dummy/app/versions/kitchen/banana_version.rb +5 -0
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +25 -0
- data/test/paper_trail_test.rb +7 -0
- metadata +18 -24
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +0 -2
- data/test/dummy/public/javascripts/controls.js +0 -965
- data/test/dummy/public/javascripts/dragdrop.js +0 -974
- data/test/dummy/public/javascripts/effects.js +0 -1123
- data/test/dummy/public/javascripts/rails.js +0 -175
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae826d8b61e5942f6eb328508ea052e2c2dbd266
|
4
|
+
data.tar.gz: 89a118c9f2a5abfec9c5540c4d380c6e6949cac1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
4
|
-
```ruby
|
5
|
-
PaperTrail::Rails::Engine.eager_load!
|
6
|
-
```
|
3
|
+
### Changed
|
7
4
|
|
8
|
-
|
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
|
-
|
12
|
+
### Added
|
11
13
|
|
12
|
-
- [#
|
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
|
15
|
-
|
16
|
-
`
|
17
|
-
|
18
|
-
|
19
|
-
- [#
|
20
|
-
|
21
|
-
|
22
|
-
|
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)
|
25
|
-
and `object_changes`.
|
26
|
-
- [#479](https://github.com/airblade/paper_trail/issues/479) - Deprecated
|
27
|
-
|
28
|
-
- [#458](https://github.com/airblade/paper_trail/pull/458) - For `create`
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
- [#
|
33
|
-
|
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) -
|
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) /
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
- [#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
- [#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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]
|
2
|
-
|
3
|
-
PaperTrail lets you track changes to your models' data. It's good for auditing
|
4
|
-
|
5
|
-
|
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`]
|
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`]
|
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`]
|
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]
|
62
|
-
|
63
|
-
[Sinatra ActiveRecord Extension]
|
64
|
-
|
65
|
-
`Sinatra ActiveRecord Extension`, steps for
|
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]
|
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
|
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
|
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`]
|
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
|
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
|
234
|
+
This gives you a `versions` method which returns the paper trail of changes to
|
235
|
+
your model.
|
207
236
|
|
208
237
|
```ruby
|
209
|
-
|
210
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
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
|
-
|
230
|
-
|
262
|
+
widget = Widget.find 153
|
263
|
+
widget.name # 'Doobly'
|
231
264
|
|
232
265
|
# Add has_paper_trail to Widget model.
|
233
266
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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
|
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
|
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
|
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
|
280
|
-
|
281
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
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
|
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
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
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`
|
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
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
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`,
|
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
|
-
|
394
|
-
|
443
|
+
widget = Widget.find 42
|
444
|
+
widget.update_attributes :name => 'Blah blah'
|
395
445
|
# Time passes....
|
396
|
-
|
397
|
-
|
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
|
-
|
404
|
-
|
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
|
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
|
-
|
413
|
-
|
463
|
+
widget = Widget.find 42
|
464
|
+
widget.destroy
|
414
465
|
# Time passes....
|
415
|
-
|
416
|
-
|
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
|
-
|
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
|
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
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
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
|
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
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
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
|
-
|
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
|
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
|
-
|
454
|
-
|
455
|
-
|
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
|
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
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
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
|
-
|
471
|
-
|
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
|
477
|
-
|
478
|
-
|
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
|
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
|
-
|
497
|
-
|
498
|
-
|
552
|
+
PaperTrail.whodunnit = 'Andy Stewart'
|
553
|
+
widget.update_attributes :name => 'Wibble'
|
554
|
+
widget.versions.last.whodunnit # Andy Stewart
|
499
555
|
```
|
500
556
|
|
501
|
-
|
502
|
-
|
503
|
-
```ruby
|
504
|
-
# config/initializers/paper_trail.rb
|
557
|
+
See also: [Setting whodunnit in the rails console][33]
|
505
558
|
|
506
|
-
|
507
|
-
PaperTrail
|
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
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
```
|
529
|
-
|
530
|
-
A version's `whodunnit` records who changed the object causing the `version` to
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
578
|
-
|
579
|
-
|
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
|
-
#
|
585
|
-
|
586
|
-
|
587
|
-
PaperTrail::
|
588
|
-
|
589
|
-
|
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
|
-
|
595
|
-
|
596
|
-
|
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
|
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
|
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
|
-
|
632
|
-
|
700
|
+
treasure.amount # 100
|
701
|
+
treasure.location.latitude # 12.345
|
633
702
|
|
634
|
-
|
635
|
-
|
703
|
+
treasure.update_attributes :amount => 153
|
704
|
+
treasure.location.update_attributes :latitude => 54.321
|
636
705
|
|
637
|
-
|
638
|
-
|
639
|
-
|
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
|
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
|
-
|
646
|
-
|
716
|
+
treasure.amount # 100
|
717
|
+
treasure.location.latitude # 12.345
|
647
718
|
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
719
|
+
Treasure.transaction do
|
720
|
+
treasure.location.update_attributes :latitude => 54.321
|
721
|
+
treasure.update_attributes :amount => 153
|
722
|
+
end
|
652
723
|
|
653
|
-
|
654
|
-
|
655
|
-
|
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
|
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
|
-
|
672
|
-
|
673
|
-
|
748
|
+
widget = Widget.create(:name => 'widget_0')
|
749
|
+
widget.update_attributes(:name => 'widget_1')
|
750
|
+
widget.create_wotsit(:name => 'wotsit')
|
674
751
|
|
675
|
-
|
676
|
-
|
752
|
+
widget_0 = widget.versions.last.reify(:has_one => true)
|
753
|
+
widget_0.wotsit # nil
|
677
754
|
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
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
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
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
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
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
|
-
|
728
|
-
|
729
|
-
|
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
|
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]
|
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
|
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
|
-
|
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
|
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
|
-
|
871
|
+
### Metadata from Controllers
|
799
872
|
|
800
|
-
You can also store any information you like from your controller.
|
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
|
-
|
885
|
+
### Protected Attributes and Metadata
|
811
886
|
|
812
|
-
|
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
|
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.
|
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
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
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
|
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
|
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
|
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]
|
839
|
-
|
840
|
-
|
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]
|
845
|
-
|
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
|
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
|
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
|
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
|
-
|
971
|
+
PaperTrail.enabled = false
|
861
972
|
```
|
862
973
|
|
863
|
-
For example, you might want to disable PaperTrail in your Rails application's
|
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
|
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
|
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
|
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
|
-
|
1035
|
+
Widget.paper_trail_off!
|
918
1036
|
```
|
919
1037
|
|
920
1038
|
And on again like this:
|
921
1039
|
|
922
1040
|
```ruby
|
923
|
-
|
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`.
|
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
|
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
|
-
|
1067
|
+
PaperTrail.serializer = MyCustomSerializer
|
948
1068
|
```
|
949
1069
|
|
950
|
-
A valid serializer is a `module` (or `class`) that defines a `load` and `dump`
|
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]
|
953
|
-
* [PaperTrail::Serializers::JSON]
|
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
|
958
|
-
[type `JSON` or type `JSONB`]
|
959
|
-
|
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`
|
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
|
975
|
-
`ActiveRecord`'s `serialized_attributes` feature.
|
976
|
-
|
977
|
-
`
|
978
|
-
|
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
|
-
|
1106
|
+
PaperTrail.config.serialized_attributes = true
|
983
1107
|
# Disable support
|
984
|
-
|
1108
|
+
PaperTrail.config.serialized_attributes = false
|
985
1109
|
# Get current setting
|
986
|
-
|
1110
|
+
PaperTrail.serialized_attributes?
|
987
1111
|
```
|
988
1112
|
|
989
|
-
## Limiting the
|
1113
|
+
## Limiting the Number of Versions Created
|
990
1114
|
|
991
|
-
|
992
|
-
|
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
|
-
#
|
997
|
-
|
998
|
-
|
999
|
-
|
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
|
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
|
-
|
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
|
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]
|
1022
|
-
|
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
|
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
|
1038
|
-
When you wish to enable PaperTrail for a test you can either wrap the
|
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
|
1059
|
-
|
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
|
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`
|
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]
|
1108
|
-
If you wish to use the helper, you will need to require in your cucumber
|
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
|
1120
|
-
|
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
|
1131
|
-
|
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]
|
1136
|
-
manually require the helper(s) in your `prefork` block on your test
|
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]
|
1158
|
-
manually require the helper(s) in your test
|
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
|
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
|
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/),
|
1185
|
-
|
1186
|
-
* [
|
1187
|
-
|
1188
|
-
* [
|
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
|