paper_trail 3.0.9 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +1 -2
- data/.travis.yml +5 -0
- data/CHANGELOG.md +37 -23
- data/README.md +170 -63
- data/gemfiles/3.0.gemfile +10 -4
- data/lib/generators/paper_trail/install_generator.rb +19 -3
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
- data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
- data/lib/paper_trail.rb +24 -4
- data/lib/paper_trail/cleaner.rb +3 -3
- data/lib/paper_trail/config.rb +17 -0
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -0
- data/lib/paper_trail/frameworks/rspec.rb +5 -0
- data/lib/paper_trail/has_paper_trail.rb +112 -38
- data/lib/paper_trail/version_association_concern.rb +13 -0
- data/lib/paper_trail/version_concern.rb +145 -38
- data/lib/paper_trail/version_number.rb +3 -3
- data/paper_trail.gemspec +11 -4
- data/spec/generators/install_generator_spec.rb +4 -4
- data/spec/models/fluxor_spec.rb +19 -0
- data/spec/models/gadget_spec.rb +10 -10
- data/spec/models/joined_version_spec.rb +9 -9
- data/spec/models/post_with_status_spec.rb +3 -3
- data/spec/models/version_spec.rb +49 -71
- data/spec/models/widget_spec.rb +124 -71
- data/spec/modules/version_concern_spec.rb +8 -8
- data/spec/modules/version_number_spec.rb +16 -16
- data/spec/paper_trail_spec.rb +17 -17
- data/spec/rails_helper.rb +34 -0
- data/spec/requests/articles_spec.rb +11 -11
- data/spec/spec_helper.rb +77 -36
- data/test/dummy/app/models/animal.rb +0 -2
- data/test/dummy/app/models/book.rb +4 -0
- data/test/dummy/app/models/customer.rb +4 -0
- data/test/dummy/app/models/editor.rb +4 -0
- data/test/dummy/app/models/editorship.rb +5 -0
- data/test/dummy/app/models/line_item.rb +4 -0
- data/test/dummy/app/models/order.rb +5 -0
- data/test/dummy/app/models/person.rb +1 -1
- data/test/dummy/app/models/post.rb +0 -1
- data/test/dummy/app/models/song.rb +0 -20
- data/test/dummy/app/models/widget.rb +4 -0
- data/test/dummy/config/application.rb +3 -0
- data/test/dummy/config/initializers/paper_trail.rb +1 -1
- data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +41 -0
- data/test/dummy/db/schema.rb +95 -25
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/rails.js +175 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/test_helper.rb +2 -2
- data/test/time_travel_helper.rb +15 -0
- data/test/unit/model_test.rb +613 -185
- data/test/unit/serializer_test.rb +3 -3
- metadata +104 -54
- data/spec/models/animal_spec.rb +0 -19
- data/test/dummy/public/javascripts/prototype.js +0 -6001
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a4a5a1bfd11ee6bc8acabb03cb262fe4691951a
|
4
|
+
data.tar.gz: 6854403b0c404645e52c94154d4d8268642969c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 891aa4e3758245d09974c472c49506a4b49b2ac565018acc1afbebe40907c91729b450e45dd9e5c501c6781f7a673f15f9d3f0b43a3fbfe65fbe9a0db079a24d
|
7
|
+
data.tar.gz: c0ae097eaa91467cd5bc5d944aa65ba9cd43899d8498725c900c6a96bcd8c332a8fedd0610f0c092a5df444e500c4a72119ee3de29df92d5748c4990c2f86282
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.travis.yml
CHANGED
@@ -10,6 +10,8 @@ env:
|
|
10
10
|
- DB=postgres
|
11
11
|
- DB=sqlite
|
12
12
|
|
13
|
+
sudo: false
|
14
|
+
|
13
15
|
before_script:
|
14
16
|
- sh -c "if [ \"$DB\" = 'mysql' ]; then mysql -e 'create database paper_trail_test;'; fi"
|
15
17
|
- sh -c "if [ \"$DB\" = 'mysql' ]; then mysql -e 'create database paper_trail_bar; '; fi"
|
@@ -25,6 +27,9 @@ gemfile:
|
|
25
27
|
matrix:
|
26
28
|
fast_finish: true
|
27
29
|
allow_failures:
|
30
|
+
- rvm: jruby-19mode
|
31
|
+
env: DB=postgres
|
32
|
+
gemfile: Gemfile
|
28
33
|
- rvm: jruby-18mode
|
29
34
|
gemfile: Gemfile
|
30
35
|
- rvm: 1.8.7
|
data/CHANGELOG.md
CHANGED
@@ -1,30 +1,44 @@
|
|
1
|
-
##
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
- [#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
1
|
+
## 4.0.0 (Unreleased)
|
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
|
+
```
|
7
|
+
|
8
|
+
- [#439](https://github.com/airblade/paper_trail/pull/439) / [#12](https://github.com/airblade/paper_trail/issues/12) -
|
9
|
+
Support for versioning of associations (Has Many, Has One, HABTM, etc.)
|
10
|
+
- [#440](https://github.com/airblade/paper_trail/pull/440) - `versions` association should clear/reload after a transaction rollback.
|
11
|
+
- [#438](https://github.com/airblade/paper_trail/issues/438) - `Model.paper_trail_enabled_for_model?` should return `false` if
|
12
|
+
`has_paper_trail` has not been declared on the class.
|
13
|
+
- [#427](https://github.com/airblade/paper_trail/pull/427) - Fix `reify` method in context of model where a column has been removed.
|
14
|
+
- [#416](https://github.com/airblade/paper_trail/issues/416) - Added a `config` option for enabling/disabling
|
15
|
+
utilization of `serialized_attributes` for `ActiveRecord`, necessary because `serialized_attributes` has been
|
16
|
+
deprecated in `ActiveRecord` version `4.2` and will be removed in version `5.0`
|
17
|
+
- [#414](https://github.com/airblade/paper_trail/issues/414) - Fix functionality `ignore` argument to `has_paper_trail`
|
18
|
+
in `ActiveRecord` 4.
|
19
|
+
- [#399](https://github.com/airblade/paper_trail/pull/399) - Add `:dup` argument for options hash to `reify` which forces a new model instance.
|
20
|
+
- [#394](https://github.com/airblade/paper_trail/pull/394) - Add RSpec matcher `have_a_version_with` for easier testing.
|
21
|
+
- [#391](https://github.com/airblade/paper_trail/issues/391) - `object_changes` value should dump to `YAML` as a normal `Hash`
|
22
|
+
instead of an `ActiveSupport::HashWithIndifferentAccess`.
|
23
|
+
- [#381](https://github.com/airblade/paper_trail/issues/381) - `Rspec` and `Cucumber` helpers should not be loaded by
|
24
|
+
default, regardless of whether those libraries are loaded.
|
25
|
+
- [#375](https://github.com/airblade/paper_trail/pull/375) / [#374](https://github.com/airblade/paper_trail/issues/374) /
|
26
|
+
[#354](https://github.com/airblade/paper_trail/issues/354) / [#131](https://github.com/airblade/paper_trail/issues/131) -
|
27
|
+
Versions should be built with `after_` callbacks so the timestamp field for a version can be forced to match the
|
28
|
+
corresponding timestamp in the database for the state persistence of a change to the base (versioned) model.
|
29
|
+
- [#347](https://github.com/airblade/paper_trail/pull/347) - Autoload `ActiveRecord` models in via a `Rails::Engine` when
|
30
|
+
the gem is used with `Rails`.
|
31
|
+
- Methods handling serialized attributes should fallback to the currently set Serializer instead of always falling back
|
32
|
+
to `PaperTrail::Serializers::YAML`.
|
19
33
|
|
20
34
|
## 3.0.6
|
21
35
|
|
22
|
-
- [#414](https://github.com/airblade/paper_trail/issues/414) -
|
23
|
-
|
36
|
+
- [#414](https://github.com/airblade/paper_trail/issues/414) - Backport fix for `ignore` argument to `has_paper_trail` in
|
37
|
+
`ActiveRecord` 4.
|
24
38
|
|
25
39
|
## 3.0.5
|
26
40
|
|
27
|
-
- [#401](https://github.com/airblade/paper_trail/issues/401) / [#406](https://github.com/airblade/paper_trail/issues/406)
|
41
|
+
- [#401](https://github.com/airblade/paper_trail/issues/401) / [#406](https://github.com/airblade/paper_trail/issues/406)
|
28
42
|
`PaperTrail::Version` class is not loaded via a `Rails::Engine`, even when the gem is used with in Rails. This feature has
|
29
43
|
will be re-introduced in version `4.0`.
|
30
44
|
- [#398](https://github.com/airblade/paper_trail/pull/398) - Only require the `RSpec` helper if `RSpec::Core` is required.
|
@@ -39,7 +53,7 @@ in the `PaperTrail::Version` class through a `Rails::Engine` when the gem is use
|
|
39
53
|
- [#383](https://github.com/airblade/paper_trail/pull/383) - Make gem compatible with `ActiveRecord::Enum` (available in `ActiveRecord` 4.1+).
|
40
54
|
- [#380](https://github.com/airblade/paper_trail/pull/380) / [#377](https://github.com/airblade/paper_trail/issues/377) -
|
41
55
|
Add `VersionConcern#where_object` instance method; acts as a helper for querying against the `object` column in versions table.
|
42
|
-
- [#373](https://github.com/airblade/paper_trail/pull/373) - Fix default sort order for the `versions` association in `ActiveRecord` 4.1
|
56
|
+
- [#373](https://github.com/airblade/paper_trail/pull/373) - Fix default sort order for the `versions` association in `ActiveRecord` 4.1.
|
43
57
|
- [#372](https://github.com/airblade/paper_trail/pull/372) - Use [Arel](https://github.com/rails/arel) for SQL construction.
|
44
58
|
- [#365](https://github.com/airblade/paper_trail/issues/365) - `VersionConcern#version_at` should return `nil` when receiving a timestamp
|
45
59
|
that occured after the object was destroyed.
|
@@ -71,7 +85,7 @@ in the `PaperTrail::Version` class through a `Rails::Engine` when the gem is use
|
|
71
85
|
with Rails `4.1.0.rc1`.
|
72
86
|
- [#334](https://github.com/airblade/paper_trail/pull/334) - Add small-scope `whodunnit` method to `PaperTrail::Model::InstanceMethods`.
|
73
87
|
- [#329](https://github.com/airblade/paper_trail/issues/329) - Add `touch_with_version` method to `PaperTrail::Model::InstanceMethods`,
|
74
|
-
to allow for generating a version
|
88
|
+
to allow for generating a version `touch`ing a model.
|
75
89
|
- [#328](https://github.com/airblade/paper_trail/pull/328) / [#326](https://github.com/airblade/paper_trail/issues/326) /
|
76
90
|
[#307](https://github.com/airblade/paper_trail/issues/307) - `Model.paper_trail_enabled_for_model?` and
|
77
91
|
`model_instance.without_versioning` is now thread-safe.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# PaperTrail [![Build Status](https://
|
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
2
|
|
3
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
4
|
|
@@ -12,7 +12,7 @@ There's an excellent [RailsCast on implementing Undo with Paper Trail](http://ra
|
|
12
12
|
* Allows you to get at every version, including the original, even once destroyed.
|
13
13
|
* Allows you to get at every version even if the schema has since changed.
|
14
14
|
* Allows you to get at the version as of a particular time.
|
15
|
-
* Option to automatically restore `has_one` associations as they were at the time.
|
15
|
+
* Option to automatically restore `has_one`, `has_many` and `has_many :through` associations as they were at the time.
|
16
16
|
* Automatically records who was responsible via your controller. PaperTrail calls `current_user` by default, if it exists, but you can have it call any method you like.
|
17
17
|
* Allows you to set who is responsible at model-level (useful for migrations).
|
18
18
|
* Allows you to store arbitrary model-level metadata with each version (useful for filtering versions).
|
@@ -30,7 +30,9 @@ There's an excellent [RailsCast on implementing Undo with Paper Trail](http://ra
|
|
30
30
|
|
31
31
|
## Compatibility
|
32
32
|
|
33
|
-
Works with ActiveRecord
|
33
|
+
Works with `ActiveRecord` 3+. Note: this code is on the `master` branch and tagged `v4.x`.
|
34
|
+
|
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.
|
34
36
|
|
35
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.
|
36
38
|
|
@@ -42,7 +44,7 @@ The Rails 2.3 code is on the [`rails2`](https://github.com/airblade/paper_trail/
|
|
42
44
|
|
43
45
|
1. Add PaperTrail to your `Gemfile`.
|
44
46
|
|
45
|
-
`gem 'paper_trail', '~> 3.0.
|
47
|
+
`gem 'paper_trail', '~> 3.0.6'`
|
46
48
|
|
47
49
|
2. Generate a migration which will add a `versions` table to your database.
|
48
50
|
|
@@ -64,7 +66,7 @@ your applications `ActiveRecord` connection in a manner similar to the way `Rail
|
|
64
66
|
|
65
67
|
1. Add PaperTrail to your `Gemfile`.
|
66
68
|
|
67
|
-
`gem 'paper_trail', '~> 3.0.
|
69
|
+
`gem 'paper_trail', '~> 3.0.6'`
|
68
70
|
|
69
71
|
2. Generate a migration to add a `versions` table to your database.
|
70
72
|
|
@@ -115,7 +117,7 @@ widget.version
|
|
115
117
|
widget.live?
|
116
118
|
|
117
119
|
# Returns who put the widget into its current state.
|
118
|
-
widget.
|
120
|
+
widget.originator
|
119
121
|
|
120
122
|
# Returns the widget (not a version) as it looked at the given timestamp.
|
121
123
|
widget.version_at(timestamp)
|
@@ -135,7 +137,7 @@ Widget.paper_trail_off!
|
|
135
137
|
# Turn PaperTrail on for all widgets.
|
136
138
|
Widget.paper_trail_on!
|
137
139
|
|
138
|
-
# Check
|
140
|
+
# Check whether PaperTrail is enabled for all widgets.
|
139
141
|
Widget.paper_trail_enabled_for_model?
|
140
142
|
widget.paper_trail_enabled_for_model?
|
141
143
|
```
|
@@ -146,8 +148,11 @@ And a `PaperTrail::Version` instance has these methods:
|
|
146
148
|
# Returns the item restored from this version.
|
147
149
|
version.reify(options = {})
|
148
150
|
|
151
|
+
# Return a new item from this version
|
152
|
+
version.reify(dup: true)
|
153
|
+
|
149
154
|
# Returns who put the item into the state stored in this version.
|
150
|
-
version.
|
155
|
+
version.originator
|
151
156
|
|
152
157
|
# Returns who changed the item from the state it had in this version.
|
153
158
|
version.terminator
|
@@ -165,6 +170,9 @@ version.index
|
|
165
170
|
|
166
171
|
# Returns the event that caused this version (create|update|destroy).
|
167
172
|
version.event
|
173
|
+
|
174
|
+
# Query versions objects by attributes.
|
175
|
+
PaperTrail::Version.where_object(attr1: val1, attr2: val2)
|
168
176
|
```
|
169
177
|
|
170
178
|
In your controllers you can override these methods:
|
@@ -241,7 +249,7 @@ Here's a helpful table showing what PaperTrail stores:
|
|
241
249
|
<tr>
|
242
250
|
<td>update</td>
|
243
251
|
<td>widget</td>
|
244
|
-
<td>widget
|
252
|
+
<td>widget</td>
|
245
253
|
<tr>
|
246
254
|
<td>destroy</td>
|
247
255
|
<td>widget</td>
|
@@ -433,7 +441,7 @@ You can find out which of an item's versions yours is:
|
|
433
441
|
>> current_version_number = version.index # 0-based
|
434
442
|
```
|
435
443
|
|
436
|
-
|
444
|
+
If you got an item by reifying one of its versions, you can navigate back to the version it came from:
|
437
445
|
|
438
446
|
```ruby
|
439
447
|
>> latest_version = Widget.find(42).versions.last
|
@@ -450,6 +458,13 @@ You can find out whether a model instance is the current, live one -- or whether
|
|
450
458
|
>> widget.live? # false
|
451
459
|
```
|
452
460
|
|
461
|
+
And you can perform `WHERE` queries for object versions based on attributes:
|
462
|
+
|
463
|
+
```ruby
|
464
|
+
>> # All versions that meet these criteria.
|
465
|
+
>> PaperTrail::Version.where_object(content: "Hello", title: "Article")
|
466
|
+
```
|
467
|
+
|
453
468
|
## Finding Out Who Was Responsible For A Change
|
454
469
|
|
455
470
|
If your `ApplicationController` has a `current_user` method, PaperTrail will store the value it returns in the version's `whodunnit` column. 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:
|
@@ -481,6 +496,10 @@ You can avoid having to do this manually by setting your initializer to pick up
|
|
481
496
|
|
482
497
|
```ruby
|
483
498
|
# config/initializers/paper_trail.rb
|
499
|
+
|
500
|
+
# the following line is required for PaperTrail >= 4.0.0 with Rails
|
501
|
+
PaperTrail::Rails::Engine.eager_load!
|
502
|
+
|
484
503
|
if defined?(::Rails::Console)
|
485
504
|
PaperTrail.whodunnit = "#{`whoami`.strip}: console"
|
486
505
|
elsif File.basename($0) == "rake"
|
@@ -504,22 +523,22 @@ Sometimes you want to define who is responsible for a change in a small scope wi
|
|
504
523
|
|
505
524
|
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`.
|
506
525
|
|
507
|
-
To find out who made a version's object look that way, use `version.
|
526
|
+
To find out who made a version's object look that way, use `version.originator`. And to find out who made a "live" object look like it does, use `originator` on the object.
|
508
527
|
|
509
528
|
```ruby
|
510
529
|
>> widget = Widget.find 153 # assume widget has 0 versions
|
511
530
|
>> PaperTrail.whodunnit = 'Alice'
|
512
531
|
>> widget.update_attributes :name => 'Yankee'
|
513
|
-
>> widget
|
532
|
+
>> widget.originator # 'Alice'
|
514
533
|
>> PaperTrail.whodunnit = 'Bob'
|
515
534
|
>> widget.update_attributes :name => 'Zulu'
|
516
|
-
>> widget.
|
535
|
+
>> widget.originator # 'Bob'
|
517
536
|
>> first_version, last_version = widget.versions.first, widget.versions.last
|
518
537
|
>> first_version.whodunnit # 'Alice'
|
519
|
-
>> first_version.
|
538
|
+
>> first_version.originator # nil
|
520
539
|
>> first_version.terminator # 'Alice'
|
521
540
|
>> last_version.whodunnit # 'Bob'
|
522
|
-
>> last_version.
|
541
|
+
>> last_version.originator # 'Alice'
|
523
542
|
>> last_version.terminator # 'Bob'
|
524
543
|
```
|
525
544
|
|
@@ -545,7 +564,7 @@ If you are using Postgres, you should also define the sequence that your custom
|
|
545
564
|
```ruby
|
546
565
|
class PostVersion < PaperTrail::Version
|
547
566
|
self.table_name = :post_versions
|
548
|
-
self.sequence_name = :
|
567
|
+
self.sequence_name = :post_version_id_seq
|
549
568
|
end
|
550
569
|
```
|
551
570
|
|
@@ -556,6 +575,11 @@ If you only use custom version classes and don't use PaperTrail's built-in one,
|
|
556
575
|
- either declare the `PaperTrail::Version` class to be abstract like this (in an initializer):
|
557
576
|
|
558
577
|
```ruby
|
578
|
+
# config/initializers/paper_trail.rb
|
579
|
+
|
580
|
+
# the following line is required for PaperTrail >= 4.0.0 with Rails
|
581
|
+
PaperTrail::Rails::Engine.eager_load!
|
582
|
+
|
559
583
|
PaperTrail::Version.module_eval do
|
560
584
|
self.abstract_class = true
|
561
585
|
end
|
@@ -583,14 +607,9 @@ end
|
|
583
607
|
|
584
608
|
## Associations
|
585
609
|
|
586
|
-
|
587
|
-
|
588
|
-
If you can think of a good way to achieve this, please let me know.
|
589
|
-
|
610
|
+
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.
|
590
611
|
|
591
|
-
|
592
|
-
|
593
|
-
PaperTrail can restore `:has_one` associations as they were at (actually, 3 seconds before) the time.
|
612
|
+
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:
|
594
613
|
|
595
614
|
```ruby
|
596
615
|
class Location < ActiveRecord::Base
|
@@ -614,21 +633,55 @@ end
|
|
614
633
|
>> t.location.latitude # 12.345
|
615
634
|
```
|
616
635
|
|
617
|
-
|
618
|
-
|
619
|
-
The correct solution is to make PaperTrail aware of requests or transactions (c.f. [Efficiency's transaction ID middleware](http://github.com/efficiency20/ops_middleware/blob/master/lib/e20/ops/middleware/transaction_id_middleware.rb)). In the meantime we work around the problem by finding the child as it was a few seconds before the parent was updated. By default we go 3 seconds before but you can change this by passing the desired number of seconds to the `:has_one` option:
|
636
|
+
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).
|
620
637
|
|
621
638
|
```ruby
|
622
|
-
>>
|
639
|
+
>> treasure.amount # 100
|
640
|
+
>> treasure.location.latitude # 12.345
|
641
|
+
|
642
|
+
>> Treasure.transaction do
|
643
|
+
>> treasure.location.update_attributes :latitude => 54.321
|
644
|
+
>> treasure.update_attributes :amount => 153
|
645
|
+
>> end
|
646
|
+
|
647
|
+
>> t = treasure.versions.last.reify(:has_one => true)
|
648
|
+
>> t.amount # 100
|
649
|
+
>> t.location.latitude # 12.345, instead of 54.321
|
623
650
|
```
|
624
651
|
|
625
|
-
|
652
|
+
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.
|
653
|
+
|
654
|
+
```ruby
|
655
|
+
class Widget < ActiveRecord::Base
|
656
|
+
has_paper_trail
|
657
|
+
has_one :wotsit, autosave: true
|
658
|
+
end
|
659
|
+
|
660
|
+
class Wotsit < ActiveRecord::Base
|
661
|
+
has_paper_trail
|
662
|
+
belongs_to :widget
|
663
|
+
end
|
664
|
+
|
665
|
+
>> widget = Widget.create(:name => 'widget_0')
|
666
|
+
>> widget.update_attributes(:name => 'widget_1')
|
667
|
+
>> widget.create_wotsit(:name => 'wotsit')
|
626
668
|
|
669
|
+
>> widget_0 = widget.versions.last.reify(:has_one => true)
|
670
|
+
>> widget_0.wotsit # nil
|
671
|
+
|
672
|
+
>> widget_0 = widget.versions.last.reify(:has_one => true, :mark_for_destruction => true)
|
673
|
+
>> widget_0.wotsit.marked_for_destruction? # true
|
674
|
+
>> widget_0.save!
|
675
|
+
>> widget.reload.wotsit # nil
|
676
|
+
```
|
627
677
|
|
678
|
+
**Caveats:**
|
628
679
|
|
629
|
-
|
680
|
+
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)
|
681
|
+
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.
|
682
|
+
3. PaperTrail only reifies the first level of associations, i.e., it does not reify any associations of its associations, and so on.
|
683
|
+
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:
|
630
684
|
|
631
|
-
PaperTrail can track most changes to the join table. Specifically it can track all additions but it can only track removals which fire the `after_destroy` callback on the join table. Here are some examples:
|
632
685
|
|
633
686
|
Given these models:
|
634
687
|
|
@@ -659,13 +712,14 @@ Then each of the following will store authorship versions:
|
|
659
712
|
>> @book.authors.create :name => 'Tolstoy'
|
660
713
|
>> @book.authorships.last.destroy
|
661
714
|
>> @book.authorships.clear
|
715
|
+
>> @book.author_ids = [@solzhenistyn.id, @dostoyevsky.id]
|
662
716
|
```
|
663
717
|
|
664
718
|
But none of these will:
|
665
719
|
|
666
720
|
```ruby
|
667
721
|
>> @book.authors.delete @tolstoy
|
668
|
-
>> @book.author_ids = [
|
722
|
+
>> @book.author_ids = []
|
669
723
|
>> @book.authors = []
|
670
724
|
```
|
671
725
|
|
@@ -690,9 +744,6 @@ end
|
|
690
744
|
|
691
745
|
See [issue 113](https://github.com/airblade/paper_trail/issues/113) for a discussion about this.
|
692
746
|
|
693
|
-
There may be a way to store authorship versions, probably using association callbacks, no matter how the collection is manipulated but I haven't found it yet. Let me know if you do.
|
694
|
-
|
695
|
-
There has been some discussion of how to implement PaperTrail to fully track HABTM associations. See [pull 90](https://github.com/airblade/paper_trail/pull/90) for an implementation that has worked for some.
|
696
747
|
|
697
748
|
## Storing metadata
|
698
749
|
|
@@ -721,6 +772,10 @@ For example:
|
|
721
772
|
|
722
773
|
```ruby
|
723
774
|
# config/initializers/paper_trail.rb
|
775
|
+
|
776
|
+
# the following line is required for PaperTrail >= 4.0.0 with Rails
|
777
|
+
PaperTrail::Rails::Engine.eager_load!
|
778
|
+
|
724
779
|
module PaperTrail
|
725
780
|
class Version < ActiveRecord::Base
|
726
781
|
attr_accessible :author_id, :word_count, :answer
|
@@ -849,7 +904,7 @@ end
|
|
849
904
|
|
850
905
|
### Per class
|
851
906
|
|
852
|
-
If you are about change some widgets and you don't want a paper trail of your changes, you can turn PaperTrail off like this:
|
907
|
+
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:
|
853
908
|
|
854
909
|
```ruby
|
855
910
|
>> Widget.paper_trail_off!
|
@@ -887,26 +942,26 @@ By default, PaperTrail stores your changes as a `YAML` dump. You can override th
|
|
887
942
|
|
888
943
|
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:
|
889
944
|
|
890
|
-
* [YAML](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/yaml.rb) - Default
|
891
|
-
* [JSON](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/json.rb)
|
945
|
+
* [PaperTrail::Serializers::YAML](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/yaml.rb) - Default
|
946
|
+
* [PaperTrail::Serializers::JSON](https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/serializers/json.rb)
|
892
947
|
|
893
|
-
|
948
|
+
## SerializedAttributes support
|
894
949
|
|
895
|
-
|
896
|
-
|
897
|
-
|
950
|
+
PaperTrail has a config option that can be used to enable/disable whether PaperTrail attempts to utilize
|
951
|
+
`ActiveRecord`'s `serialized_attributes` feature. Note: This is enabled by default when PaperTrail is used
|
952
|
+
with `ActiveRecord` version < `4.2`, and disabled by default when used with ActiveRecord `4.2.x`. Since
|
953
|
+
`serialized_attributes` will be removed in `ActiveRecord` version `5.0`, this configuration value
|
954
|
+
has no functionality when PaperTrail is used with version `5.0` or greater.
|
898
955
|
|
899
956
|
```ruby
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
957
|
+
# Enable support
|
958
|
+
>> PaperTrail.config.serialized_attributes = true
|
959
|
+
# Disable support
|
960
|
+
>> PaperTrail.config.serialized_attributes = false
|
961
|
+
# Get current setting
|
962
|
+
>> PaperTrail.serialized_attributes?
|
906
963
|
```
|
907
964
|
|
908
|
-
Note: You don't need to use a particular serializer for the PostgreSQL `JSON` column type.
|
909
|
-
|
910
965
|
## Limiting the number of versions created per object instance
|
911
966
|
|
912
967
|
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,
|
@@ -939,24 +994,39 @@ You may want to turn PaperTrail off to speed up your tests. See the [Turning Pa
|
|
939
994
|
|
940
995
|
### RSpec
|
941
996
|
|
942
|
-
PaperTrail provides a helper that works with [RSpec](https://github.com/rspec/rspec)
|
943
|
-
|
997
|
+
PaperTrail provides a helper that works with [RSpec](https://github.com/rspec/rspec)
|
998
|
+
to make it easier to control when `PaperTrail` is enabled during testing.
|
999
|
+
|
1000
|
+
If you wish to use the helper, you will need to require it in your RSpec test helper like so:
|
1001
|
+
|
1002
|
+
```ruby
|
1003
|
+
# spec/rails_helper.rb
|
1004
|
+
|
1005
|
+
ENV["RAILS_ENV"] ||= 'test'
|
1006
|
+
require 'spec_helper'
|
1007
|
+
require File.expand_path("../../config/environment", __FILE__)
|
1008
|
+
require 'rspec/rails'
|
1009
|
+
...
|
1010
|
+
require 'paper_trail/frameworks/rspec'
|
1011
|
+
```
|
1012
|
+
|
1013
|
+
When the helper is loaded, PaperTrail will be turned off for all tests by default.
|
944
1014
|
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:
|
945
1015
|
|
946
1016
|
```ruby
|
947
1017
|
describe "RSpec test group" do
|
948
1018
|
it 'by default, PaperTrail will be turned off' do
|
949
|
-
PaperTrail.
|
1019
|
+
expect(PaperTrail).to_not be_enabled
|
950
1020
|
end
|
951
1021
|
|
952
1022
|
with_versioning do
|
953
1023
|
it 'within a `with_versioning` block it will be turned on' do
|
954
|
-
PaperTrail.
|
1024
|
+
expect(PaperTrail).to be_enabled
|
955
1025
|
end
|
956
1026
|
end
|
957
1027
|
|
958
1028
|
it 'can be turned on at the `it` or `describe` level like this', :versioning => true do
|
959
|
-
PaperTrail.
|
1029
|
+
expect(PaperTrail).to be_enabled
|
960
1030
|
end
|
961
1031
|
end
|
962
1032
|
```
|
@@ -971,7 +1041,9 @@ class Widget < ActiveRecord::Base
|
|
971
1041
|
end
|
972
1042
|
|
973
1043
|
describe Widget do
|
974
|
-
it
|
1044
|
+
it "is not versioned by default" do
|
1045
|
+
is_expected.to_not be_versioned
|
1046
|
+
end
|
975
1047
|
|
976
1048
|
describe "add versioning to the `Widget` class" do
|
977
1049
|
before(:all) do
|
@@ -980,15 +1052,47 @@ describe Widget do
|
|
980
1052
|
end
|
981
1053
|
end
|
982
1054
|
|
983
|
-
it
|
1055
|
+
it "enables paper trail" do
|
1056
|
+
is_expected.to be_versioned
|
1057
|
+
end
|
984
1058
|
end
|
985
1059
|
end
|
986
1060
|
```
|
987
1061
|
|
1062
|
+
It is also possible to do assertions on the versions using `have_a_version_with` matcher
|
1063
|
+
|
1064
|
+
```
|
1065
|
+
describe '`have_a_version_with` matcher' do
|
1066
|
+
before do
|
1067
|
+
widget.update_attributes!(:name => 'Leonard', :an_integer => 1 )
|
1068
|
+
widget.update_attributes!(:name => 'Tom')
|
1069
|
+
widget.update_attributes!(:name => 'Bob')
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
it "is possible to do assertions on versions" do
|
1073
|
+
expect(widget).to have_a_version_with :name => 'Leonard', :an_integer => 1
|
1074
|
+
expect(widget).to have_a_version_with :an_integer => 1
|
1075
|
+
expect(widget).to have_a_version_with :name => 'Tom'
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
```
|
1080
|
+
|
988
1081
|
### Cucumber
|
989
1082
|
|
990
1083
|
PaperTrail provides a helper for [Cucumber](http://cukes.info) that works similar to the RSpec helper.
|
991
|
-
|
1084
|
+
If you wish to use the helper, you will need to require in your cucumber helper like so:
|
1085
|
+
|
1086
|
+
```ruby
|
1087
|
+
# features/support/env.rb
|
1088
|
+
|
1089
|
+
ENV["RAILS_ENV"] ||= "cucumber"
|
1090
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
|
1091
|
+
...
|
1092
|
+
require 'paper_trail/frameworks/cucumber'
|
1093
|
+
```
|
1094
|
+
|
1095
|
+
When the helper is loaded, PaperTrail will be turned off for all scenarios by a `before` hook added by the helper by default.
|
992
1096
|
When you wish to enable PaperTrail for a scenario, you can wrap code in a `with_versioning` block in a step, like so:
|
993
1097
|
|
994
1098
|
```ruby
|
@@ -1008,16 +1112,16 @@ If you wish to use the `RSpec` or `Cucumber` helpers with [Spork](https://github
|
|
1008
1112
|
manually require the helper(s) in your `prefork` block on your test helper, like so:
|
1009
1113
|
|
1010
1114
|
```ruby
|
1011
|
-
# spec/
|
1115
|
+
# spec/rails_helper.rb
|
1012
1116
|
|
1013
1117
|
require 'spork'
|
1014
1118
|
|
1015
1119
|
Spork.prefork do
|
1016
1120
|
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
1017
1121
|
ENV["RAILS_ENV"] ||= 'test'
|
1122
|
+
require 'spec_helper'
|
1018
1123
|
require File.expand_path("../../config/environment", __FILE__)
|
1019
1124
|
require 'rspec/rails'
|
1020
|
-
require 'rspec/autorun'
|
1021
1125
|
require 'paper_trail/frameworks/rspec'
|
1022
1126
|
require 'paper_trail/frameworks/cucumber'
|
1023
1127
|
...
|
@@ -1030,9 +1134,10 @@ If you wish to use the `RSpec` or `Cucumber` helpers with [Zeus](https://github.
|
|
1030
1134
|
manually require the helper(s) in your test helper, like so:
|
1031
1135
|
|
1032
1136
|
```ruby
|
1033
|
-
# spec/
|
1137
|
+
# spec/rails_helper.rb
|
1034
1138
|
|
1035
1139
|
ENV["RAILS_ENV"] ||= 'test'
|
1140
|
+
require 'spec_helper'
|
1036
1141
|
require File.expand_path("../../config/environment", __FILE__)
|
1037
1142
|
require 'rspec/rails'
|
1038
1143
|
require 'paper_trail/frameworks/rspec'
|
@@ -1040,9 +1145,9 @@ require 'paper_trail/frameworks/rspec'
|
|
1040
1145
|
|
1041
1146
|
## Testing PaperTrail
|
1042
1147
|
|
1043
|
-
Paper Trail has facilities to test
|
1148
|
+
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.
|
1044
1149
|
|
1045
|
-
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.
|
1150
|
+
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.
|
1046
1151
|
|
1047
1152
|
```
|
1048
1153
|
export DB=postgres
|
@@ -1052,6 +1157,7 @@ export DB=sqlite # this is default
|
|
1052
1157
|
|
1053
1158
|
## Articles
|
1054
1159
|
|
1160
|
+
* [Jutsu #8 - Version your RoR models with PaperTrail](http://samurails.com/gems/papertrail/), [Thibault](http://samurails.com/about-me/), 29th September 2014
|
1055
1161
|
* [Versioning with PaperTrail](http://www.sitepoint.com/versioning-papertrail), [Ilya Bodrov](http://www.sitepoint.com/author/ibodrov), 10th April 2014
|
1056
1162
|
* [Using PaperTrail to track stack traces](http://rubyrailsexpert.com/?p=36), T James Corcoran's blog, 1st October 2013.
|
1057
1163
|
* [RailsCast #255 - Undo with PaperTrail](http://railscasts.com/episodes/255-undo-with-paper-trail), 28th February 2011.
|
@@ -1118,7 +1224,8 @@ Many thanks to:
|
|
1118
1224
|
* [Chulki Lee](https://github.com/chulkilee)
|
1119
1225
|
* [Lucas Souza](https://github.com/lucasas)
|
1120
1226
|
* [Russell Osborne](https://github.com/rposborne)
|
1121
|
-
|
1227
|
+
* [Ben Li](https://github.com/bli)
|
1228
|
+
* [Felix Liu](https://github.com/lyfeyaj)
|
1122
1229
|
|
1123
1230
|
## Inspirations
|
1124
1231
|
|