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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -2
  4. data/.travis.yml +5 -0
  5. data/CHANGELOG.md +37 -23
  6. data/README.md +170 -63
  7. data/gemfiles/3.0.gemfile +10 -4
  8. data/lib/generators/paper_trail/install_generator.rb +19 -3
  9. data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb +11 -0
  10. data/lib/generators/paper_trail/templates/create_version_associations.rb +17 -0
  11. data/lib/paper_trail.rb +24 -4
  12. data/lib/paper_trail/cleaner.rb +3 -3
  13. data/lib/paper_trail/config.rb +17 -0
  14. data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +7 -0
  15. data/lib/paper_trail/frameworks/rails.rb +1 -0
  16. data/lib/paper_trail/frameworks/rspec.rb +5 -0
  17. data/lib/paper_trail/has_paper_trail.rb +112 -38
  18. data/lib/paper_trail/version_association_concern.rb +13 -0
  19. data/lib/paper_trail/version_concern.rb +145 -38
  20. data/lib/paper_trail/version_number.rb +3 -3
  21. data/paper_trail.gemspec +11 -4
  22. data/spec/generators/install_generator_spec.rb +4 -4
  23. data/spec/models/fluxor_spec.rb +19 -0
  24. data/spec/models/gadget_spec.rb +10 -10
  25. data/spec/models/joined_version_spec.rb +9 -9
  26. data/spec/models/post_with_status_spec.rb +3 -3
  27. data/spec/models/version_spec.rb +49 -71
  28. data/spec/models/widget_spec.rb +124 -71
  29. data/spec/modules/version_concern_spec.rb +8 -8
  30. data/spec/modules/version_number_spec.rb +16 -16
  31. data/spec/paper_trail_spec.rb +17 -17
  32. data/spec/rails_helper.rb +34 -0
  33. data/spec/requests/articles_spec.rb +11 -11
  34. data/spec/spec_helper.rb +77 -36
  35. data/test/dummy/app/models/animal.rb +0 -2
  36. data/test/dummy/app/models/book.rb +4 -0
  37. data/test/dummy/app/models/customer.rb +4 -0
  38. data/test/dummy/app/models/editor.rb +4 -0
  39. data/test/dummy/app/models/editorship.rb +5 -0
  40. data/test/dummy/app/models/line_item.rb +4 -0
  41. data/test/dummy/app/models/order.rb +5 -0
  42. data/test/dummy/app/models/person.rb +1 -1
  43. data/test/dummy/app/models/post.rb +0 -1
  44. data/test/dummy/app/models/song.rb +0 -20
  45. data/test/dummy/app/models/widget.rb +4 -0
  46. data/test/dummy/config/application.rb +3 -0
  47. data/test/dummy/config/initializers/paper_trail.rb +1 -1
  48. data/test/dummy/db/migrate/20110208155312_set_up_test_tables.rb +41 -0
  49. data/test/dummy/db/schema.rb +95 -25
  50. data/test/dummy/public/404.html +26 -0
  51. data/test/dummy/public/422.html +26 -0
  52. data/test/dummy/public/500.html +26 -0
  53. data/test/dummy/public/favicon.ico +0 -0
  54. data/test/dummy/public/javascripts/application.js +2 -0
  55. data/test/dummy/public/javascripts/controls.js +965 -0
  56. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  57. data/test/dummy/public/javascripts/effects.js +1123 -0
  58. data/test/dummy/public/javascripts/rails.js +175 -0
  59. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  60. data/test/test_helper.rb +2 -2
  61. data/test/time_travel_helper.rb +15 -0
  62. data/test/unit/model_test.rb +613 -185
  63. data/test/unit/serializer_test.rb +3 -3
  64. metadata +104 -54
  65. data/spec/models/animal_spec.rb +0 -19
  66. data/test/dummy/public/javascripts/prototype.js +0 -6001
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de205e84196241d1165f38050f43114114b47cca
4
- data.tar.gz: 2401292c3098d9050260528490556294f8034a6d
3
+ metadata.gz: 5a4a5a1bfd11ee6bc8acabb03cb262fe4691951a
4
+ data.tar.gz: 6854403b0c404645e52c94154d4d8268642969c1
5
5
  SHA512:
