paper_trail 10.2.0 → 11.1.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/Gemfile +4 -0
- data/LICENSE +20 -0
- 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 +5 -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 +62 -30
- data/lib/paper_trail/events/create.rb +1 -0
- data/lib/paper_trail/events/update.rb +1 -0
- data/lib/paper_trail/frameworks/rails/controller.rb +1 -3
- data/lib/paper_trail/has_paper_trail.rb +1 -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
- data/paper_trail.gemspec +69 -0
- metadata +71 -41
- 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: c312ab701b8ab7b26df37957b03b9f01b6ce6cbe0e5e25d4c478258f5bec1d6f
|
4
|
+
data.tar.gz: 62e94f6c2fa657d4c24f0fe6ecdf5abf3c8ae785e7b25f88611fc18e6d4324a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b59d91302dde736a2476240eadb9306938db2521594cf55de792c9678324762c4a1ffedfa5a53c3fc0fd9e37a87bc1b6ea4e74e68535b80fe70342d48a221532
|
7
|
+
data.tar.gz: b4aba2c107556fd6fbf758d9373f147c9b9bb0d1e6059bb3e2ad28b77906e980c559bd3c888fe9734665182cbe294bf470e77527a5536e202b8f32a5b7993516
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Andy Stewart, AirBlade Software Ltd.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -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"
|
@@ -145,3 +146,7 @@ if defined?(::Rails)
|
|
145
146
|
else
|
146
147
|
require "paper_trail/frameworks/active_record"
|
147
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.2" # 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,13 +117,18 @@ module PaperTrail
|
|
102
117
|
ignore << attr if condition.respond_to?(:call) && condition.call(@record)
|
103
118
|
}
|
104
119
|
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# @api private
|
123
|
+
def changed_and_not_ignored
|
105
124
|
skip = @record.paper_trail_options[:skip]
|
106
|
-
(changed_in_latest_version -
|
125
|
+
(changed_in_latest_version - calculated_ignored_array) - skip
|
107
126
|
end
|
108
127
|
|
109
128
|
# @api private
|
110
129
|
def changed_in_latest_version
|
111
|
-
|
130
|
+
# Memoized to reduce memory usage
|
131
|
+
@changed_in_latest_version ||= changes_in_latest_version.keys
|
112
132
|
end
|
113
133
|
|
114
134
|
# Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
|
@@ -116,10 +136,13 @@ module PaperTrail
|
|
116
136
|
#
|
117
137
|
# @api private
|
118
138
|
def changes_in_latest_version
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
123
146
|
end
|
124
147
|
end
|
125
148
|
|
@@ -129,7 +152,7 @@ module PaperTrail
|
|
129
152
|
#
|
130
153
|
# @api private
|
131
154
|
def ignored_attr_has_changed?
|
132
|
-
ignored =
|
155
|
+
ignored = calculated_ignored_array + @record.paper_trail_options[:skip]
|
133
156
|
ignored.any? && (changed_in_latest_version & ignored).any?
|
134
157
|
end
|
135
158
|
|
@@ -200,16 +223,19 @@ module PaperTrail
|
|
200
223
|
|
201
224
|
# @api private
|
202
225
|
def notably_changed
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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)
|
211
238
|
end
|
212
|
-
only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
|
213
239
|
end
|
214
240
|
|
215
241
|
# Returns hash of attributes (with appropriate attributes serialized),
|
@@ -217,8 +243,7 @@ module PaperTrail
|
|
217
243
|
#
|
218
244
|
# @api private
|
219
245
|
def object_attrs_for_paper_trail(is_touch)
|
220
|
-
attrs =
|
221
|
-
except(*@record.paper_trail_options[:skip])
|
246
|
+
attrs = nonskipped_attributes_before_change(is_touch)
|
222
247
|
AttributeSerializers::ObjectAttribute.new(@record.class).serialize(attrs)
|
223
248
|
attrs
|
224
249
|
end
|
@@ -226,8 +251,7 @@ module PaperTrail
|
|
226
251
|
# @api private
|
227
252
|
def prepare_object_changes(changes)
|
228
253
|
changes = serialize_object_changes(changes)
|
229
|
-
|
230
|
-
changes
|
254
|
+
recordable_object_changes(changes)
|
231
255
|
end
|
232
256
|
|
233
257
|
# Returns an object which can be assigned to the `object_changes`
|
@@ -237,9 +261,14 @@ module PaperTrail
|
|
237
261
|
# serialization here, using `PaperTrail.serializer`.
|
238
262
|
#
|
239
263
|
# @api private
|
264
|
+
# @param changes HashWithIndifferentAccess
|
240
265
|
def recordable_object_changes(changes)
|
241
266
|
if PaperTrail.config.object_changes_adapter&.respond_to?(:diff)
|
242
|
-
|
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)
|
243
272
|
end
|
244
273
|
|
245
274
|
if @record.class.paper_trail.version_class.object_changes_col_is_json?
|
@@ -284,7 +313,10 @@ module PaperTrail
|
|
284
313
|
AttributeSerializers::ObjectChangesAttribute.
|
285
314
|
new(@record.class).
|
286
315
|
serialize(changes)
|
287
|
-
|
316
|
+
|
317
|
+
# We'd like to convert this `HashWithIndifferentAccess` to a plain
|
318
|
+
# `Hash`, but we don't, to save memory.
|
319
|
+
changes
|
288
320
|
end
|
289
321
|
end
|
290
322
|
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
|
@@ -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 = 1
|
12
12
|
TINY = 0
|
13
13
|
|
14
14
|
# Set PRE to nil unless it's a pre-release (beta, rc, etc.)
|
data/paper_trail.gemspec
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("lib", __dir__)
|
4
|
+
require "paper_trail/compatibility"
|
5
|
+
require "paper_trail/version_number"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "paper_trail"
|
9
|
+
s.version = PaperTrail::VERSION::STRING
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.summary = "Track changes to your models."
|
12
|
+
s.description = <<-EOS
|
13
|
+
Track changes to your models, for auditing or versioning. See how a model looked
|
14
|
+
at any stage in its lifecycle, revert it to any version, or restore it after it
|
15
|
+
has been destroyed.
|
16
|
+
EOS
|
17
|
+
s.homepage = "https://github.com/paper-trail-gem/paper_trail"
|
18
|
+
s.authors = ["Andy Stewart", "Ben Atkins", "Jared Beck"]
|
19
|
+
s.email = "jared@jaredbeck.com"
|
20
|
+
s.license = "MIT"
|
21
|
+
|
22
|
+
s.files = `git ls-files -z`.split("\x0").select { |f|
|
23
|
+
f.match(%r{^(Gemfile|LICENSE|lib/|paper_trail.gemspec)})
|
24
|
+
}
|
25
|
+
s.executables = []
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
|
28
|
+
s.required_rubygems_version = ">= 1.3.6"
|
29
|
+
|
30
|
+
# Ruby 2.4 reaches EoL at the end of March of 2020
|
31
|
+
# https://www.ruby-lang.org/en/news/2019/10/02/ruby-2-4-9-released/
|
32
|
+
s.required_ruby_version = ">= 2.4.0"
|
33
|
+
|
34
|
+
# We no longer specify a maximum activerecord version.
|
35
|
+
# See discussion in paper_trail/compatibility.rb
|
36
|
+
s.add_dependency "activerecord", ::PaperTrail::Compatibility::ACTIVERECORD_GTE
|
37
|
+
s.add_dependency "request_store", "~> 1.1"
|
38
|
+
|
39
|
+
s.add_development_dependency "appraisal", "~> 2.2"
|
40
|
+
s.add_development_dependency "byebug", "~> 11.0"
|
41
|
+
s.add_development_dependency "ffaker", "~> 2.11"
|
42
|
+
s.add_development_dependency "generator_spec", "~> 0.9.4"
|
43
|
+
s.add_development_dependency "memory_profiler", "~> 0.9.14"
|
44
|
+
|
45
|
+
# For `spec/dummy_app`. Technically, we only need `actionpack` (as of 2020).
|
46
|
+
# However, that might change in the future, and the advantages of specifying a
|
47
|
+
# subset (e.g. actionpack only) are unclear.
|
48
|
+
s.add_development_dependency "rails", ::PaperTrail::Compatibility::ACTIVERECORD_GTE
|
49
|
+
|
50
|
+
s.add_development_dependency "rake", "~> 13.0"
|
51
|
+
s.add_development_dependency "rspec-rails", "~> 4.0"
|
52
|
+
s.add_development_dependency "rubocop", "~> 0.89.1"
|
53
|
+
s.add_development_dependency "rubocop-performance", "~> 1.7.1"
|
54
|
+
s.add_development_dependency "rubocop-rspec", "~> 1.42.0"
|
55
|
+
|
56
|
+
# ## Database Adapters
|
57
|
+
#
|
58
|
+
# The dependencies here must match the `gem` call at the top of their
|
59
|
+
# adapters, eg. `active_record/connection_adapters/mysql2_adapter.rb`,
|
60
|
+
# assuming said call is consistent for all versions of rails we test against
|
61
|
+
# (see `Appraisals`).
|
62
|
+
#
|
63
|
+
# Currently, all versions of rails we test against are consistent. In the past,
|
64
|
+
# when we tested against rails 4.2, we had to specify database adapters in
|
65
|
+
# `Appraisals`.
|
66
|
+
s.add_development_dependency "mysql2", "~> 0.5"
|
67
|
+
s.add_development_dependency "pg", ">= 0.18", "< 2.0"
|
68
|
+
s.add_development_dependency "sqlite3", "~> 1.4"
|
69
|
+
end
|
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.1.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-12-16 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.1'
|
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.1'
|
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,151 @@ 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: rails
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '5.2'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '5.2'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: rake
|
121
129
|
requirement: !ruby/object:Gem::Requirement
|
122
130
|
requirements:
|
123
131
|
- - "~>"
|
124
132
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
133
|
+
version: '13.0'
|
126
134
|
type: :development
|
127
135
|
prerelease: false
|
128
136
|
version_requirements: !ruby/object:Gem::Requirement
|
129
137
|
requirements:
|
130
138
|
- - "~>"
|
131
139
|
- !ruby/object:Gem::Version
|
132
|
-
version:
|
140
|
+
version: '13.0'
|
133
141
|
- !ruby/object:Gem::Dependency
|
134
|
-
name:
|
142
|
+
name: rspec-rails
|
135
143
|
requirement: !ruby/object:Gem::Requirement
|
136
144
|
requirements:
|
137
145
|
- - "~>"
|
138
146
|
- !ruby/object:Gem::Version
|
139
|
-
version: '
|
147
|
+
version: '4.0'
|
140
148
|
type: :development
|
141
149
|
prerelease: false
|
142
150
|
version_requirements: !ruby/object:Gem::Requirement
|
143
151
|
requirements:
|
144
152
|
- - "~>"
|
145
153
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
154
|
+
version: '4.0'
|
147
155
|
- !ruby/object:Gem::Dependency
|
148
|
-
name:
|
156
|
+
name: rubocop
|
149
157
|
requirement: !ruby/object:Gem::Requirement
|
150
158
|
requirements:
|
151
159
|
- - "~>"
|
152
160
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
161
|
+
version: 0.89.1
|
154
162
|
type: :development
|
155
163
|
prerelease: false
|
156
164
|
version_requirements: !ruby/object:Gem::Requirement
|
157
165
|
requirements:
|
158
166
|
- - "~>"
|
159
167
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
168
|
+
version: 0.89.1
|
161
169
|
- !ruby/object:Gem::Dependency
|
162
|
-
name:
|
170
|
+
name: rubocop-performance
|
163
171
|
requirement: !ruby/object:Gem::Requirement
|
164
172
|
requirements:
|
165
173
|
- - "~>"
|
166
174
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
175
|
+
version: 1.7.1
|
168
176
|
type: :development
|
169
177
|
prerelease: false
|
170
178
|
version_requirements: !ruby/object:Gem::Requirement
|
171
179
|
requirements:
|
172
180
|
- - "~>"
|
173
181
|
- !ruby/object:Gem::Version
|
174
|
-
version:
|
182
|
+
version: 1.7.1
|
175
183
|
- !ruby/object:Gem::Dependency
|
176
|
-
name: rubocop
|
184
|
+
name: rubocop-rspec
|
177
185
|
requirement: !ruby/object:Gem::Requirement
|
178
186
|
requirements:
|
179
187
|
- - "~>"
|
180
188
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
189
|
+
version: 1.42.0
|
182
190
|
type: :development
|
183
191
|
prerelease: false
|
184
192
|
version_requirements: !ruby/object:Gem::Requirement
|
185
193
|
requirements:
|
186
194
|
- - "~>"
|
187
195
|
- !ruby/object:Gem::Version
|
188
|
-
version:
|
196
|
+
version: 1.42.0
|
189
197
|
- !ruby/object:Gem::Dependency
|
190
|
-
name:
|
198
|
+
name: mysql2
|
191
199
|
requirement: !ruby/object:Gem::Requirement
|
192
200
|
requirements:
|
193
201
|
- - "~>"
|
194
202
|
- !ruby/object:Gem::Version
|
195
|
-
version:
|
203
|
+
version: '0.5'
|
196
204
|
type: :development
|
197
205
|
prerelease: false
|
198
206
|
version_requirements: !ruby/object:Gem::Requirement
|
199
207
|
requirements:
|
200
208
|
- - "~>"
|
201
209
|
- !ruby/object:Gem::Version
|
202
|
-
version:
|
210
|
+
version: '0.5'
|
211
|
+
- !ruby/object:Gem::Dependency
|
212
|
+
name: pg
|
213
|
+
requirement: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '0.18'
|
218
|
+
- - "<"
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '2.0'
|
221
|
+
type: :development
|
222
|
+
prerelease: false
|
223
|
+
version_requirements: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '0.18'
|
228
|
+
- - "<"
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '2.0'
|
203
231
|
- !ruby/object:Gem::Dependency
|
204
232
|
name: sqlite3
|
205
233
|
requirement: !ruby/object:Gem::Requirement
|
206
234
|
requirements:
|
207
235
|
- - "~>"
|
208
236
|
- !ruby/object:Gem::Version
|
209
|
-
version: '1.
|
237
|
+
version: '1.4'
|
210
238
|
type: :development
|
211
239
|
prerelease: false
|
212
240
|
version_requirements: !ruby/object:Gem::Requirement
|
213
241
|
requirements:
|
214
242
|
- - "~>"
|
215
243
|
- !ruby/object:Gem::Version
|
216
|
-
version: '1.
|
244
|
+
version: '1.4'
|
217
245
|
description: |
|
218
246
|
Track changes to your models, for auditing or versioning. See how a model looked
|
219
247
|
at any stage in its lifecycle, revert it to any version, or restore it after it
|
@@ -223,11 +251,12 @@ executables: []
|
|
223
251
|
extensions: []
|
224
252
|
extra_rdoc_files: []
|
225
253
|
files:
|
254
|
+
- Gemfile
|
255
|
+
- LICENSE
|
226
256
|
- lib/generators/paper_trail/install/USAGE
|
227
257
|
- lib/generators/paper_trail/install/install_generator.rb
|
228
258
|
- lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb
|
229
259
|
- lib/generators/paper_trail/install/templates/create_versions.rb.erb
|
230
|
-
- lib/generators/paper_trail/install_generator.rb
|
231
260
|
- lib/generators/paper_trail/migration_generator.rb
|
232
261
|
- lib/generators/paper_trail/update_item_subtype/USAGE
|
233
262
|
- lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb
|
@@ -239,6 +268,7 @@ files:
|
|
239
268
|
- lib/paper_trail/attribute_serializers/object_attribute.rb
|
240
269
|
- lib/paper_trail/attribute_serializers/object_changes_attribute.rb
|
241
270
|
- lib/paper_trail/cleaner.rb
|
271
|
+
- lib/paper_trail/compatibility.rb
|
242
272
|
- lib/paper_trail/config.rb
|
243
273
|
- lib/paper_trail/events/base.rb
|
244
274
|
- lib/paper_trail/events/create.rb
|
@@ -265,6 +295,7 @@ files:
|
|
265
295
|
- lib/paper_trail/type_serializers/postgres_array_serializer.rb
|
266
296
|
- lib/paper_trail/version_concern.rb
|
267
297
|
- lib/paper_trail/version_number.rb
|
298
|
+
- paper_trail.gemspec
|
268
299
|
homepage: https://github.com/paper-trail-gem/paper_trail
|
269
300
|
licenses:
|
270
301
|
- MIT
|
@@ -277,15 +308,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
277
308
|
requirements:
|
278
309
|
- - ">="
|
279
310
|
- !ruby/object:Gem::Version
|
280
|
-
version: 2.
|
311
|
+
version: 2.4.0
|
281
312
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
282
313
|
requirements:
|
283
314
|
- - ">="
|
284
315
|
- !ruby/object:Gem::Version
|
285
316
|
version: 1.3.6
|
286
317
|
requirements: []
|
287
|
-
|
288
|
-
rubygems_version: 2.5.2.3
|
318
|
+
rubygems_version: 3.0.3
|
289
319
|
signing_key:
|
290
320
|
specification_version: 4
|
291
321
|
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
|