paper_trail-association_tracking 0.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21bc61f6e8c1ce4cc16c937554bf13facad85e45cbc7323155f877841202ddc6
4
- data.tar.gz: 24958dbdcf40ff4351b2aeec0f2b1303459d65fd92fb058fd3061f6fc7f88faa
3
+ metadata.gz: 8b2d339d9003d50ddc86c1e1cdea91863bcb2908ce4b4c42db9b59937431a3f0
4
+ data.tar.gz: c9638879c3932b3f5c07b549401c60b2c473da0afed2dab078b35deb5b8eae10
5
5
  SHA512:
6
- metadata.gz: a03f3497443252136bf6a43389e6629cc693086cbcb0da9172896f83e1e52c747eb9c4461d27f8ff56090ec6a36b3927f7c98537db1d9b71f593cf9503540a5d
7
- data.tar.gz: 5c10679e72f9b90ba90c92d5af0fc05a6272c3f37a651a1f6471f5adeddb120335ac2aa02eac62c457197a916005144a8788d375ed79da96635471cdb09fb214
6
+ metadata.gz: 79f9131a55657ea99ae5393e5c1bb17e00e79744896f99faf70d2df982c2a38a2c06cb17c8bed7131213de814c10c0e96c29f27782ad1bb064db1778649bc6a7
7
+ data.tar.gz: 2199d8c642a22494c6f792f809bc7cd7143f287f9fc6a2a8cd3ea31243ab100de9914bc27ef06ebeafd902730b46b7747d2f8a5d41ca6e662b81d2354c140315
@@ -1,8 +1,35 @@
1
- # Changelog
1
+ # CHANGELOG
2
2
 