6
- metadata.gz: 89b14153e1f66e8e043532c78a6cda7033e2aa902c9d191fddc29ece1a4036c9808c26e345ca3360d4c70f6d1193eac14eb5884025a45750361419e290217d2f
7
- data.tar.gz: 531199dd212d76562c9baed8033fd3ca765b5458b19c3b8029e89c8cd3f7a44202cdcb5efd3ad979022c5e4386e47e042fab2617470134a52d3ee6b328779f65
6
+ metadata.gz: 891aa4e3758245d09974c472c49506a4b49b2ac565018acc1afbebe40907c91729b450e45dd9e5c501c6781f7a673f15f9d3f0b43a3fbfe65fbe9a0db079a24d
7
+ data.tar.gz: c0ae097eaa91467cd5bc5d944aa65ba9cd43899d8498725c900c6a96bcd8c332a8fedd0610f0c092a5df444e500c4a72119ee3de29df92d5748c4990c2f86282
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ Gemfile.lock
15
15
  vendor/*
16
16
  .idea
17
17
  .rvmrc
18
+ .tags
19
+ .tags_sorted_by_file
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format progress
2
1
  --color
3
-
2
+ --require spec_helper
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
- ## 3.0.9
2
-
3
- - [#479](https://github.com/airblade/paper_trail/issues/479) - Deprecated `originator` method in favor of
4
- `paper_trail_originator` Deprecation warning informs users that the `originator` of the methods will be
5
- removed in version `4.0`. (Backported from v4)
6
- - Updated deprecation warnings for `Model.paper_trail_on` and `Model.paper_trail_off` to have display correct
7
- version number the methods will be removed (`4.0`)
8
-
9
- ## 3.0.8
10
-
11
- - [#525](https://github.com/airblade/paper_trail/issues/525) / [#512](https://github.com/airblade/paper_trail/pull/512) -
12
- Support for virtual accessors and redefined setter and getter methods. (Backported from v4)
13
-
14
- ## 3.0.7
15
-
16
- - [#404](https://github.com/airblade/paper_trail/issues/404) / [#428](https://github.com/airblade/paper_trail/issues/428) -
17
- Fix errors occuring when a user attempts to update the inheritance column on an STI model instance in `ActiveRecord` 4.1.x.
18
- (Backported from v4)
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) - Fix functionality `ignore` argument to `has_paper_trail`
23
- in `ActiveRecord` 4. (Backported from v4)
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 while `touch`ing a model.
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://travis-ci.org/airblade/paper_trail.svg?branch=3.0-stable)](https://travis-ci.org/airblade/paper_trail) [![Dependency Status](https://img.shields.io/gemnasium/airblade/paper_trail.svg)](https://gemnasium.com/airblade/paper_trail)
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 4 and ActiveRecord 3. Note: this code is on the `master` branch and tagged `v3.x`.
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.9'`
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.9'`
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.paper_trail_originator
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 wheter PaperTrail is enabled for all widgets
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.paper_trail_originator
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'</td>
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
- Finally, if you got an item by reifying one of its versions, you can navigate back to the version it came from:
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.paper_trail_originator`. And to find out who made a "live" object look like it does, call `paper_trail_originator` on the object.
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..paper_trail_originator # 'Alice'
532
+ >> widget.originator # 'Alice'
514
533
  >> PaperTrail.whodunnit = 'Bob'
515
534
  >> widget.update_attributes :name => 'Zulu'
516
- >> widget.paper_trail_originator # 'Bob'
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.paper_trail_originator # nil
538
+ >> first_version.originator # nil
520
539
  >> first_version.terminator # 'Alice'
521
540
  >> last_version.whodunnit # 'Bob'
522
- >> last_version.paper_trail_originator # 'Alice'
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 = :post_versions_id_seq
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
- I haven't yet found a good way to get PaperTrail to automatically restore associations when you reify a model. See [here for a little more info](http://airbladesoftware.com/notes/undo-and-redo-with-papertrail).
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
- ## Has-One Associations
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
- The implementation is complicated by the edge case where the parent and child are updated in one go, e.g. in one web request or database transaction. PaperTrail doesn't know about different models being updated "together", so you can't ask it definitively to get the child as it was before the joint parent-and-child update.
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
- >> t = treasure.versions.last.reify(:has_one => 1) # look back 1 second instead of 3
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
- If you are shuddering, take solace from knowing PaperTrail opts out of these shenanigans by default. This means your `:has_one` associated objects will be the live ones, not the ones the user saw at the time. Since PaperTrail doesn't auto-restore `:has_many` associations (I can't get it to work) or `:belongs_to` (I ran out of time looking at `:has_many`), this at least makes your associations wrong consistently ;)
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
- ## Has-Many-Through Associations
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 = [@solzhenistyn.id, @dostoyevsky.id]
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
- ### PostgreSQL JSON column type support
948
+ ## SerializedAttributes support
894
949
 
895
- If you use PostgreSQL, and would like to store your `object` (and/or `object_changes`) data in a column of
896
- [type `JSON`](http://www.postgresql.org/docs/9.4/static/datatype-json.html),
897
- specify `json` instead of `text` for these columns in your migration:
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
- create_table :versions do |t|
901
- ...
902
- t.json :object # Full object changes
903
- t.json :object_changes # Optional column-level changes
904
- ...
905
- end
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) to make it easier to control when `PaperTrail` is enabled
943
- during testing. By default, PaperTrail will be turned off for all tests.
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.should_not be_enabled
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.should be_enabled
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.should be_enabled
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 { should_not be_versioned }
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 { should be_versioned }
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
- By default, PaperTrail will be turned off for all scenarios by a `before` hook added by the helper.
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/spec_helper.rb
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/spec_helper.rb
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 aganist 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.
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