paper_trail 10.1.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|