paper_trail 10.1.0 → 11.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/generators/paper_trail/install/install_generator.rb +7 -3
- data/lib/generators/paper_trail/install/templates/create_versions.rb.erb +1 -1
- data/lib/generators/paper_trail/migration_generator.rb +5 -4
- data/lib/paper_trail.rb +9 -0
- data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +10 -42
- data/lib/paper_trail/compatibility.rb +51 -0
- data/lib/paper_trail/config.rb +0 -33
- data/lib/paper_trail/events/base.rb +80 -55
- data/lib/paper_trail/events/create.rb +1 -0
- data/lib/paper_trail/events/destroy.rb +11 -3
- data/lib/paper_trail/events/update.rb +1 -0
- data/lib/paper_trail/frameworks/rails/controller.rb +1 -3
- data/lib/paper_trail/frameworks/rails/engine.rb +32 -1
- data/lib/paper_trail/model_config.rb +17 -6
- data/lib/paper_trail/record_trail.rb +49 -17
- data/lib/paper_trail/reifier.rb +18 -19
- data/lib/paper_trail/serializers/yaml.rb +5 -0
- data/lib/paper_trail/version_concern.rb +33 -20
- data/lib/paper_trail/version_number.rb +2 -2
- metadata +56 -43
- data/lib/generators/paper_trail/install_generator.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8f7237a39b0645932b394ee0332846be680f6a725ed02c7a126aa892d840ccc
|
4
|
+
data.tar.gz: b0356d622a6e6f3ba0938c4d9fe0ff211b347a0c65e2d3a6574bc8bf5fb04df6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46ad3d73231d4ab6c3d978925e7b838af2697b2b998b9a7948d078266fbdbec33b6dbdcbfb93751f2aa961be6a3499f499a71d56e3fbc6138174cb752feae41a
|
7
|
+
data.tar.gz: 9ec2fc044229898aa2c5ab70dc41b549b6896c61af42633af0760e6b99aadd375f5f7e39160ad713131ecb439b0f5aff8cb6696f276f4c5225eb40e371f43035
|
@@ -25,10 +25,14 @@ module PaperTrail
|
|
25
25
|
" See section 5.c. Generators in README.md for more information."
|
26
26
|
|
27
27
|
def create_migration_file
|
28
|
-
add_paper_trail_migration(
|
28
|
+
add_paper_trail_migration(
|
29
|
+
"create_versions",
|
29
30
|
item_type_options: item_type_options,
|
30
|
-
versions_table_options: versions_table_options
|
31
|
-
|
31
|
+
versions_table_options: versions_table_options
|
32
|
+
)
|
33
|
+
if options.with_changes?
|
34
|
+
add_paper_trail_migration("add_object_changes_to_versions")
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
private
|
@@ -11,7 +11,7 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
|
|
11
11
|
def change
|
12
12
|
create_table :versions<%= versions_table_options %> do |t|
|
13
13
|
t.string :item_type<%= item_type_options %>
|
14
|
-
t.
|
14
|
+
t.bigint :item_id, null: false
|
15
15
|
t.string :event, null: false
|
16
16
|
t.string :whodunnit
|
17
17
|
t.text :object, limit: TEXT_BYTES
|
@@ -28,10 +28,11 @@ module PaperTrail
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def migration_version
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
format(
|
32
|
+
"[%d.%d]",
|
33
|
+
ActiveRecord::VERSION::MAJOR,
|
34
|
+
ActiveRecord::VERSION::MINOR
|
35
|
+
)
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
data/lib/paper_trail.rb
CHANGED
@@ -16,6 +16,7 @@ require "active_record"
|
|
16
16
|
|
17
17
|
require "request_store"
|
18
18
|
require "paper_trail/cleaner"
|
19
|
+
require "paper_trail/compatibility"
|
19
20
|
require "paper_trail/config"
|
20
21
|
require "paper_trail/has_paper_trail"
|
21
22
|
require "paper_trail/record_history"
|
@@ -125,6 +126,10 @@ module PaperTrail
|
|
125
126
|
end
|
126
127
|
end
|
127
128
|
|
129
|
+
# We use the `on_load` "hook" instead of `ActiveRecord::Base.include` because we
|
130
|
+
# don't want to cause all of AR to be autloaded yet. See
|
131
|
+
# https://guides.rubyonrails.org/engines.html#what-are-on-load-hooks-questionmark
|
132
|
+
# to learn more about `on_load`.
|
128
133
|
ActiveSupport.on_load(:active_record) do
|
129
134
|
include PaperTrail::Model
|
130
135
|
end
|
@@ -141,3 +146,7 @@ if defined?(::Rails)
|
|
141
146
|
else
|
142
147
|
require "paper_trail/frameworks/active_record"
|
143
148
|
end
|
149
|
+
|
150
|
+
if defined?(::ActiveRecord)
|
151
|
+
::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version)
|
152
|
+
end
|
@@ -32,50 +32,18 @@ module PaperTrail
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
AttributeSerializerFactory.for(@klass, attr).serialize(val)
|
40
|
-
end
|
41
|
-
|
42
|
-
def deserialize(attr, val)
|
43
|
-
if defined_enums[attr] && val.is_a?(::String)
|
44
|
-
# Because PT 4 used to save the string version of enums to `object_changes`
|
45
|
-
val
|
46
|
-
else
|
47
|
-
AttributeSerializerFactory.for(@klass, attr).deserialize(val)
|
48
|
-
end
|
49
|
-
end
|
35
|
+
# Uses AR 5's `serialize` and `deserialize`.
|
36
|
+
class CastAttributeSerializer
|
37
|
+
def serialize(attr, val)
|
38
|
+
AttributeSerializerFactory.for(@klass, attr).serialize(val)
|
50
39
|
end
|
51
|
-
else
|
52
|
-
# This implementation uses AR 4.2's `type_cast_for_database`. For
|
53
|
-
# versions of AR < 4.2 we provide an implementation of
|
54
|
-
# `type_cast_for_database` in our shim attribute type classes,
|
55
|
-
# `NoOpAttribute` and `SerializedAttribute`.
|
56
|
-
class CastAttributeSerializer
|
57
|
-
def serialize(attr, val)
|
58
|
-
castable_val = val
|
59
|
-
if defined_enums[attr]
|
60
|
-
# `attr` is an enum. Find the number that corresponds to `val`. If `val` is
|
61
|
-
# a number already, there won't be a corresponding entry, just use `val`.
|
62
|
-
castable_val = defined_enums[attr][val] || val
|
63
|
-
end
|
64
|
-
@klass.type_for_attribute(attr).type_cast_for_database(castable_val)
|
65
|
-
end
|
66
40
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
if defined_enums[attr]
|
74
|
-
defined_enums[attr].key(val)
|
75
|
-
else
|
76
|
-
val
|
77
|
-
end
|
78
|
-
end
|
41
|
+
def deserialize(attr, val)
|
42
|
+
if defined_enums[attr] && val.is_a?(::String)
|
43
|
+
# Because PT 4 used to save the string version of enums to `object_changes`
|
44
|
+
val
|
45
|
+
else
|
46
|
+
AttributeSerializerFactory.for(@klass, attr).deserialize(val)
|
79
47
|
end
|
80
48
|
end
|
81
49
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
# Rails does not follow SemVer, makes breaking changes in minor versions.
|
5
|
+
# Breaking changes are expected, and are generally good for the rails
|
6
|
+
# ecosystem. However, they often require dozens of hours to fix, even with the
|
7
|
+
# [help of experts](https://github.com/paper-trail-gem/paper_trail/pull/899).
|
8
|
+
#
|
9
|
+
# It is not safe to assume that a new version of rails will be compatible with
|
10
|
+
# PaperTrail. PT is only compatible with the versions of rails that it is
|
11
|
+
# tested against. See `.travis.yml`.
|
12
|
+
#
|
13
|
+
# However, as of
|
14
|
+
# [#1213](https://github.com/paper-trail-gem/paper_trail/pull/1213) our
|
15
|
+
# gemspec allows installation with newer, incompatible rails versions. We hope
|
16
|
+
# this will make it easier for contributors to work on compatibility with
|
17
|
+
# newer rails versions. Most PT users should avoid incompatible rails
|
18
|
+
# versions.
|
19
|
+
module Compatibility
|
20
|
+
ACTIVERECORD_GTE = ">= 5.2" # enforced in gemspec
|
21
|
+
ACTIVERECORD_LT = "< 6.1" # not enforced in gemspec
|
22
|
+
|
23
|
+
E_INCOMPATIBLE_AR = <<-EOS
|
24
|
+
PaperTrail %s is not compatible with ActiveRecord %s. We allow PT
|
25
|
+
contributors to install incompatible versions of ActiveRecord, and this
|
26
|
+
warning can be silenced with an environment variable, but this is a bad
|
27
|
+
idea for normal use. Please install a compatible version of ActiveRecord
|
28
|
+
instead (%s). Please see the discussion in paper_trail/compatibility.rb
|
29
|
+
for details.
|
30
|
+
EOS
|
31
|
+
|
32
|
+
# Normal users need a warning if they accidentally install an incompatible
|
33
|
+
# version of ActiveRecord. Contributors can silence this warning with an
|
34
|
+
# environment variable.
|
35
|
+
def self.check_activerecord(ar_version)
|
36
|
+
raise ::TypeError unless ar_version.instance_of?(::Gem::Version)
|
37
|
+
return if ::ENV["PT_SILENCE_AR_COMPAT_WARNING"].present?
|
38
|
+
req = ::Gem::Requirement.new([ACTIVERECORD_GTE, ACTIVERECORD_LT])
|
39
|
+
unless req.satisfied_by?(ar_version)
|
40
|
+
::Kernel.warn(
|
41
|
+
format(
|
42
|
+
E_INCOMPATIBLE_AR,
|
43
|
+
::PaperTrail.gem_version,
|
44
|
+
ar_version,
|
45
|
+
req
|
46
|
+
)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/paper_trail/config.rb
CHANGED
@@ -9,14 +9,6 @@ module PaperTrail
|
|
9
9
|
class Config
|
10
10
|
include Singleton
|
11
11
|
|
12
|
-
E_PT_AT_REMOVED = <<-EOS.squish
|
13
|
-
Association Tracking for PaperTrail has been extracted to a separate gem.
|
14
|
-
To use it, please add `paper_trail-association_tracking` to your Gemfile.
|
15
|
-
If you don't use it (most people don't, that's the default) and you set
|
16
|
-
`track_associations = false` somewhere (probably a rails initializer) you
|
17
|
-
can remove that line now.
|
18
|
-
EOS
|
19
|
-
|
20
12
|
attr_accessor(
|
21
13
|
:association_reify_error_behaviour,
|
22
14
|
:object_changes_adapter,
|
@@ -43,30 +35,5 @@ module PaperTrail
|
|
43
35
|
def enabled=(enable)
|
44
36
|
@mutex.synchronize { @enabled = enable }
|
45
37
|
end
|
46
|
-
|
47
|
-
# In PT 10, the paper_trail-association_tracking gem was changed from a
|
48
|
-
# runtime dependency to a development dependency. We raise an error about
|
49
|
-
# this for the people who don't read changelogs.
|
50
|
-
#
|
51
|
-
# We raise a generic RuntimeError instead of a specific PT error class
|
52
|
-
# because there is no known use case where someone would want to rescue
|
53
|
-
# this. If we think of such a use case in the future we can revisit this
|
54
|
-
# decision.
|
55
|
-
#
|
56
|
-
# @override If PT-AT is `require`d, it will replace this method with its
|
57
|
-
# own implementation.
|
58
|
-
def track_associations=(value)
|
59
|
-
if value
|
60
|
-
raise E_PT_AT_REMOVED
|
61
|
-
else
|
62
|
-
::Kernel.warn(E_PT_AT_REMOVED)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# @override If PT-AT is `require`d, it will replace this method with its
|
67
|
-
# own implementation.
|
68
|
-
def track_associations?
|
69
|
-
raise E_PT_AT_REMOVED
|
70
|
-
end
|
71
38
|
end
|
72
39
|
end
|
@@ -59,14 +59,29 @@ module PaperTrail
|
|
59
59
|
end
|
60
60
|
|
61
61
|
# @api private
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
def nonskipped_attributes_before_change(is_touch)
|
63
|
+
cache_changed_attributes do
|
64
|
+
record_attributes = @record.attributes.except(*@record.paper_trail_options[:skip])
|
65
|
+
|
66
|
+
record_attributes.each_key do |k|
|
67
|
+
if @record.class.column_names.include?(k)
|
68
|
+
record_attributes[k] = attribute_in_previous_version(k, is_touch)
|
69
|
+
end
|
68
70
|
end
|
69
|
-
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Rails 5.1 changed the API of `ActiveRecord::Dirty`.
|
75
|
+
# @api private
|
76
|
+
def cache_changed_attributes(&block)
|
77
|
+
if RAILS_GTE_5_1
|
78
|
+
# Everything works fine as it is
|
79
|
+
yield
|
80
|
+
else
|
81
|
+
# Any particular call to `changed_attributes` produces the huge memory allocation.
|
82
|
+
# Lets use the generic AR workaround for that.
|
83
|
+
@record.send(:cache_changed_attributes, &block)
|
84
|
+
end
|
70
85
|
end
|
71
86
|
|
72
87
|
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
@@ -92,7 +107,7 @@ module PaperTrail
|
|
92
107
|
end
|
93
108
|
|
94
109
|
# @api private
|
95
|
-
def
|
110
|
+
def calculated_ignored_array
|
96
111
|
ignore = @record.paper_trail_options[:ignore].dup
|
97
112
|
# Remove Hash arguments and then evaluate whether the attributes (the
|
98
113
|
# keys of the hash) should also get pushed into the collection.
|
@@ -102,42 +117,18 @@ module PaperTrail
|
|
102
117
|
ignore << attr if condition.respond_to?(:call) && condition.call(@record)
|
103
118
|
}
|
104
119
|
end
|
105
|
-
skip = @record.paper_trail_options[:skip]
|
106
|
-
(changed_in_latest_version - ignore) - skip
|
107
120
|
end
|
108
121
|
|
109
|
-
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
110
|
-
# https://github.com/paper-trail-gem/paper_trail/pull/899
|
111
|
-
#
|
112
122
|
# @api private
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
else
|
117
|
-
@record.changed
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# @api private
|
122
|
-
def prepare_object_changes(changes)
|
123
|
-
changes = serialize_object_changes(changes)
|
124
|
-
changes = recordable_object_changes(changes)
|
125
|
-
changes
|
126
|
-
end
|
127
|
-
|
128
|
-
# @api private
|
129
|
-
def serialize_object_changes(changes)
|
130
|
-
AttributeSerializers::ObjectChangesAttribute.
|
131
|
-
new(@record.class).
|
132
|
-
serialize(changes)
|
133
|
-
changes.to_hash
|
123
|
+
def changed_and_not_ignored
|
124
|
+
skip = @record.paper_trail_options[:skip]
|
125
|
+
(changed_in_latest_version - calculated_ignored_array) - skip
|
134
126
|
end
|
135
127
|
|
136
128
|
# @api private
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
}
|
129
|
+
def changed_in_latest_version
|
130
|
+
# Memoized to reduce memory usage
|
131
|
+
@changed_in_latest_version ||= changes_in_latest_version.keys
|
141
132
|
end
|
142
133
|
|
143
134
|
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
@@ -145,10 +136,13 @@ module PaperTrail
|
|
145
136
|
#
|
146
137
|
# @api private
|
147
138
|
def changes_in_latest_version
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
139
|
+
# Memoized to reduce memory usage
|
140
|
+
@changes_in_latest_version ||= begin
|
141
|
+
if @in_after_callback && RAILS_GTE_5_1
|
142
|
+
@record.saved_changes
|
143
|
+
else
|
144
|
+
@record.changes
|
145
|
+
end
|
152
146
|
end
|
153
147
|
end
|
154
148
|
|
@@ -158,7 +152,7 @@ module PaperTrail
|
|
158
152
|
#
|
159
153
|
# @api private
|
160
154
|
def ignored_attr_has_changed?
|
161
|
-
ignored =
|
155
|
+
ignored = calculated_ignored_array + @record.paper_trail_options[:skip]
|
162
156
|
ignored.any? && (changed_in_latest_version & ignored).any?
|
163
157
|
end
|
164
158
|
|
@@ -220,18 +214,28 @@ module PaperTrail
|
|
220
214
|
end
|
221
215
|
end
|
222
216
|
|
217
|
+
# @api private
|
218
|
+
def notable_changes
|
219
|
+
changes_in_latest_version.delete_if { |k, _v|
|
220
|
+
!notably_changed.include?(k)
|
221
|
+
}
|
222
|
+
end
|
223
|
+
|
223
224
|
# @api private
|
224
225
|
def notably_changed
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
226
|
+
# Memoized to reduce memory usage
|
227
|
+
@notably_changed ||= begin
|
228
|
+
only = @record.paper_trail_options[:only].dup
|
229
|
+
# Remove Hash arguments and then evaluate whether the attributes (the
|
230
|
+
# keys of the hash) should also get pushed into the collection.
|
231
|
+
only.delete_if do |obj|
|
232
|
+
obj.is_a?(Hash) &&
|
233
|
+
obj.each { |attr, condition|
|
234
|
+
only << attr if condition.respond_to?(:call) && condition.call(@record)
|
235
|
+
}
|
236
|
+
end
|
237
|
+
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
|
233
238
|
end
|
234
|
-
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
|
235
239
|
end
|
236
240
|
|
237
241
|
# Returns hash of attributes (with appropriate attributes serialized),
|
@@ -239,12 +243,17 @@ module PaperTrail
|
|
239
243
|
#
|
240
244
|
# @api private
|
241
245
|
def object_attrs_for_paper_trail(is_touch)
|
242
|
-
attrs =
|
243
|
-
except(*@record.paper_trail_options[:skip])
|
246
|
+
attrs = nonskipped_attributes_before_change(is_touch)
|
244
247
|
AttributeSerializers::ObjectAttribute.new(@record.class).serialize(attrs)
|
245
248
|
attrs
|
246
249
|
end
|
247
250
|
|
251
|
+
# @api private
|
252
|
+
def prepare_object_changes(changes)
|
253
|
+
changes = serialize_object_changes(changes)
|
254
|
+
recordable_object_changes(changes)
|
255
|
+
end
|
256
|
+
|
248
257
|
# Returns an object which can be assigned to the `object_changes`
|
249
258
|
# attribute of a nascent version record. If the `object_changes` column is
|
250
259
|
# a postgres `json` column, then a hash can be used in the assignment,
|
@@ -252,9 +261,14 @@ module PaperTrail
|
|
252
261
|
# serialization here, using `PaperTrail.serializer`.
|
253
262
|
#
|
254
263
|
# @api private
|
264
|
+
# @param changes HashWithIndifferentAccess
|
255
265
|
def recordable_object_changes(changes)
|
256
266
|
if PaperTrail.config.object_changes_adapter&.respond_to?(:diff)
|
257
|
-
|
267
|
+
# We'd like to avoid the `to_hash` here, because it increases memory
|
268
|
+
# usage, but that would be a breaking change because
|
269
|
+
# `object_changes_adapter` expects a plain `Hash`, not a
|
270
|
+
# `HashWithIndifferentAccess`.
|
271
|
+
changes = PaperTrail.config.object_changes_adapter.diff(changes.to_hash)
|
258
272
|
end
|
259
273
|
|
260
274
|
if @record.class.paper_trail.version_class.object_changes_col_is_json?
|
@@ -293,6 +307,17 @@ module PaperTrail
|
|
293
307
|
PaperTrail.serializer.dump(object_attrs_for_paper_trail(is_touch))
|
294
308
|
end
|
295
309
|
end
|
310
|
+
|
311
|
+
# @api private
|
312
|
+
def serialize_object_changes(changes)
|
313
|
+
AttributeSerializers::ObjectChangesAttribute.
|
314
|
+
new(@record.class).
|
315
|
+
serialize(changes)
|
316
|
+
|
317
|
+
# We'd like to convert this `HashWithIndifferentAccess` to a plain
|
318
|
+
# `Hash`, but we don't, to save memory.
|
319
|
+
changes
|
320
|
+
end
|
296
321
|
end
|
297
322
|
end
|
298
323
|
end
|
@@ -22,13 +22,21 @@ module PaperTrail
|
|
22
22
|
data[:object] = recordable_object(false)
|
23
23
|
end
|
24
24
|
if record_object_changes?
|
25
|
-
|
26
|
-
changes = @record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
|
27
|
-
data[:object_changes] = prepare_object_changes(changes)
|
25
|
+
data[:object_changes] = prepare_object_changes(notable_changes)
|
28
26
|
end
|
29
27
|
merge_item_subtype_into(data)
|
30
28
|
merge_metadata_into(data)
|
31
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Rails' implementation (eg. `@record.saved_changes`) returns nothing on
|
34
|
+
# destroy, so we have to build the hash we want.
|
35
|
+
#
|
36
|
+
# @override
|
37
|
+
def changes_in_latest_version
|
38
|
+
@record.attributes.map { |attr, value| [attr, [value, nil]] }.to_h
|
39
|
+
end
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
@@ -25,9 +25,7 @@ module PaperTrail
|
|
25
25
|
# @api public
|
26
26
|
def user_for_paper_trail
|
27
27
|
return unless defined?(current_user)
|
28
|
-
|
29
|
-
rescue NoMethodError
|
30
|
-
current_user
|
28
|
+
current_user.try(:id) || current_user
|
31
29
|
end
|
32
30
|
|
33
31
|
# Returns any information about the controller or request that you
|
@@ -4,11 +4,42 @@ module PaperTrail
|
|
4
4
|
module Rails
|
5
5
|
# See http://guides.rubyonrails.org/engines.html
|
6
6
|
class Engine < ::Rails::Engine
|
7
|
+
DPR_CONFIG_ENABLED = <<~EOS.squish.freeze
|
8
|
+
The rails configuration option config.paper_trail.enabled is deprecated.
|
9
|
+
Please use PaperTrail.enabled= instead. People were getting confused
|
10
|
+
that PT has both, specifically regarding *when* each was happening. If
|
11
|
+
you'd like to keep config.paper_trail, join the discussion at
|
12
|
+
https://github.com/paper-trail-gem/paper_trail/pull/1176
|
13
|
+
EOS
|
14
|
+
private_constant :DPR_CONFIG_ENABLED
|
15
|
+
DPR_RUDELY_ENABLING = <<~EOS.squish.freeze
|
16
|
+
At some point early in the rails boot process, you have set
|
17
|
+
PaperTrail.enabled = false. PT's rails engine is now overriding your
|
18
|
+
setting, and setting it to true. We're not sure why, but this is how PT
|
19
|
+
has worked since 5.0, when the config.paper_trail.enabled option was
|
20
|
+
introduced. This is now deprecated. In the future, PT will not override
|
21
|
+
your setting. See
|
22
|
+
https://github.com/paper-trail-gem/paper_trail/pull/1176 for discussion.
|
23
|
+
EOS
|
24
|
+
private_constant :DPR_RUDELY_ENABLING
|
25
|
+
|
7
26
|
paths["app/models"] << "lib/paper_trail/frameworks/active_record/models"
|
27
|
+
|
28
|
+
# --- Begin deprecated section ---
|
8
29
|
config.paper_trail = ActiveSupport::OrderedOptions.new
|
9
30
|
initializer "paper_trail.initialisation" do |app|
|
10
|
-
|
31
|
+
enable = app.config.paper_trail[:enabled]
|
32
|
+
if enable.nil?
|
33
|
+
unless PaperTrail.enabled?
|
34
|
+
::ActiveSupport::Deprecation.warn(DPR_RUDELY_ENABLING)
|
35
|
+
PaperTrail.enabled = true
|
36
|
+
end
|
37
|
+
else
|
38
|
+
::ActiveSupport::Deprecation.warn(DPR_CONFIG_ENABLED)
|
39
|
+
PaperTrail.enabled = enable
|
40
|
+
end
|
11
41
|
end
|
42
|
+
# --- End deprecated section ---
|
12
43
|
end
|
13
44
|
end
|
14
45
|
end
|
@@ -18,6 +18,11 @@ module PaperTrail
|
|
18
18
|
`abstract_class`. This is fine, but all application models must be
|
19
19
|
configured to use concrete (not abstract) version models.
|
20
20
|
STR
|
21
|
+
E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE = <<~STR.squish.freeze
|
22
|
+
To use PaperTrail's per-model limit in your %s model, you must have an
|
23
|
+
item_subtype column in your versions table. See documentation sections
|
24
|
+
2.e.1 Per-model limit, and 4.b.1 The optional item_subtype column.
|
25
|
+
STR
|
21
26
|
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION = <<~STR.squish
|
22
27
|
Passing versions association name as `has_paper_trail versions: %{versions_name}`
|
23
28
|
is deprecated. Use `has_paper_trail versions: {name: %{versions_name}}` instead.
|
@@ -112,6 +117,7 @@ module PaperTrail
|
|
112
117
|
@model_class.send :include, ::PaperTrail::Model::InstanceMethods
|
113
118
|
setup_options(options)
|
114
119
|
setup_associations(options)
|
120
|
+
check_presence_of_item_subtype_column(options)
|
115
121
|
@model_class.after_rollback { paper_trail.clear_rolled_back_versions }
|
116
122
|
setup_callbacks_from_options options[:on]
|
117
123
|
end
|
@@ -122,10 +128,6 @@ module PaperTrail
|
|
122
128
|
|
123
129
|
private
|
124
130
|
|
125
|
-
def active_record_gem_version
|
126
|
-
Gem::Version.new(ActiveRecord::VERSION::STRING)
|
127
|
-
end
|
128
|
-
|
129
131
|
# Raises an error if the provided class is an `abstract_class`.
|
130
132
|
# @api private
|
131
133
|
def assert_concrete_activerecord_class(class_name)
|
@@ -135,8 +137,17 @@ module PaperTrail
|
|
135
137
|
end
|
136
138
|
|
137
139
|
def cannot_record_after_destroy?
|
138
|
-
|
139
|
-
|
140
|
+
::ActiveRecord::Base.belongs_to_required_by_default
|
141
|
+
end
|
142
|
+
|
143
|
+
# Some options require the presence of the `item_subtype` column. Currently
|
144
|
+
# only `limit`, but in the future there may be others.
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
def check_presence_of_item_subtype_column(options)
|
148
|
+
return unless options.key?(:limit)
|
149
|
+
return if version_class.item_subtype_column_present?
|
150
|
+
raise format(E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE, @model_class.name)
|
140
151
|
end
|
141
152
|
|
142
153
|
def check_version_class_name(options)
|
@@ -67,14 +67,14 @@ module PaperTrail
|
|
67
67
|
|
68
68
|
def record_create
|
69
69
|
return unless enabled?
|
70
|
-
event = Events::Create.new(@record, true)
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
build_version_on_create(in_after_callback: true).tap do |version|
|
72
|
+
version.save!
|
73
|
+
# Because the version object was created using version_class.new instead
|
74
|
+
# of versions_assoc.build?, the association cache is unaware. So, we
|
75
|
+
# invalidate the `versions` association cache with `reset`.
|
76
|
+
versions.reset
|
77
|
+
end
|
78
78
|
end
|
79
79
|
|
80
80
|
# PT-AT extends this method to add its transaction id.
|
@@ -119,19 +119,22 @@ module PaperTrail
|
|
119
119
|
# paper_trail-association_tracking
|
120
120
|
def record_update(force:, in_after_callback:, is_touch:)
|
121
121
|
return unless enabled?
|
122
|
-
event = Events::Update.new(@record, in_after_callback, is_touch, nil)
|
123
|
-
return unless force || event.changed_notably?
|
124
122
|
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
version = build_version_on_update(
|
124
|
+
force: force,
|
125
|
+
in_after_callback: in_after_callback,
|
126
|
+
is_touch: is_touch
|
127
|
+
)
|
128
|
+
return unless version
|
128
129
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
130
|
+
if version.save
|
131
|
+
# Because the version object was created using version_class.new instead
|
132
|
+
# of versions_assoc.build?, the association cache is unaware. So, we
|
133
|
+
# invalidate the `versions` association cache with `reset`.
|
134
|
+
versions.reset
|
134
135
|
version
|
136
|
+
else
|
137
|
+
log_version_errors(version, :update)
|
135
138
|
end
|
136
139
|
end
|
137
140
|
|
@@ -250,6 +253,35 @@ module PaperTrail
|
|
250
253
|
@record.send(@record.class.versions_association_name).reset
|
251
254
|
end
|
252
255
|
|
256
|
+
# @api private
|
257
|
+
def build_version_on_create(in_after_callback:)
|
258
|
+
event = Events::Create.new(@record, in_after_callback)
|
259
|
+
|
260
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
261
|
+
# `data_for_create` but PT-AT still does.
|
262
|
+
data = event.data.merge!(data_for_create)
|
263
|
+
|
264
|
+
# Pure `version_class.new` reduces memory usage compared to `versions_assoc.build`
|
265
|
+
@record.class.paper_trail.version_class.new(data)
|
266
|
+
end
|
267
|
+
|
268
|
+
# @api private
|
269
|
+
def build_version_on_update(force:, in_after_callback:, is_touch:)
|
270
|
+
event = Events::Update.new(@record, in_after_callback, is_touch, nil)
|
271
|
+
return unless force || event.changed_notably?
|
272
|
+
|
273
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
274
|
+
# `data_for_update` but PT-AT still does. To save memory, we use `merge!`
|
275
|
+
# instead of `merge`.
|
276
|
+
data = event.data.merge!(data_for_update)
|
277
|
+
|
278
|
+
# Using `version_class.new` reduces memory usage compared to
|
279
|
+
# `versions_assoc.build`. It's a trade-off though. We have to clear
|
280
|
+
# the association cache (see `versions.reset`) and that could cause an
|
281
|
+
# additional query in certain applications.
|
282
|
+
@record.class.paper_trail.version_class.new(data)
|
283
|
+
end
|
284
|
+
|
253
285
|
def log_version_errors(version, action)
|
254
286
|
version.logger&.warn(
|
255
287
|
"Unable to create version for #{action} of #{@record.class.name}" \
|
data/lib/paper_trail/reifier.rb
CHANGED
@@ -52,23 +52,23 @@ module PaperTrail
|
|
52
52
|
# not the actual subclass. If `type` is present but empty, the class is
|
53
53
|
# the base class.
|
54
54
|
def init_model(attrs, options, version)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
55
|
+
klass = version_reification_class(version, attrs)
|
56
|
+
|
57
|
+
# The `dup` option and destroyed version always returns a new object,
|
58
|
+
# otherwise we should attempt to load item or to look for the item
|
59
|
+
# outside of default scope(s).
|
60
|
+
model = if options[:dup] == true || version.event == "destroy"
|
61
|
+
klass.new
|
62
|
+
else
|
63
|
+
find_cond = { klass.primary_key => version.item_id }
|
64
|
+
|
65
|
+
version.item || klass.unscoped.where(find_cond).first || klass.new
|
66
|
+
end
|
67
|
+
|
68
|
+
if options[:unversioned_attributes] == :nil && !model.new_record?
|
69
|
+
init_unversioned_attrs(attrs, model)
|
71
70
|
end
|
71
|
+
|
72
72
|
model
|
73
73
|
end
|
74
74
|
|
@@ -88,9 +88,7 @@ module PaperTrail
|
|
88
88
|
#
|
89
89
|
# @api private
|
90
90
|
def reify_attribute(k, v, model, version)
|
91
|
-
|
92
|
-
is_enum_without_type_caster = ::ActiveRecord::VERSION::MAJOR < 5 && enums.key?(k)
|
93
|
-
if model.has_attribute?(k) && !is_enum_without_type_caster
|
91
|
+
if model.has_attribute?(k)
|
94
92
|
model[k.to_sym] = v
|
95
93
|
elsif model.respond_to?("#{k}=")
|
96
94
|
model.send("#{k}=", v)
|
@@ -120,6 +118,7 @@ module PaperTrail
|
|
120
118
|
# this method returns the constant `Animal`. You can see this particular
|
121
119
|
# example in action in `spec/models/animal_spec.rb`.
|
122
120
|
#
|
121
|
+
# TODO: Duplication: similar `constantize` in VersionConcern#version_limit
|
123
122
|
def version_reification_class(version, attrs)
|
124
123
|
inheritance_column_name = version.item_type.constantize.inheritance_column
|
125
124
|
inher_col_value = attrs[inheritance_column_name]
|
@@ -12,7 +12,12 @@ module PaperTrail
|
|
12
12
|
::YAML.load string
|
13
13
|
end
|
14
14
|
|
15
|
+
# @param object (Hash | HashWithIndifferentAccess) - Coming from
|
16
|
+
# `recordable_object` `object` will be a plain `Hash`. However, due to
|
17
|
+
# recent [memory optimizations](https://git.io/fjeYv), when coming from
|
18
|
+
# `recordable_object_changes`, it will be a `HashWithIndifferentAccess`.
|
15
19
|
def dump(object)
|
20
|
+
object = object.to_hash if object.is_a?(HashWithIndifferentAccess)
|
16
21
|
::YAML.dump object
|
17
22
|
end
|
18
23
|
|
@@ -25,6 +25,10 @@ module PaperTrail
|
|
25
25
|
|
26
26
|
# :nodoc:
|
27
27
|
module ClassMethods
|
28
|
+
def item_subtype_column_present?
|
29
|
+
column_names.include?("item_subtype")
|
30
|
+
end
|
31
|
+
|
28
32
|
def with_item_keys(item_type, item_id)
|
29
33
|
where item_type: item_type, item_id: item_id
|
30
34
|
end
|
@@ -141,6 +145,7 @@ module PaperTrail
|
|
141
145
|
# Default: false.
|
142
146
|
# @return `ActiveRecord::Relation`
|
143
147
|
# @api public
|
148
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
144
149
|
def preceding(obj, timestamp_arg = false)
|
145
150
|
if timestamp_arg != true && primary_key_is_int?
|
146
151
|
preceding_by_id(obj)
|
@@ -148,6 +153,7 @@ module PaperTrail
|
|
148
153
|
preceding_by_timestamp(obj)
|
149
154
|
end
|
150
155
|
end
|
156
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
151
157
|
|
152
158
|
# Returns versions after `obj`.
|
153
159
|
#
|
@@ -156,6 +162,7 @@ module PaperTrail
|
|
156
162
|
# Default: false.
|
157
163
|
# @return `ActiveRecord::Relation`
|
158
164
|
# @api public
|
165
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
159
166
|
def subsequent(obj, timestamp_arg = false)
|
160
167
|
if timestamp_arg != true && primary_key_is_int?
|
161
168
|
subsequent_by_id(obj)
|
@@ -163,6 +170,7 @@ module PaperTrail
|
|
163
170
|
subsequent_by_timestamp(obj)
|
164
171
|
end
|
165
172
|
end
|
173
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
166
174
|
|
167
175
|
private
|
168
176
|
|
@@ -201,18 +209,8 @@ module PaperTrail
|
|
201
209
|
|
202
210
|
# Restore the item from this version.
|
203
211
|
#
|
204
|
-
# Optionally this can also restore all :has_one and :has_many (including
|
205
|
-
# has_many :through) associations as they were "at the time", if they are
|
206
|
-
# also being versioned by PaperTrail.
|
207
|
-
#
|
208
212
|
# Options:
|
209
213
|
#
|
210
|
-
# - :has_one
|
211
|
-
# - `true` - Also reify has_one associations.
|
212
|
-
# - `false - Default.
|
213
|
-
# - :has_many
|
214
|
-
# - `true` - Also reify has_many and has_many :through associations.
|
215
|
-
# - `false` - Default.
|
216
214
|
# - :mark_for_destruction
|
217
215
|
# - `true` - Mark the has_one/has_many associations that did not exist in
|
218
216
|
# the reified version for destruction, instead of removing them.
|
@@ -254,13 +252,6 @@ module PaperTrail
|
|
254
252
|
end
|
255
253
|
alias version_author terminator
|
256
254
|
|
257
|
-
def sibling_versions(reload = false)
|
258
|
-
if reload || !defined?(@sibling_versions) || @sibling_versions.nil?
|
259
|
-
@sibling_versions = self.class.with_item_keys(item_type, item_id)
|
260
|
-
end
|
261
|
-
@sibling_versions
|
262
|
-
end
|
263
|
-
|
264
255
|
def next
|
265
256
|
@next ||= sibling_versions.subsequent(self).first
|
266
257
|
end
|
@@ -270,8 +261,9 @@ module PaperTrail
|
|
270
261
|
end
|
271
262
|
|
272
263
|
# Returns an integer representing the chronological position of the
|
273
|
-
# version among its siblings
|
274
|
-
#
|
264
|
+
# version among its siblings. The "create" event, for example, has an index
|
265
|
+
# of 0.
|
266
|
+
#
|
275
267
|
# @api public
|
276
268
|
def index
|
277
269
|
@index ||= RecordHistory.new(sibling_versions, self.class).index(self)
|
@@ -329,7 +321,7 @@ module PaperTrail
|
|
329
321
|
# Enforces the `version_limit`, if set. Default: no limit.
|
330
322
|
# @api private
|
331
323
|
def enforce_version_limit!
|
332
|
-
limit =
|
324
|
+
limit = version_limit
|
333
325
|
return unless limit.is_a? Numeric
|
334
326
|
previous_versions = sibling_versions.not_creates.
|
335
327
|
order(self.class.timestamp_sort_order("asc"))
|
@@ -337,5 +329,26 @@ module PaperTrail
|
|
337
329
|
excess_versions = previous_versions - previous_versions.last(limit)
|
338
330
|
excess_versions.map(&:destroy)
|
339
331
|
end
|
332
|
+
|
333
|
+
# @api private
|
334
|
+
def sibling_versions
|
335
|
+
@sibling_versions ||= self.class.with_item_keys(item_type, item_id)
|
336
|
+
end
|
337
|
+
|
338
|
+
# See docs section 2.e. Limiting the Number of Versions Created.
|
339
|
+
# The version limit can be global or per-model.
|
340
|
+
#
|
341
|
+
# @api private
|
342
|
+
#
|
343
|
+
# TODO: Duplication: similar `constantize` in Reifier#version_reification_class
|
344
|
+
def version_limit
|
345
|
+
if self.class.item_subtype_column_present?
|
346
|
+
klass = (item_subtype || item_type).constantize
|
347
|
+
if klass&.paper_trail_options&.key?(:limit)
|
348
|
+
return klass.paper_trail_options[:limit]
|
349
|
+
end
|
350
|
+
end
|
351
|
+
PaperTrail.config.version_limit
|
352
|
+
end
|
340
353
|
end
|
341
354
|
end
|
@@ -7,8 +7,8 @@ module PaperTrail
|
|
7
7
|
# because of this confusion, but it's not worth the breaking change.
|
8
8
|
# People are encouraged to use `PaperTrail.gem_version` instead.
|
9
9
|
module VERSION
|
10
|
-
MAJOR =
|
11
|
-
MINOR =
|
10
|
+
MAJOR = 11
|
11
|
+
MINOR = 0
|
12
12
|
TINY = 0
|
13
13
|
|
14
14
|
# Set PRE to nil unless it's a pre-release (beta, rc, etc.)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paper_trail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 11.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Stewart
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-08-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -18,20 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
22
|
-
- - "<"
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: '6.0'
|
21
|
+
version: '5.2'
|
25
22
|
type: :runtime
|
26
23
|
prerelease: false
|
27
24
|
version_requirements: !ruby/object:Gem::Requirement
|
28
25
|
requirements:
|
29
26
|
- - ">="
|
30
27
|
- !ruby/object:Gem::Version
|
31
|
-
version: '
|
32
|
-
- - "<"
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '6.0'
|
28
|
+
version: '5.2'
|
35
29
|
- !ruby/object:Gem::Dependency
|
36
30
|
name: request_store
|
37
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,28 +60,28 @@ dependencies:
|
|
66
60
|
requirements:
|
67
61
|
- - "~>"
|
68
62
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
63
|
+
version: '11.0'
|
70
64
|
type: :development
|
71
65
|
prerelease: false
|
72
66
|
version_requirements: !ruby/object:Gem::Requirement
|
73
67
|
requirements:
|
74
68
|
- - "~>"
|
75
69
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
70
|
+
version: '11.0'
|
77
71
|
- !ruby/object:Gem::Dependency
|
78
72
|
name: ffaker
|
79
73
|
requirement: !ruby/object:Gem::Requirement
|
80
74
|
requirements:
|
81
75
|
- - "~>"
|
82
76
|
- !ruby/object:Gem::Version
|
83
|
-
version: '2.
|
77
|
+
version: '2.11'
|
84
78
|
type: :development
|
85
79
|
prerelease: false
|
86
80
|
version_requirements: !ruby/object:Gem::Requirement
|
87
81
|
requirements:
|
88
82
|
- - "~>"
|
89
83
|
- !ruby/object:Gem::Version
|
90
|
-
version: '2.
|
84
|
+
version: '2.11'
|
91
85
|
- !ruby/object:Gem::Dependency
|
92
86
|
name: generator_spec
|
93
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,117 +97,137 @@ dependencies:
|
|
103
97
|
- !ruby/object:Gem::Version
|
104
98
|
version: 0.9.4
|
105
99
|
- !ruby/object:Gem::Dependency
|
106
|
-
name:
|
100
|
+
name: memory_profiler
|
107
101
|
requirement: !ruby/object:Gem::Requirement
|
108
102
|
requirements:
|
109
103
|
- - "~>"
|
110
104
|
- !ruby/object:Gem::Version
|
111
|
-
version: 0.
|
105
|
+
version: 0.9.14
|
112
106
|
type: :development
|
113
107
|
prerelease: false
|
114
108
|
version_requirements: !ruby/object:Gem::Requirement
|
115
109
|
requirements:
|
116
110
|
- - "~>"
|
117
111
|
- !ruby/object:Gem::Version
|
118
|
-
version: 0.
|
112
|
+
version: 0.9.14
|
119
113
|
- !ruby/object:Gem::Dependency
|
120
|
-
name:
|
114
|
+
name: rake
|
121
115
|
requirement: !ruby/object:Gem::Requirement
|
122
116
|
requirements:
|
123
|
-
- - "
|
117
|
+
- - "~>"
|
124
118
|
- !ruby/object:Gem::Version
|
125
|
-
version: '
|
119
|
+
version: '13.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: '
|
126
|
+
version: '13.0'
|
133
127
|
- !ruby/object:Gem::Dependency
|
134
|
-
name:
|
128
|
+
name: rspec-rails
|
135
129
|
requirement: !ruby/object:Gem::Requirement
|
136
130
|
requirements:
|
137
131
|
- - "~>"
|
138
132
|
- !ruby/object:Gem::Version
|
139
|
-
version: '
|
133
|
+
version: '4.0'
|
140
134
|
type: :development
|
141
135
|
prerelease: false
|
142
136
|
version_requirements: !ruby/object:Gem::Requirement
|
143
137
|
requirements:
|
144
138
|
- - "~>"
|
145
139
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
140
|
+
version: '4.0'
|
147
141
|
- !ruby/object:Gem::Dependency
|
148
|
-
name:
|
142
|
+
name: rubocop
|
149
143
|
requirement: !ruby/object:Gem::Requirement
|
150
144
|
requirements:
|
151
145
|
- - "~>"
|
152
146
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
147
|
+
version: 0.89.1
|
154
148
|
type: :development
|
155
149
|
prerelease: false
|
156
150
|
version_requirements: !ruby/object:Gem::Requirement
|
157
151
|
requirements:
|
158
152
|
- - "~>"
|
159
153
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
154
|
+
version: 0.89.1
|
161
155
|
- !ruby/object:Gem::Dependency
|
162
|
-
name:
|
156
|
+
name: rubocop-performance
|
163
157
|
requirement: !ruby/object:Gem::Requirement
|
164
158
|
requirements:
|
165
159
|
- - "~>"
|
166
160
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
161
|
+
version: 1.7.1
|
168
162
|
type: :development
|
169
163
|
prerelease: false
|
170
164
|
version_requirements: !ruby/object:Gem::Requirement
|
171
165
|
requirements:
|
172
166
|
- - "~>"
|
173
167
|
- !ruby/object:Gem::Version
|
174
|
-
version:
|
168
|
+
version: 1.7.1
|
175
169
|
- !ruby/object:Gem::Dependency
|
176
|
-
name: rubocop
|
170
|
+
name: rubocop-rspec
|
177
171
|
requirement: !ruby/object:Gem::Requirement
|
178
172
|
requirements:
|
179
173
|
- - "~>"
|
180
174
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
175
|
+
version: 1.42.0
|
182
176
|
type: :development
|
183
177
|
prerelease: false
|
184
178
|
version_requirements: !ruby/object:Gem::Requirement
|
185
179
|
requirements:
|
186
180
|
- - "~>"
|
187
181
|
- !ruby/object:Gem::Version
|
188
|
-
version:
|
182
|
+
version: 1.42.0
|
189
183
|
- !ruby/object:Gem::Dependency
|
190
|
-
name:
|
184
|
+
name: mysql2
|
191
185
|
requirement: !ruby/object:Gem::Requirement
|
192
186
|
requirements:
|
193
187
|
- - "~>"
|
194
188
|
- !ruby/object:Gem::Version
|
195
|
-
version:
|
189
|
+
version: '0.5'
|
196
190
|
type: :development
|
197
191
|
prerelease: false
|
198
192
|
version_requirements: !ruby/object:Gem::Requirement
|
199
193
|
requirements:
|
200
194
|
- - "~>"
|
201
195
|
- !ruby/object:Gem::Version
|
202
|
-
version:
|
196
|
+
version: '0.5'
|
197
|
+
- !ruby/object:Gem::Dependency
|
198
|
+
name: pg
|
199
|
+
requirement: !ruby/object:Gem::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: '0.18'
|
204
|
+
- - "<"
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: '2.0'
|
207
|
+
type: :development
|
208
|
+
prerelease: false
|
209
|
+
version_requirements: !ruby/object:Gem::Requirement
|
210
|
+
requirements:
|
211
|
+
- - ">="
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0.18'
|
214
|
+
- - "<"
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '2.0'
|
203
217
|
- !ruby/object:Gem::Dependency
|
204
218
|
name: sqlite3
|
205
219
|
requirement: !ruby/object:Gem::Requirement
|
206
220
|
requirements:
|
207
221
|
- - "~>"
|
208
222
|
- !ruby/object:Gem::Version
|
209
|
-
version: '1.
|
223
|
+
version: '1.4'
|
210
224
|
type: :development
|
211
225
|
prerelease: false
|
212
226
|
version_requirements: !ruby/object:Gem::Requirement
|
213
227
|
requirements:
|
214
228
|
- - "~>"
|
215
229
|
- !ruby/object:Gem::Version
|
216
|
-
version: '1.
|
230
|
+
version: '1.4'
|
217
231
|
description: |
|
218
232
|
Track changes to your models, for auditing or versioning. See how a model looked
|
219
233
|
at any stage in its lifecycle, revert it to any version, or restore it after it
|
@@ -227,7 +241,6 @@ files:
|
|
227
241
|
- lib/generators/paper_trail/install/install_generator.rb
|
228
242
|
- lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb
|
229
243
|
- lib/generators/paper_trail/install/templates/create_versions.rb.erb
|
230
|
-
- lib/generators/paper_trail/install_generator.rb
|
231
244
|
- lib/generators/paper_trail/migration_generator.rb
|
232
245
|
- lib/generators/paper_trail/update_item_subtype/USAGE
|
233
246
|
- lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb
|
@@ -239,6 +252,7 @@ files:
|
|
239
252
|
- lib/paper_trail/attribute_serializers/object_attribute.rb
|
240
253
|
- lib/paper_trail/attribute_serializers/object_changes_attribute.rb
|
241
254
|
- lib/paper_trail/cleaner.rb
|
255
|
+
- lib/paper_trail/compatibility.rb
|
242
256
|
- lib/paper_trail/config.rb
|
243
257
|
- lib/paper_trail/events/base.rb
|
244
258
|
- lib/paper_trail/events/create.rb
|
@@ -277,15 +291,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
277
291
|
requirements:
|
278
292
|
- - ">="
|
279
293
|
- !ruby/object:Gem::Version
|
280
|
-
version: 2.
|
294
|
+
version: 2.4.0
|
281
295
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
282
296
|
requirements:
|
283
297
|
- - ">="
|
284
298
|
- !ruby/object:Gem::Version
|
285
299
|
version: 1.3.6
|
286
300
|
requirements: []
|
287
|
-
|
288
|
-
rubygems_version: 2.6.14.3
|
301
|
+
rubygems_version: 3.0.3
|
289
302
|
signing_key:
|
290
303
|
specification_version: 4
|
291
304
|
summary: Track changes to your models.
|
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rails/generators"
|
4
|
-
require "rails/generators/active_record"
|
5
|
-
|
6
|
-
module PaperTrail
|
7
|
-
# Installs PaperTrail in a rails app.
|
8
|
-
class InstallGenerator < ::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", __dir__)
|
20
|
-
class_option(
|
21
|
-
:with_changes,
|
22
|
-
type: :boolean,
|
23
|
-
default: false,
|
24
|
-
desc: "Store changeset (diff) with each version"
|
25
|
-
)
|
26
|
-
|
27
|
-
desc "Generates (but does not run) a migration to add a versions table."
|
28
|
-
|
29
|
-
def create_migration_file
|
30
|
-
add_paper_trail_migration("create_versions")
|
31
|
-
add_paper_trail_migration("add_object_changes_to_versions") if options.with_changes?
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.next_migration_number(dirname)
|
35
|
-
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
36
|
-
end
|
37
|
-
|
38
|
-
protected
|
39
|
-
|
40
|
-
def add_paper_trail_migration(template)
|
41
|
-
migration_dir = File.expand_path("db/migrate")
|
42
|
-
if self.class.migration_exists?(migration_dir, template)
|
43
|
-
::Kernel.warn "Migration already exists: #{template}"
|
44
|
-
else
|
45
|
-
migration_template(
|
46
|
-
"#{template}.rb.erb",
|
47
|
-
"db/migrate/#{template}.rb",
|
48
|
-
item_type_options: item_type_options,
|
49
|
-
migration_version: migration_version,
|
50
|
-
versions_table_options: versions_table_options
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
# MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
|
58
|
-
# See https://github.com/paper-trail-gem/paper_trail/issues/651
|
59
|
-
def item_type_options
|
60
|
-
opt = { null: false }
|
61
|
-
opt[:limit] = 191 if mysql?
|
62
|
-
", #{opt}"
|
63
|
-
end
|
64
|
-
|
65
|
-
def migration_version
|
66
|
-
major = ActiveRecord::VERSION::MAJOR
|
67
|
-
if major >= 5
|
68
|
-
"[#{major}.#{ActiveRecord::VERSION::MINOR}]"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def mysql?
|
73
|
-
MYSQL_ADAPTERS.include?(ActiveRecord::Base.connection.class.name)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Even modern versions of MySQL still use `latin1` as the default character
|
77
|
-
# encoding. Many users are not aware of this, and run into trouble when they
|
78
|
-
# try to use PaperTrail in apps that otherwise tend to use UTF-8. Postgres, by
|
79
|
-
# comparison, uses UTF-8 except in the unusual case where the OS is configured
|
80
|
-
# with a custom locale.
|
81
|
-
#
|
82
|
-
# - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
|
83
|
-
# - http://www.postgresql.org/docs/9.4/static/multibyte.html
|
84
|
-
#
|
85
|
-
# Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
|
86
|
-
# to be fixed later by introducing a new charset, `utf8mb4`.
|
87
|
-
#
|
88
|
-
# - https://mathiasbynens.be/notes/mysql-utf8mb4
|
89
|
-
# - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
|
90
|
-
#
|
91
|
-
def versions_table_options
|
92
|
-
if mysql?
|
93
|
-
', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
94
|
-
else
|
95
|
-
""
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|