3
- This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the
4
- recommendations of [keepachangelog.com](http://keepachangelog.com/).
3
+ ## Unreleased
5
4
 
6
- ## 0.9.0
5
+ - Nothing yet
7
6
 
8
- - [PaperTrail #1070](https://github.com/paper-trail-gem/paper_trail/issues/1070) - Extracted from paper_trail gem in v9.2 was released.
7
+ ## 2.1.0 - 2020-08-14
8
+
9
+ - [PR #18](https://github.com/westonganger/paper_trail-association_tracking/pull/18) - Improve performance for `Model.reify(has_many: true)` by separating the SQL subquery.
10
+ - [PR #15](https://github.com/westonganger/paper_trail-association_tracking/pull/15) - Recreate `version_associations.foreign_key` index to utilize the new `version_associations.foreign_type` column
11
+ - Update test matrix to support multiple versions of PT-core and ActiveRecord
12
+ - Remove deprecated methods `clear_transaction_id`, `transaction_id` and `transaction_id=`
13
+
14
+ ## 2.0.0 - 2019-01-22
15
+
16
+ - [PR #11](https://github.com/westonganger/paper_trail-association_tracking/issues/11) - Remove null constraint on `version_associations.foreign_type` column which was added in `v1.1.0`. This fixes issues adding the column to existing projects who are upgrading.
17
+ - Add generator `rails g paper_trail_association_tracking:add_foreign_type_to_version_associations` for `versions_associations.foreign_type` column for upgrading applications from `v1.0.0` or earlier.
18
+
19
+ ### How to Upgrade from v1.0.0 or earlier
20
+
21
+ - Run `rails g paper_trail_association_tracking:add_foreign_type_to_version_associations` and then migrate your database.
22
+
23
+ ## 1.1.1 - 2018-01-14
24
+
25
+ - Same as v2 release, this is released simply to maintain a working `v1` branch since `v1.1.0` was broken
26
+
27
+ ## 1.1.0 - 2018-12-28
28
+
29
+ - Note: This release is somewhat broken, please upgrade to `v2.0.0` or stay on `v1.0.0`
30
+ - [PR #10](https://github.com/westonganger/paper_trail-association_tracking/pull/10) - The `has_many: true` option now reifies polymorphic associations. Previously they were skipped.
31
+ - [PR #9](https://github.com/westonganger/paper_trail-association_tracking/pull/9) - The `belongs_to: true` option now reifies polymorphic associations. Previously they were skipped.
32
+
33
+ ## 1.0.0 - 2018-06-04
34
+
35
+ - [PT #1070](https://github.com/paper-trail-gem/paper_trail/issues/1070), [#2](https://github.com/westonganger/paper_trail-association_tracking/issues/2) - Extracted from paper_trail gem in v9.2
data/README.md CHANGED
@@ -1,175 +1,108 @@
1
1
  # PaperTrail-AssociationTracking
2
2
 
3
- [![Build Status][1]][2]
3
+ <a href="https://badge.fury.io/rb/paper_trail-association_tracking" target="_blank"><img height="21" style='border:0px;height:21px;' border='0' src="https://badge.fury.io/rb/paper_trail-association_tracking.svg" alt="Gem Version"></a>
4
+ <a href='https://travis-ci.com/westonganger/paper_trail-association_tracking' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://api.travis-ci.com/westonganger/paper_trail-association_tracking.svg?branch=master' border='0' alt='Build Status' /></a>
5
+ <a href='https://rubygems.org/gems/paper_trail-association_tracking' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://ruby-gem-downloads-badge.herokuapp.com/paper_trail-association_tracking?label=rubygems&type=total&total_label=downloads&color=brightgreen' border='0' alt='RubyGems Downloads' /></a>
4
6
 
5
- Plugin for the [PaperTrail](https://github.com/paper-trail-gem/paper_trail.git) gem to track and reify associations.
7
+ Plugin for the [PaperTrail](https://github.com/paper-trail-gem/paper_trail.git) gem to track and reify associations. This gem was extracted from PaperTrail for v9.2.0 to simplify things in PaperTrail and association tracking separately.
6
8
 
7
9
  **PR's will happily be accepted**
8
10
 
9
- This gem was extracted from PaperTrail in v9.2 to simplify things in PaperTrail and association tracking seperately.
10
- At this time, `paper_trail` has a runtime dependency on this gem and will keep running the existing tests related
11
- to association tracking. This arrangement will be maintained for a few years, if practical.
11
+ PaperTrail-AssociationTracking can restore three types of associations: Has-One, Has-Many, and Has-Many-Through.
12
12
 
13
- A little history lesson, discussed as early as 2009, and first implemented in late 2014, association
14
- tracking was part of PT core until 2018 as an experimental feature and was use at your own risk. This gem now
15
- maintains a list of known issues and we hope the community can help remove some of them via PR's.
13
+ It will store in the `version_associations` table additional information to correlate versions of the association and versions of the model when the associated record is changed. When reifying the model, it will utilize 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.
16
14
 
17
- ## TODO
18
15
 
19
- - Continue removing most-non association specs
20
- - Add consolidated list of paper trail plugins to paper_trail core readme
21
16
 
22
- ## Table of Contents
23
17
 
24
- <!-- toc -->
18
+ ## Table of Contents
25
19
 
26
20
  - [Install](#install)
27
- - [Associations](#associations)
21
+ - [Usage](#usage)
22
+ - [Limitations](#limitations)
28
23
  - [Known Issues](#known-issues)
29
24
  - [Contributing](#contributing)
30
25
  - [Credits](#credits)
31
26
 
32
- <!-- tocstop -->
33
27
 
34
28
  # Install
35
29
 
36
30
  ```ruby
37
31
  # Gemfile
38
32
 
39
- gem 'paper_trail' # Requires v9.2+
33
+ gem 'paper_trail' # Minimum required version is v9.2.0
40
34
  gem 'paper_trail-association_tracking'
41
35
  ```
42
36
 
43
- # Association Tracking
44
-
45
- This plugin currently can restore three types of associations: Has-One, Has-Many, and
46
- Has-Many-Through. In order to do this, you will need to do two things:
37
+ Then run `rails generate paper_trail_association_tracking:install` which will do the following two things for you:
47
38
 
48
39
  1. Create a `version_associations` table
49
- 2. Set `PaperTrail.config.track_associations = true` (e.g. in an initializer)
50
-
51
- Both will be done for you automatically if you install PaperTrail with the
52
- `--with_associations` option
53
- (e.g. `rails generate paper_trail:install --with-associations`)
40
+ 2. Set `PaperTrail.config.track_associations = true` in an initializer
54
41
 
55
- If you want to add this functionality after the initial installation, you will
56
- need to create the `version_associations` table manually, and you will need to
57
- ensure that `PaperTrail.config.track_associations = true` is set.
42
+ # Usage
58
43
 
59
- PaperTrail will store in the `version_associations` table additional information
60
- to correlate versions of the association and versions of the model when the
61
- associated record is changed. When reifying the model, PaperTrail can use this
62
- table, together with the `transaction_id` to find the correct version of the
63
- association and reify it. The `transaction_id` is a unique id for version records
64
- created in the same transaction. It is used to associate the version of the model
65
- and the version of the association that are created in the same transaction.
66
-
67
- To restore Has-One associations as they were at the time, pass option `has_one:
68
- true` to `reify`. To restore Has-Many and Has-Many-Through associations, use
69
- option `has_many: true`. To restore Belongs-To association, use
70
- option `belongs_to: true`. For example:
71
-
72
- ```ruby
73
- class Location < ActiveRecord::Base
74
- belongs_to :treasure
75
- has_paper_trail
76
- end
44
+ First, ensure that you have added `has_paper_trail` to your main model and all associated models that are to be tracked.
77
45
 
78
- class Treasure < ActiveRecord::Base
79
- has_one :location
80
- has_paper_trail
81
- end
46
+ To restore associations as they were at the time you must pass any of the following options to the `reify` method.
82
47
 
83
- treasure.amount # 100
84
- treasure.location.latitude # 12.345
48
+ - To restore Has-Many and Has-Many-Through associations, use option `has_many: true`
49
+ - To restore Has-One associations , use option `has_one: true` to `reify`
50
+ - To restore Belongs-To associations, use option `belongs_to: true`
85
51
 
86
- treasure.update_attributes amount: 153
87
- treasure.location.update_attributes latitude: 54.321
52
+ For example:
88
53
 
89
- t = treasure.versions.last.reify(has_one: true)
90
- t.amount # 100
91
- t.location.latitude # 12.345
54
+ ```ruby
55
+ item.versions.last.reify(has_many: true, has_one: true, belongs_to: false)
92
56
  ```
93
57
 
94
- If the parent and child are updated in one go, PaperTrail-AssociationTracking can use the
95
- aforementioned `transaction_id` to reify the models as they were before the
96
- transaction (instead of before the update to the model).
58
+ If the parent and child are updated in one go, it will utilize the aforementioned `transaction_id` to reify the models as they were before the transaction (instead of before the update to the model).
97
59
 
98
60
  ```ruby
99
- treasure.amount # 100
100
- treasure.location.latitude # 12.345
61
+ item.amount # 100
62
+ item.location.latitude # 12.345
101
63
 
102
- Treasure.transaction do
103
- treasure.location.update_attributes latitude: 54.321
104
- treasure.update_attributes amount: 153
64
+ Item.transaction do
65
+ item.location.update(latitude: 54.321)
66
+ item.update(amount: 153)
105
67
  end
106
68
 
107
- t = treasure.versions.last.reify(has_one: true)
69
+ t = item.versions.last.reify(has_one: true)
108
70
  t.amount # 100
109
71
  t.location.latitude # 12.345, instead of 54.321
110
72
  ```
111
73
 
112
- By default, PaperTrail-AssociationTracking excludes an associated record from the reified parent
113
- model if the associated record exists in the live model but did not exist as at
114
- the time the version was created. This is usually what you want if you just want
115
- to look at the reified version. But if you want to persist it, it would be
116
- better to pass in option `mark_for_destruction: true` so that the associated
117
- record is included and marked for destruction. Note that `mark_for_destruction`
118
- only has [an effect on associations marked with `autosave: true`](http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction).
74
+ By default, it excludes an associated record from the reified parent model if the associated record exists in the live model but did not exist as at the time the version was created. This is usually what you want if you just want to look at the reified version. But if you want to persist it, it would be better to pass in option `mark_for_destruction: true` so that the associated record is included and marked for destruction. Note that `mark_for_destruction` only has [an effect on associations marked with `autosave: true`](http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction).
119
75
 
120
76
  ```ruby
121
- class Widget < ActiveRecord::Base
77
+ class Product < ActiveRecord::Base
122
78
  has_paper_trail
123
- has_one :wotsit, autosave: true
79
+ has_one :photo, autosave: true
124
80
  end
125
81
 
126
- class Wotsit < ActiveRecord::Base
82
+ class Photo < ActiveRecord::Base
127
83
  has_paper_trail
128
- belongs_to :widget
84
+ belongs_to :product
129
85
  end
130
86
 
131
- widget = Widget.create(name: 'widget_0')
132
- widget.update_attributes(name: 'widget_1')
133
- widget.create_wotsit(name: 'wotsit')
87
+ product = Product.create(name: 'product_0')
88
+ product.update(name: 'product_1')
89
+ product.create_photo(name: 'photo')
134
90
 
135
- widget_0 = widget.versions.last.reify(has_one: true)
136
- widget_0.wotsit # nil
91
+ product_0 = product.versions.last.reify(has_one: true)
92
+ product_0.photo # nil
137
93
 
138
- widget_0 = widget.versions.last.reify(has_one: true, mark_for_destruction: true)
139
- widget_0.wotsit.marked_for_destruction? # true
140
- widget_0.save!
141
- widget.reload.wotsit # nil
94
+ product_0 = product.versions.last.reify(has_one: true, mark_for_destruction: true)
95
+ product_0.photo.marked_for_destruction? # true
96
+ product_0.save!
97
+ product.reload.photo # nil
142
98
  ```
143
99
 
144
- # Known Issues
145
-
146
- Associations have the following known issues, in order of descending importance. Use in Production at your own risk.
147
-
148
- **PR's for these issues will happily be accepted**
149
100
 
150
- If you notice anything here that should be updated/removed/edited feel free to create an issue.
101
+ # Limitations
151
102
 
152
- 1. PaperTrail-AssociationTracking only reifies the first level of associations.
153
- 1. Sometimes the has_one association will find more than one possible candidate and will raise a `PaperTrailAssociationTracking::Reifiers::HasOne::FoundMoreThanOne` error. For example, see `spec/models/person_spec.rb`
154
- - If you are not using STI, you may want to just assume the first result (of multiple) is the correct one and continue. PaperTrail <= v8 did this without error or warning. To do so add the following line to your initializer: `PaperTrail.config.association_reify_error_behaviour = :warn`. Valid options are: `[:error, :warn, :ignore]`
155
- - When using STI, even if you enable `:warn` you will likely still end up recieving an `ActiveRecord::AssociationTypeMismatch` error.
156
- 1. Not compatible with [transactional tests](https://github.com/rails/rails/blob/591a0bb87fff7583e01156696fbbf929d48d3e54/activerecord/lib/active_record/fixtures.rb#L142), aka. transactional fixtures. - [PT Issue #542](https://github.com/airblade/paper_trail/issues/542)
157
- 1. Requires database timestamp columns with fractional second precision.
158
- - Sqlite and postgres timestamps have fractional second precision by default.
159
- [MySQL timestamps do not](https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html). Furthermore, MySQL 5.5 and earlier do not
160
- support fractional second precision at all.
161
- - Also, support for fractional seconds in MySQL was not added to
162
- rails until ActiveRecord 4.2 (https://github.com/rails/rails/pull/14359).
163
- 1. PaperTrail-AssociationTracking can't restore an association properly if the association record
164
- can be updated to replace its parent model (by replacing the foreign key)
165
- 1. Currently PaperTrail-AssociationTracking only supports a single `version_associations` table.
166
- Therefore, you can only use a single table to store the versions for
167
- all related models. Sorry for those who use multiple version tables.
168
- 1. PaperTrail-AssociationTracking relies on the callbacks on the association model (and the :through
169
- association model for Has-Many-Through associations) to record the versions
170
- and the relationship between the versions. If the association is changed
171
- without invoking the callbacks, Reification won't work. Below are some
172
- examples:
103
+ 1. Only reifies the first level of associations. If you want to include nested associations simply add :through relationships to your model.
104
+ 1. Currently we only supports a single `version_associations` table. Therefore, you can only use a single table to store the versions for all related models.
105
+ 1. 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:
173
106
 
174
107
  Given these models:
175
108
 
@@ -197,7 +130,7 @@ If you notice anything here that should be updated/removed/edited feel free to c
197
130
 
198
131
  ```ruby
199
132
  @book.authors << @dostoyevsky
200
- @book.authors.create name: 'Tolstoy'
133
+ @book.authors.create(name: 'Tolstoy')
201
134
  @book.authorships.last.destroy
202
135
  @book.authorships.clear
203
136
  @book.author_ids = [@solzhenistyn.id, @dostoyevsky.id]
@@ -211,42 +144,27 @@ If you notice anything here that should be updated/removed/edited feel free to c
211
144
  @book.authors = []
212
145
  ```
213
146
 
214
- Having said that, you can apparently get all these working (I haven't tested it
215
- myself) with this patch:
216
-
217
- ```ruby
218
- # config/initializers/active_record_patch.rb
219
-
220
- class HasManyThroughAssociationPatch
221
- def delete_records(records, method)
222
- method ||= :destroy
223
- super
224
- end
225
- end
226
-
227
- ActiveRecord::Associations::HasManyThroughAssociation.prepend(HasManyThroughAssociationPatch)
228
- ```
229
147
 
230
- See [PT Issue #113](https://github.com/paper-trail-gem/paper_trail/issues/113) for a discussion about this.
148
+ # Known Issues
231
149
 
150
+ 1. Sometimes the has_one association will find more than one possible candidate and will raise a `PaperTrailAssociationTracking::Reifiers::HasOne::FoundMoreThanOne` error. For example, see `spec/models/person_spec.rb`
151
+ - If you are not using STI, you may want to just assume the first result of multiple is the correct one and continue. PaperTrail <= v8 did this without error or warning. To do so add the following line to your initializer: `PaperTrail.config.association_reify_error_behaviour = :warn`. Valid options are: `[:error, :warn, :ignore]`
152
+ - When using STI, even if you enable `:warn` you will likely still end up recieving an `ActiveRecord::AssociationTypeMismatch` error. See [PT Issue #594](https://github.com/airblade/paper_trail/issues/594). I recommend that you never use STI in any Rails application due to the problems they cause.
153
+ 1. Not compatible with transactional tests, see [PT Issue #542](https://github.com/airblade/paper_trail/issues/542). However, apparently there has been some success by using the [transactional_capybara](https://rubygems.org/gems/transactional_capybara) gem.
232
154
 
233
- ### Regarding ActiveRecord Single Table Inheritance (STI)
234
155
 
235
- At this time during `reify` any STI `has_one` associations will raise a `PaperTrailAssociationTracking::Reifiers::HasOne::FoundMoreThanOne` error. See [PT Issue #594](https://github.com/airblade/paper_trail/issues/594)
156
+ # Contributing
236
157
 
237
- Something to note though, is while the PaperTrail gem supports [Single Table Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance), I do NOT recommend STI ever. Your better off rolling your own solution rather than using STI.
158
+ We use the `appraisal` gem for testing multiple versions of `paper_trail` and `activerecord`. Please use the following steps to test using `appraisal`.
238
159
 
239
- # Contributing
160
+ 1. `bundle exec appraisal install`
161
+ 2. `bundle exec appraisal rake test`
240
162
 
241
- See the paper_trail [contribution guidelines](https://github.com/paper-trail-gem/paper_trail/blob/master/.github/CONTRIBUTING.md)
242
163
 
243
164
  # Credits
244
165
 
245
- Plugin authored by [Weston Ganger](https://github.com/westonganger) & Jared Beck
166
+ Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
246
167
 
247
- Maintained by [Weston Ganger](https://github.com/westonganger) & [Jared Beck](https://github.com/jaredbeck)
168
+ Plugin authored by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
248
169
 
249
170
  Associations code originally contributed by Ben Atkins, Jared Beck, Andy Stewart & more
250
-
251
- [1]: https://api.travis-ci.org/westonganger/paper_trail-association_tracking.svg?branch=master
252
- [2]: https://travis-ci.org/westonganger/paper_trail-association_tracking
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require "bundler"
4
2
  Bundler::GemHelper.install_tasks
5
3
 
@@ -20,9 +18,8 @@ Rake::TestTask.new(:test) do |t|
20
18
  t.pattern = "test/**/*_test.rb"
21
19
  t.verbose = false
22
20
 
23
- # Enabling ruby interpreter warnings (-w) is, sadly, impractical. There are
24
- # too many noisy warnings that we have no control over, e.g. caused by libs we
25
- # depend on.
21
+ ### Enabling ruby interpreter warnings (-w) is, sadly, impractical.
22
+ ### There are too many noisy warnings that we have no control over, e.g. caused by libs we depend on.
26
23
  t.warning = false
27
24
  end
28
25
 
@@ -33,29 +30,5 @@ RSpec::Core::RakeTask.new(:spec) do |t|
33
30
  t.verbose = false # hide list of specs bit.ly/1nVq3Jn
34
31
  end
35
32
 
36
- task :autocorrect do
37
- rules = [
38
- 'FrozenStringLiteralComment',
39
- 'Layout/EmptyLineAfterMagicComment',
40
- 'Layout/EmptyLinesAroundBlockBody',
41
- 'Layout/EmptyLinesAroundClassBody',
42
- 'Layout/EmptyLinesAroundMethodBody',
43
- 'Layout/EmptyLinesAroundModuleBody',
44
- 'Layout/TrailingWhitespace',
45
- 'Style/EmptyMethod',
46
- 'Style/TrailingCommaInArguments',
47
- ]
48
-
49
- rules.each do |rule|
50
- `bundle exec rubocop --auto-correct --only #{rule}`
51
- end
52
-
53
- Rake::Task['rubocop'].invoke
54
- end
55
-
56
- require "rubocop/rake_task"
57
- RuboCop::RakeTask.new
58
-
59
- ### TODO: Allow rubocop to fail, but still continue
60
33
  desc "Default: run all available test suites"
61
34
  task default: %i[prepare test spec]
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/active_record"
5
+
6
+ module PaperTrailAssociationTracking
7
+ # Installs PaperTrail in a rails app.
8
+ class AddForeignTypeToVersionAssociationsGenerator < ::Rails::Generators::Base
9
+ include ::Rails::Generators::Migration
10
+
11
+ # Class names of MySQL adapters.
12
+ # - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
13
+ # - `Mysql2Adapter` - Used by `mysql2` gem.
14
+ MYSQL_ADAPTERS = [
15
+ "ActiveRecord::ConnectionAdapters::MysqlAdapter",
16
+ "ActiveRecord::ConnectionAdapters::Mysql2Adapter"
17
+ ].freeze
18
+
19
+ source_root File.expand_path("../templates", __FILE__)
20
+
21
+ desc "Generates (but does not run) a migration to add a the foreign_type column to the version_associations table after upgrading from v1.0 or earlier."
22
+
23
+ def create_migrations
24
+ add_paper_trail_migration("add_foreign_type_to_version_associations")
25
+ end
26
+
27
+ def self.next_migration_number(dirname)
28
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
29
+ end
30
+
31
+ protected
32
+
33
+ def add_paper_trail_migration(template)
34
+ migration_dir = File.expand_path("db/migrate")
35
+ if self.class.migration_exists?(migration_dir, template)
36
+ ::Kernel.warn "Migration already exists: #{template}"
37
+ else
38
+ migration_template(
39
+ "#{template}.rb.erb",
40
+ "db/migrate/#{template}.rb",
41
+ migration_version: migration_version
42
+ )
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def migration_version
49
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
50
+ end
51
+ end
52
+ end
@@ -55,10 +55,7 @@ module PaperTrailAssociationTracking
55
55
  private
56
56
 
57
57
  def migration_version
58
- major = ActiveRecord::VERSION::MAJOR
59
- if major >= 5
60
- "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
61
- end
58
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
62
59
  end
63
60
  end
64
61
  end
@@ -0,0 +1,21 @@
1
+ # This migration and AddTransactionIdColumnToVersions provide the necessary
2
+ # schema for tracking associations.
3
+ class AddForeignTypeToVersionAssociations < ActiveRecord::Migration<%= migration_version %>
4
+ def self.up
5
+ add_column :version_associations, :foreign_type, :string, index: true
6
+ remove_index :version_associations,
7
+ name: "index_version_associations_on_foreign_key"
8
+ add_index :version_associations,
9
+ %i(foreign_key_name foreign_key_id foreign_type),
10
+ name: "index_version_associations_on_foreign_key"
11
+ end
12
+
13
+ def self.down
14
+ remove_index :version_associations,
15
+ name: "index_version_associations_on_foreign_key"
16
+ remove_column :version_associations, :foreign_type
17
+ add_index :version_associations,
18
+ %i(foreign_key_name foreign_key_id),
19
+ name: "index_version_associations_on_foreign_key"
20
+ end
21
+ end
@@ -6,10 +6,11 @@ class CreateVersionAssociations < ActiveRecord::Migration<%= migration_version %
6
6
  t.integer :version_id
7
7
  t.string :foreign_key_name, null: false
8
8
  t.integer :foreign_key_id
9
+ t.string :foreign_type
9
10
  end
10
11
  add_index :version_associations, [:version_id]
11
12
  add_index :version_associations,
12
- %i(foreign_key_name foreign_key_id),
13
+ %i(foreign_key_name foreign_key_id foreign_type),
13
14
  name: "index_version_associations_on_foreign_key"
14
15
  end
15
16
 
@@ -11,11 +11,11 @@ require "paper_trail_association_tracking/version_concern"
11
11
 
12
12
  module PaperTrailAssociationTracking
13
13
  def self.version
14
- VERSION::STRING
14
+ VERSION
15
15
  end
16
16
 
17
17
  def self.gem_version
18
- ::Gem::Version.new(VERSION::STRING)
18
+ ::Gem::Version.new(VERSION)
19
19
  end
20
20
  end
21
21
 
@@ -53,11 +53,9 @@ module PaperTrail
53
53
  end
54
54
  end
55
55
 
56
-
57
56
  # Require frameworks
58
57
  if defined?(::Rails)
59
- # Rails module is sometimes defined by gems like rails-html-sanitizer
60
- # so we check for presence of Rails.application.
58
+ # Rails module is sometimes defined by gems like rails-html-sanitizer so we check for presence of Rails.application.
61
59
  if defined?(::Rails.application)
62
60
  require "paper_trail_association_tracking/frameworks/rails"
63
61
  else
@@ -14,10 +14,10 @@ module PaperTrailAssociationTracking
14
14
  def association_reify_error_behaviour
15
15
  @association_reify_error_behaviour ||= "error"
16
16
  end
17
-
17
+
18
18
  def track_associations=(val)
19
19
  @track_associations = !!val
20
- end
20
+ end
21
21
 
22
22
  def track_associations?
23
23
  !!@track_associations
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "paper_trail_association_tracking/version_association_concern"
4
4
 
5
- module ::PaperTrail
5
+ module PaperTrail
6
6
  # This is the default ActiveRecord model provided by PaperTrail. Most simple
7
7
  # applications will only use this and its partner, `Version`, but it is
8
8
  # possible to sub-class, extend, or even do without this model entirely.
@@ -6,33 +6,6 @@ module PaperTrailAssociationTracking
6
6
  def transaction?
7
7
  ::ActiveRecord::Base.connection.open_transactions.positive?
8
8
  end
9
-
10
- # @deprecated
11
- def clear_transaction_id
12
- ::ActiveSupport::Deprecation.warn(
13
- "PaperTrail.clear_transaction_id is deprecated, use PaperTrail.request.clear_transaction_id",
14
- caller(1)
15
- )
16
- request.clear_transaction_id
17
- end
18
-
19
- # @deprecated
20
- def transaction_id
21
- ::ActiveSupport::Deprecation.warn(
22
- "PaperTrail.transaction_id is deprecated without replacement.",
23
- caller(1)
24
- )
25
- request.transaction_id
26
- end
27
-
28
- # @deprecated
29
- def transaction_id=(id)
30
- ::ActiveSupport::Deprecation.warn(
31
- "PaperTrail.transaction_id= is deprecated without replacement.",
32
- caller(1)
33
- )
34
- request.transaction_id = id
35
- end
36
9
  end
37
10
  end
38
11
  end
@@ -136,7 +136,8 @@ module PaperTrailAssociationTracking
136
136
  ::PaperTrail::VersionAssociation.create(
137
137
  version_id: version.transaction_id,
138
138
  foreign_key_name: a.name,
139
- foreign_key_id: id
139
+ foreign_key_id: id,
140
+ foreign_type: a.klass
140
141
  )
141
142
  end
142
143
  end
@@ -168,12 +169,14 @@ module PaperTrailAssociationTracking
168
169
  }
169
170
 
170
171
  if assoc.options[:polymorphic]
171
- associated_record = @record.send(assoc.name) if @record.send(assoc.foreign_type)
172
- if associated_record && ::PaperTrail.request.enabled_for_model?(associated_record.class)
173
- assoc_version_args[:foreign_key_id] = associated_record.id
172
+ foreign_type = @record.send(assoc.foreign_type)
173
+ if foreign_type && ::PaperTrail.request.enabled_for_model?(foreign_type.constantize)
174
+ assoc_version_args[:foreign_key_id] = @record.send(assoc.foreign_key)
175
+ assoc_version_args[:foreign_type] = foreign_type
174
176
  end
175
177
  elsif ::PaperTrail.request.enabled_for_model?(assoc.klass)
176
178
  assoc_version_args[:foreign_key_id] = @record.send(assoc.foreign_key)
179
+ assoc_version_args[:foreign_type] = assoc.klass
177
180
  end
178
181
 
179
182
  if assoc_version_args.key?(:foreign_key_id)
@@ -50,9 +50,11 @@ module PaperTrailAssociationTracking
50
50
  end
51
51
 
52
52
  # @api private
53
- def each_enabled_association(associations)
53
+ def each_enabled_association(associations, model)
54
54
  associations.each do |assoc|
55
- next unless ::PaperTrail.request.enabled_for_model?(assoc.klass)
55
+ assoc_klass = assoc.polymorphic? ?
56
+ model.send(assoc.foreign_type).constantize : assoc.klass
57
+ next unless ::PaperTrail.request.enabled_for_model?(assoc_klass)
56
58
  yield assoc
57
59
  end
58
60
  end
@@ -79,7 +81,7 @@ module PaperTrailAssociationTracking
79
81
  # @api private
80
82
  def reify_has_one_associations(transaction_id, model, options = {})
81
83
  associations = model.class.reflect_on_all_associations(:has_one)
82
- each_enabled_association(associations) do |assoc|
84
+ each_enabled_association(associations, model) do |assoc|
83
85
  ::PaperTrailAssociationTracking::Reifiers::HasOne.reify(assoc, model, options, transaction_id)
84
86
  end
85
87
  end
@@ -88,7 +90,7 @@ module PaperTrailAssociationTracking
88
90
  # @api private
89
91
  def reify_belongs_to_associations(transaction_id, model, options = {})
90
92
  associations = model.class.reflect_on_all_associations(:belongs_to)
91
- each_enabled_association(associations) do |assoc|
93
+ each_enabled_association(associations, model) do |assoc|
92
94
  ::PaperTrailAssociationTracking::Reifiers::BelongsTo.reify(assoc, model, options, transaction_id)
93
95
  end
94
96
  end
@@ -97,7 +99,7 @@ module PaperTrailAssociationTracking
97
99
  # @api private
98
100
  def reify_has_many_associations(transaction_id, associations, model, options = {})
99
101
  version_table_name = model.class.paper_trail.version_class.table_name
100
- each_enabled_association(associations) do |assoc|
102
+ each_enabled_association(associations, model) do |assoc|
101
103
  ::PaperTrailAssociationTracking::Reifiers::HasMany.reify(assoc, model, options, transaction_id, version_table_name)
102
104
  end
103
105
  end
@@ -106,7 +108,7 @@ module PaperTrailAssociationTracking
106
108
  # direct (non-`through`) has_manys have been reified.
107
109
  # @api private
108
110
  def reify_has_many_through_associations(transaction_id, associations, model, options = {})
109
- each_enabled_association(associations) do |assoc|
111
+ each_enabled_association(associations, model) do |assoc|
110
112
  ::PaperTrailAssociationTracking::Reifiers::HasManyThrough.reify(assoc, model, options, transaction_id)
111
113
  end
112
114
  end
@@ -9,8 +9,10 @@ module PaperTrailAssociationTracking
9
9
  # @api private
10
10
  def reify(assoc, model, options, transaction_id)
11
11
  id = model.send(assoc.foreign_key)
12
- version = load_version(assoc, id, transaction_id, options[:version_at])
13
- record = load_record(assoc, id, options, version)
12
+ klass = assoc.polymorphic? ?
13
+ model.send(assoc.foreign_type).constantize : assoc.klass
14
+ version = load_version(klass, id, transaction_id, options[:version_at])
15
+ record = load_record(klass, id, options, version)
14
16
  model.send("#{assoc.name}=".to_sym, record)
15
17
  end
16
18
 
@@ -19,9 +21,9 @@ module PaperTrailAssociationTracking
19
21
  # Given a `belongs_to` association and a `version`, return a record that
20
22
  # can be assigned in order to reify that association.
21
23
  # @api private
22
- def load_record(assoc, id, options, version)
24
+ def load_record(assoc_klass, id, options, version)
23
25
  if version.nil?
24
- assoc.klass.where(assoc.klass.primary_key => id).first
26
+ assoc_klass.where(assoc_klass.primary_key => id).first
25
27
  else
26
28
  version.reify(
27
29
  options.merge(
@@ -37,9 +39,9 @@ module PaperTrailAssociationTracking
37
39
  # Given a `belongs_to` association and an `id`, return a version record
38
40
  # from the point in time identified by `transaction_id` or `version_at`.
39
41
  # @api private
40
- def load_version(assoc, id, transaction_id, version_at)
41
- assoc.klass.paper_trail.version_class.
42
- where("item_type = ?", assoc.klass.base_class.name).
42
+ def load_version(assoc_klass, id, transaction_id, version_at)
43
+ assoc_klass.paper_trail.version_class.
44
+ where("item_type = ?", assoc_klass.base_class.name).
43
45
  where("item_id = ?", id).
44
46
  where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
45
47
  order("id").limit(1).first
@@ -78,14 +78,13 @@ module PaperTrailAssociationTracking
78
78
  #
79
79
  # @api private
80
80
  # @param klass - An ActiveRecord class.
81
- # @param version_id_subquery - String. A SQL subquery that selects
82
- # the IDs of version records.
81
+ # @param version_ids - Array. The IDs of version records.
83
82
  # @return A `Hash` mapping IDs to `Version`s
84
83
  #
85
- def versions_by_id(klass, version_id_subquery)
84
+ def versions_by_id(klass, version_ids)
86
85
  klass.
87
86
  paper_trail.version_class.
88
- where("id IN (#{version_id_subquery})").
87
+ where(id: version_ids).
89
88
  inject({}) { |a, e| a.merge!(e.item_id => e) }
90
89
  end
91
90
 
@@ -95,16 +94,17 @@ module PaperTrailAssociationTracking
95
94
  # from the point in time identified by `tx_id` or `version_at`.
96
95
  # @api private
97
96
  def load_versions_for_hm_association(assoc, model, version_table, tx_id, version_at)
98
- version_id_subquery = ::PaperTrail::VersionAssociation.
97
+ version_ids = ::PaperTrail::VersionAssociation.
99
98
  joins(model.class.version_association_name).
100
- select("MIN(version_id)").
99
+ select("MIN(version_id) as version_id").
101
100
  where("foreign_key_name = ?", assoc.foreign_key).
102
101
  where("foreign_key_id = ?", model.id).
102
+ where(foreign_type: [model.class.name, nil]).
103
103
  where("#{version_table}.item_type = ?", assoc.klass.base_class.name).
104
104
  where("created_at >= ? OR transaction_id = ?", version_at, tx_id).
105
105
  group("item_id").
106
- to_sql
107
- versions_by_id(model.class, version_id_subquery)
106
+ map{|e| e.version_id}
107
+ versions_by_id(model.class, version_ids)
108
108
  end
109
109
  end
110
110
  end
@@ -73,8 +73,8 @@ module PaperTrailAssociationTracking
73
73
  # `version_at`.
74
74
  # @api private
75
75
  def load_versions_for_hmt_association(assoc, ids, tx_id, version_at)
76
- version_id_subquery = assoc.klass.paper_trail.version_class.
77
- select("MIN(id)").
76
+ version_ids = assoc.klass.paper_trail.version_class.
77
+ select("MIN(id) as id").
78
78
  where("item_type = ?", assoc.klass.base_class.name).
79
79
  where("item_id IN (?)", ids).
80
80
  where(
@@ -83,8 +83,8 @@ module PaperTrailAssociationTracking
83
83
  tx_id
84
84
  ).
85
85
  group("item_id").
86
- to_sql
87
- ::PaperTrailAssociationTracking::Reifiers::HasMany.versions_by_id(assoc.klass, version_id_subquery)
86
+ map{|e| e.id}
87
+ ::PaperTrailAssociationTracking::Reifiers::HasMany.versions_by_id(assoc.klass, version_ids)
88
88
  end
89
89
  end
90
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PaperTrailAssociationTracking
4
- VERSION = "0.0.1"
4
+ VERSION = "2.1.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paper_trail-association_tracking
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Weston Ganger
@@ -10,196 +10,162 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-06-03 00:00:00.000000000 Z
13
+ date: 2020-08-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: appraisal
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '2.2'
21
+ version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - "~>"
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: '2.2'
28
+ version: '0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: byebug
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - "~>"
33
+ - - ">="
34
34
  - !ruby/object:Gem::Version
35
- version: '9.1'
35
+ version: '0'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - "~>"
40
+ - - ">="
41
41
  - !ruby/object:Gem::Version
42
- version: '9.1'
42
+ version: '0'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: ffaker
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - "~>"
47
+ - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: '2.7'
49
+ version: '0'
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
- - - "~>"
54
+ - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: '2.7'
56
+ version: '0'
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: generator_spec
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - "~>"
61
+ - - ">="
62
62
  - !ruby/object:Gem::Version
63
- version: 0.9.4
63
+ version: '0'
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
- - - "~>"
68
+ - - ">="
69
69
  - !ruby/object:Gem::Version
70
- version: 0.9.4
70
+ version: '0'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: mysql2
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
- - - "~>"
75
+ - - ">="
76
76
  - !ruby/object:Gem::Version
77
- version: 0.4.10
77
+ version: '0'
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
- - - "~>"
82
+ - - ">="
83
83
  - !ruby/object:Gem::Version
84
- version: 0.4.10
84
+ version: '0'
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: pg
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
- - - "~>"
89
+ - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: 0.21.0
91
+ version: '0'
92
92
  type: :development
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - "~>"
96
+ - - ">="
97
97
  - !ruby/object:Gem::Version
98
- version: 0.21.0
98
+ version: '0'
99
99
  - !ruby/object:Gem::Dependency
100
100
  name: rack-test
101
101
  requirement: !ruby/object:Gem::Requirement
102
102
  requirements:
103
103
  - - ">="
104
104
  - !ruby/object:Gem::Version
105
- version: 0.6.3
106
- - - "<"
107
- - !ruby/object:Gem::Version
108
- version: '0.9'
105
+ version: '0'
109
106
  type: :development
110
107
  prerelease: false
111
108
  version_requirements: !ruby/object:Gem::Requirement
112
109
  requirements:
113
110
  - - ">="
114
111
  - !ruby/object:Gem::Version
115
- version: 0.6.3
116
- - - "<"
117
- - !ruby/object:Gem::Version
118
- version: '0.9'
112
+ version: '0'
119
113
  - !ruby/object:Gem::Dependency
120
114
  name: rake
121
115
  requirement: !ruby/object:Gem::Requirement
122
116
  requirements:
123
- - - "~>"
117
+ - - ">="
124
118
  - !ruby/object:Gem::Version
125
- version: '12.3'
119
+ version: '0'
126
120
  type: :development
127
121
  prerelease: false
128
122
  version_requirements: !ruby/object:Gem::Requirement
129
123
  requirements:
130
- - - "~>"
124
+ - - ">="
131
125
  - !ruby/object:Gem::Version
132
- version: '12.3'
126
+ version: '0'
133
127
  - !ruby/object:Gem::Dependency
134
128
  name: rspec-rails
135
129
  requirement: !ruby/object:Gem::Requirement
136
130
  requirements:
137
- - - "~>"
138
- - !ruby/object:Gem::Version
139
- version: 3.7.2
140
- type: :development
141
- prerelease: false
142
- version_requirements: !ruby/object:Gem::Requirement
143
- requirements:
144
- - - "~>"
145
- - !ruby/object:Gem::Version
146
- version: 3.7.2
147
- - !ruby/object:Gem::Dependency
148
- name: rubocop
149
- requirement: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - "~>"
152
- - !ruby/object:Gem::Version
153
- version: 0.51.0
154
- type: :development
155
- prerelease: false
156
- version_requirements: !ruby/object:Gem::Requirement
157
- requirements:
158
- - - "~>"
159
- - !ruby/object:Gem::Version
160
- version: 0.51.0
161
- - !ruby/object:Gem::Dependency
162
- name: rubocop-rspec
163
- requirement: !ruby/object:Gem::Requirement
164
- requirements:
165
- - - "~>"
131
+ - - ">="
166
132
  - !ruby/object:Gem::Version
167
- version: 1.19.0
133
+ version: '0'
168
134
  type: :development
169
135
  prerelease: false
170
136
  version_requirements: !ruby/object:Gem::Requirement
171
137
  requirements:
172
- - - "~>"
138
+ - - ">="
173
139
  - !ruby/object:Gem::Version
174
- version: 1.19.0
140
+ version: '0'
175
141
  - !ruby/object:Gem::Dependency
176
142
  name: sqlite3
177
143
  requirement: !ruby/object:Gem::Requirement
178
144
  requirements:
179
- - - "~>"
145
+ - - ">="
180
146
  - !ruby/object:Gem::Version
181
- version: '1.3'
147
+ version: '0'
182
148
  type: :development
183
149
  prerelease: false
184
150
  version_requirements: !ruby/object:Gem::Requirement
185
151
  requirements:
186
- - - "~>"
152
+ - - ">="
187
153
  - !ruby/object:Gem::Version
188
- version: '1.3'
154
+ version: '0'
189
155
  - !ruby/object:Gem::Dependency
190
156
  name: timecop
191
157
  requirement: !ruby/object:Gem::Requirement
192
158
  requirements:
193
- - - "~>"
159
+ - - ">="
194
160
  - !ruby/object:Gem::Version
195
- version: 0.9.1
161
+ version: '0'
196
162
  type: :development
197
163
  prerelease: false
198
164
  version_requirements: !ruby/object:Gem::Requirement
199
165
  requirements:
200
- - - "~>"
166
+ - - ">="
201
167
  - !ruby/object:Gem::Version
202
- version: 0.9.1
168
+ version: '0'
203
169
  description: Plugin for the PaperTrail gem to track and reify associations
204
170
  email: weston@westonganger.com
205
171
  executables: []
@@ -210,7 +176,9 @@ files:
210
176
  - LICENSE
211
177
  - README.md
212
178
  - Rakefile
179
+ - lib/generators/paper_trail_association_tracking/add_foreign_type_to_version_associations_generator.rb
213
180
  - lib/generators/paper_trail_association_tracking/install_generator.rb
181
+ - lib/generators/paper_trail_association_tracking/templates/add_foreign_type_to_version_associations.rb.erb
214
182
  - lib/generators/paper_trail_association_tracking/templates/add_transaction_id_column_to_versions.rb.erb
215
183
  - lib/generators/paper_trail_association_tracking/templates/create_version_associations.rb.erb
216
184
  - lib/paper_trail-association_tracking.rb
@@ -250,10 +218,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
218
  requirements:
251
219
  - - ">="
252
220
  - !ruby/object:Gem::Version
253
- version: 1.3.6
221
+ version: '0'
254
222
  requirements: []
255
- rubyforge_project:
256
- rubygems_version: 2.7.6
223
+ rubygems_version: 3.1.2
257
224
  signing_key:
258
225
  specification_version: 4
259
226
  summary: Plugin for the PaperTrail gem to track and reify associations