paper_trail 14.0.0 → 16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8442e35802b28551f1ab7275a3e6a14fed55edcba6c1aa9beeff5e65a2a92626
4
- data.tar.gz: 3014b42bde912764165fc14a8974b14cc16cdf9643a1f8c9af23083a4479c50d
3
+ metadata.gz: 0a834abeee917bf2c090c6ef7f6da09437b12aff37c0bcf94f8ff88ca1434203
4
+ data.tar.gz: 6fdf12293b452fe7dcce563e5f4547d9ae0081399b5cf6885308ea451f29b6ed
5
5
  SHA512:
6
- metadata.gz: 57f5248c0e5d448dd20e88530f06e74fddcf641c1f13f6b9d7e33ef54305168e5f0bb34322c4db7b0b9b5fe9c7c988116a72b17605500061c43ffc67f0519f9d
7
- data.tar.gz: 6d84306539336b774e8b9cd5f1f2412fa59752c6ceb3033af6be9652045e19d5e11f95555f47cdde3742bf18e6165addcde3e3d5a5b2c7b8df7ecc908b16d6eb
6
+ metadata.gz: 82fbf4c2ec002d52ee5b4023cbef6a16ef5530e04852a4a3d65fb38b468395016db6230e2f89c199a13549bb9da98a1f53a2b29148a94455bc6470ea7912dcaf
7
+ data.tar.gz: 9944b2f2e66c9fdc10fe2ecf6f9c560b73da21e49d98504f826158e92faeef5b9cad7a4a1cb1f033217c1f66e230d4c8e2df034c4751298a93df5bf946ef108e
@@ -35,7 +35,8 @@ module PaperTrail
35
35
  "create_versions",
36
36
  item_type_options: item_type_options,
37
37
  versions_table_options: versions_table_options,
38
- item_id_type_options: item_id_type_options
38
+ item_id_type_options: item_id_type_options,
39
+ version_table_primary_key_type: version_table_primary_key_type
39
40
  )
40
41
  if options.with_changes?
41
42
  add_paper_trail_migration("add_object_changes_to_versions")
@@ -49,6 +50,15 @@ module PaperTrail
49
50
  options.uuid? ? "string" : "bigint"
50
51
  end
51
52
 
53
+ # To use uuid for version table primary key
54
+ def version_table_primary_key_type
55
+ if options.uuid?
56
+ ", id: :uuid"
57
+ else
58
+ ""
59
+ end
60
+ end
61
+
52
62
  # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
53
63
  # See https://github.com/paper-trail-gem/paper_trail/issues/651
54
64
  def item_type_options
@@ -9,12 +9,10 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
9
9
  TEXT_BYTES = 1_073_741_823
10
10
 
11
11
  def change
12
- create_table :versions<%= versions_table_options %> do |t|
13
- t.string :item_type<%= item_type_options %>
14
- t.<%= item_id_type_options %> :item_id, null: false
15
- t.string :event, null: false
12
+ create_table :versions<%= versions_table_options %><%= version_table_primary_key_type %> do |t|
13
+ # Consider using bigint type for performance if you are going to store only numeric ids.
14
+ # t.bigint :whodunnit
16
15
  t.string :whodunnit
17
- t.text :object, limit: TEXT_BYTES
18
16
 
19
17
  # Known issue in MySQL: fractional second precision
20
18
  # -------------------------------------------------
@@ -32,6 +30,11 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
32
30
  # MySQL users should use the following line for `created_at`
33
31
  # t.datetime :created_at, limit: 6
34
32
  t.datetime :created_at
33
+
34
+ t.<%= item_id_type_options %> :item_id, null: false
35
+ t.string :item_type<%= item_type_options %>
36
+ t.string :event, null: false
37
+ t.text :object, limit: TEXT_BYTES
35
38
  end
36
39
  add_index :versions, %i[item_type item_id]
37
40
  end
@@ -32,7 +32,7 @@ module PaperTrail
32
32
  if defined_enums[attr] && val.is_a?(::String)
33
33
  # Because PT 4 used to save the string version of enums to `object_changes`
34
34
  val
35
- elsif rails_gte_7_0? && val.is_a?(ActiveRecord::Type::Time::Value)
35
+ elsif PaperTrail.active_record_gte_7_0? && val.is_a?(ActiveRecord::Type::Time::Value)
36
36
  # Because Rails 7 time attribute throws a delegation error when you deserialize
37
37
  # it with the factory.
38
38
  # See ActiveRecord::Type::Time::Value crashes when loaded from YAML on rails 7.0
@@ -43,10 +43,6 @@ module PaperTrail
43
43
  end
44
44
  end
45
45
 
46
- def rails_gte_7_0?
47
- ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0")
48
- end
49
-
50
46
  def serialize(attr, val)
51
47
  AttributeSerializerFactory.for(@klass, attr).serialize(val)
52
48
  end
@@ -8,6 +8,12 @@ module PaperTrail
8
8
  class ObjectAttribute
9
9
  def initialize(model_class)
10
10
  @model_class = model_class
11
+
12
+ # ActiveRecord since 7.0 has a built-in encryption mechanism
13
+ @encrypted_attributes =
14
+ if PaperTrail.active_record_gte_7_0?
15
+ @model_class.encrypted_attributes&.map(&:to_s)
16
+ end
11
17
  end
12
18
 
13
19
  def serialize(attributes)
@@ -23,14 +29,18 @@ module PaperTrail
23
29
  # Modifies `attributes` in place.
24
30
  # TODO: Return a new hash instead.
25
31
  def alter(attributes, serialization_method)
26
- # Don't serialize before values before inserting into columns of type
32
+ # Don't serialize non-encrypted before values before inserting into columns of type
27
33
  # `JSON` on `PostgreSQL` databases.
28
- return attributes if object_col_is_json?
34
+ attributes_to_serialize =
35
+ object_col_is_json? ? attributes.slice(*@encrypted_attributes) : attributes
36
+ return attributes if attributes_to_serialize.blank?
29
37
 
30
38
  serializer = CastAttributeSerializer.new(@model_class)
31
- attributes.each do |key, value|
39
+ attributes_to_serialize.each do |key, value|
32
40
  attributes[key] = serializer.send(serialization_method, key, value)
33
41
  end
42
+
43
+ attributes
34
44
  end
35
45
 
36
46
  def object_col_is_json?
@@ -8,6 +8,12 @@ module PaperTrail
8
8
  class ObjectChangesAttribute
9
9
  def initialize(item_class)
10
10
  @item_class = item_class
11
+
12
+ # ActiveRecord since 7.0 has a built-in encryption mechanism
13
+ @encrypted_attributes =
14
+ if PaperTrail.active_record_gte_7_0?
15
+ @item_class.encrypted_attributes&.map(&:to_s)
16
+ end
11
17
  end
12
18
 
13
19
  def serialize(changes)
@@ -23,17 +29,21 @@ module PaperTrail
23
29
  # Modifies `changes` in place.
24
30
  # TODO: Return a new hash instead.
25
31
  def alter(changes, serialization_method)
26
- # Don't serialize before values before inserting into columns of type
32
+ # Don't serialize non-encrypted before values before inserting into columns of type
27
33
  # `JSON` on `PostgreSQL` databases.
28
- return changes if object_changes_col_is_json?
34
+ changes_to_serialize =
35
+ object_changes_col_is_json? ? changes.slice(*@encrypted_attributes) : changes.clone
36
+ return changes if changes_to_serialize.blank?
29
37
 
30
38
  serializer = CastAttributeSerializer.new(@item_class)
31
- changes.clone.each do |key, change|
39
+ changes_to_serialize.each do |key, change|
32
40
  # `change` is an Array with two elements, representing before and after.
33
41
  changes[key] = Array(change).map do |value|
34
42
  serializer.send(serialization_method, key, value)
35
43
  end
36
44
  end
45
+
46
+ changes
37
47
  end
38
48
 
39
49
  def object_changes_col_is_json?
@@ -17,8 +17,8 @@ module PaperTrail
17
17
  # newer rails versions. Most PT users should avoid incompatible rails
18
18
  # versions.
19
19
  module Compatibility
20
- ACTIVERECORD_GTE = ">= 6.0" # enforced in gemspec
21
- ACTIVERECORD_LT = "< 7.1" # not enforced in gemspec
20
+ ACTIVERECORD_GTE = ">= 6.1" # enforced in gemspec
21
+ ACTIVERECORD_LT = "< 8.1" # not enforced in gemspec
22
22
 
23
23
  E_INCOMPATIBLE_AR = <<-EOS
24
24
  PaperTrail %s is not compatible with ActiveRecord %s. We allow PT
@@ -14,7 +14,8 @@ module PaperTrail
14
14
  :object_changes_adapter,
15
15
  :serializer,
16
16
  :version_limit,
17
- :has_paper_trail_defaults
17
+ :has_paper_trail_defaults,
18
+ :version_error_behavior
18
19
  )
19
20
 
20
21
  def initialize
@@ -25,6 +26,7 @@ module PaperTrail
25
26
  # Variables which affect all threads, whose access is *not* synchronized.
26
27
  @serializer = PaperTrail::Serializers::YAML
27
28
  @has_paper_trail_defaults = {}
29
+ @version_error_behavior = :legacy
28
30
  end
29
31
 
30
32
  # Indicates whether PaperTrail is on or off. Default: true.
@@ -10,7 +10,7 @@ module PaperTrail
10
10
  # We specify `before: "load_config_initializers"` to ensure that the PT
11
11
  # initializer happens before "app initializers" (those defined in
12
12
  # the app's `config/initalizers`).
13
- initializer "paper_trail", before: "load_config_initializers" do
13
+ initializer "paper_trail", before: "load_config_initializers" do |app|
14
14
  # `on_load` is a "lazy load hook". It "declares a block that will be
15
15
  # executed when a Rails component is fully loaded". (See
16
16
  # `active_support/lazy_load_hooks.rb`)
@@ -25,6 +25,10 @@ module PaperTrail
25
25
  ActiveSupport.on_load(:active_record) do
26
26
  require "paper_trail/frameworks/active_record"
27
27
  end
28
+
29
+ if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("7.1")
30
+ app.deprecators[:paper_trail] = PaperTrail.deprecator
31
+ end
28
32
  end
29
33
  end
30
34
  end
@@ -53,6 +53,10 @@ module PaperTrail
53
53
  # - A Hash - options passed to `has_many`, plus `name:` and `scope:`.
54
54
  # - :version - The name to use for the method which returns the version
55
55
  # the instance was reified from. Default is `:version`.
56
+ # - :synchronize_version_creation_timestamp - By default, paper trail
57
+ # sets the `created_at` field for a new Version equal to the `updated_at`
58
+ # column of the model being updated. If you instead want `created_at` to
59
+ # populate with the current timestamp, set this option to `false`.
56
60
  #
57
61
  # Plugins like the experimental `paper_trail-association_tracking` gem
58
62
  # may accept additional options.
@@ -64,6 +68,8 @@ module PaperTrail
64
68
  #
65
69
  # @api public
66
70
  def has_paper_trail(options = {})
71
+ raise Error, "has_paper_trail must be called only once" if self < InstanceMethods
72
+
67
73
  defaults = PaperTrail.config.has_paper_trail_defaults
68
74
  paper_trail.setup(defaults.merge(options))
69
75
  end
@@ -82,8 +82,9 @@ module PaperTrail
82
82
 
83
83
  # Adds a callback that records a version after a "touch" event.
84
84
  #
85
- # Rails < 6.0 has a bug where dirty-tracking does not occur during
86
- # a `touch`. (https://github.com/rails/rails/issues/33429) See also:
85
+ # Rails < 6.0 (no longer supported by PT) had a bug where dirty-tracking
86
+ # did not occur during a `touch`.
87
+ # (https://github.com/rails/rails/issues/33429) See also:
87
88
  # https://github.com/paper-trail-gem/paper_trail/issues/1121
88
89
  # https://github.com/paper-trail-gem/paper_trail/issues/1161
89
90
  # https://github.com/paper-trail-gem/paper_trail/pull/1285
@@ -155,7 +156,7 @@ module PaperTrail
155
156
  # @api private - `version_class_name`
156
157
  @model_class.class_attribute :version_class_name
157
158
  if options[:class_name]
158
- ::ActiveSupport::Deprecation.warn(
159
+ PaperTrail.deprecator.warn(
159
160
  format(
160
161
  DPR_CLASS_NAME_OPTION,
161
162
  class_name: options[:class_name].inspect
@@ -191,7 +192,7 @@ module PaperTrail
191
192
  def ensure_versions_option_is_hash(options)
192
193
  unless options[:versions].is_a?(Hash)
193
194
  if options[:versions]
194
- ::ActiveSupport::Deprecation.warn(
195
+ PaperTrail.deprecator.warn(
195
196
  format(
196
197
  DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION,
197
198
  versions_name: options[:versions].inspect
@@ -64,6 +64,8 @@ module PaperTrail
64
64
  # of versions_assoc.build?, the association cache is unaware. So, we
65
65
  # invalidate the `versions` association cache with `reset`.
66
66
  versions.reset
67
+ rescue StandardError => e
68
+ handle_version_errors e, version, :create
67
69
  end
68
70
  end
69
71
 
@@ -81,12 +83,13 @@ module PaperTrail
81
83
  # `data_for_destroy` but PT-AT still does.
82
84
  data = event.data.merge(data_for_destroy)
83
85
 
84
- version = @record.class.paper_trail.version_class.create(data)
85
- if version.errors.any?
86
- log_version_errors(version, :destroy)
87
- else
86
+ version = @record.class.paper_trail.version_class.new(data)
87
+ begin
88
+ version.save!
88
89
  assign_and_reset_version_association(version)
89
90
  version
91
+ rescue StandardError => e
92
+ handle_version_errors e, version, :destroy
90
93
  end
91
94
  end
92
95
 
@@ -108,14 +111,15 @@ module PaperTrail
108
111
  )
109
112
  return unless version
110
113
 
111
- if version.save
114
+ begin
115
+ version.save!
112
116
  # Because the version object was created using version_class.new instead
113
117
  # of versions_assoc.build?, the association cache is unaware. So, we
114
118
  # invalidate the `versions` association cache with `reset`.
115
119
  versions.reset
116
120
  version
117
- else
118
- log_version_errors(version, :update)
121
+ rescue StandardError => e
122
+ handle_version_errors e, version, :update
119
123
  end
120
124
  end
121
125
 
@@ -226,7 +230,8 @@ module PaperTrail
226
230
  # unnatural to tamper with creation timestamps in this way. But, this
227
231
  # feature has existed for a long time, almost a decade now, and some users
228
232
  # may rely on it now.
229
- if @record.respond_to?(:updated_at)
233
+ if @record.respond_to?(:updated_at) &&
234
+ @record.paper_trail_options[:synchronize_version_creation_timestamp] != false
230
235
  data[:created_at] = @record.updated_at
231
236
  end
232
237
 
@@ -285,6 +290,26 @@ module PaperTrail
285
290
  )
286
291
  end
287
292
 
293
+ # Centralized handler for version errors
294
+ # @api private
295
+ def handle_version_errors(e, version, action)
296
+ case PaperTrail.config.version_error_behavior
297
+ when :legacy
298
+ # legacy behavior was to raise on create and log on update/delete
299
+ if action == :create
300
+ raise e
301
+ else
302
+ log_version_errors(version, action)
303
+ end
304
+ when :log
305
+ log_version_errors(version, action)
306
+ when :exception
307
+ raise e
308
+ when :silent
309
+ # noop
310
+ end
311
+ end
312
+
288
313
  # @api private
289
314
  # @return - The created version object, so that plugins can use it, e.g.
290
315
  # paper_trail-association_tracking
@@ -297,11 +322,12 @@ module PaperTrail
297
322
  data.merge!(data_for_update_columns)
298
323
 
299
324
  versions_assoc = @record.send(@record.class.versions_association_name)
300
- version = versions_assoc.create(data)
301
- if version.errors.any?
302
- log_version_errors(version, :update)
303
- else
325
+ version = versions_assoc.new(data)
326
+ begin
327
+ version.save!
304
328
  version
329
+ rescue StandardError => e
330
+ handle_version_errors e, version, :update
305
331
  end
306
332
  end
307
333
 
@@ -7,7 +7,7 @@ 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 = 14
10
+ MAJOR = 16
11
11
  MINOR = 0
12
12
  TINY = 0
13
13
 
data/lib/paper_trail.rb CHANGED
@@ -114,6 +114,14 @@ module PaperTrail
114
114
  def version
115
115
  VERSION::STRING
116
116
  end
117
+
118
+ def active_record_gte_7_0?
119
+ @active_record_gte_7_0 ||= ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0")
120
+ end
121
+
122
+ def deprecator
123
+ @deprecator ||= ActiveSupport::Deprecation.new("16.0", "PaperTrail")
124
+ end
117
125
  end
118
126
  end
119
127
 
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: 14.0.0
4
+ version: 16.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: 2022-11-26 00:00:00.000000000 Z
13
+ date: 2024-11-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '6.0'
21
+ version: '6.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: '6.0'
28
+ version: '6.1'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: request_store
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -46,14 +46,14 @@ dependencies:
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: 2.4.1
49
+ version: '2.5'
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 2.4.1
56
+ version: '2.5'
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: byebug
59
59
  requirement: !ruby/object:Gem::Requirement
@@ -116,14 +116,14 @@ dependencies:
116
116
  requirements:
117
117
  - - ">="
118
118
  - !ruby/object:Gem::Version
119
- version: '6.0'
119
+ version: '6.1'
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
- version: '6.0'
126
+ version: '6.1'
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: rake
129
129
  requirement: !ruby/object:Gem::Requirement
@@ -144,14 +144,14 @@ dependencies:
144
144
  requirements:
145
145
  - - "~>"
146
146
  - !ruby/object:Gem::Version
147
- version: 5.0.2
147
+ version: 6.0.3
148
148
  type: :development
149
149
  prerelease: false
150
150
  version_requirements: !ruby/object:Gem::Requirement
151
151
  requirements:
152
152
  - - "~>"
153
153
  - !ruby/object:Gem::Version
154
- version: 5.0.2
154
+ version: 6.0.3
155
155
  - !ruby/object:Gem::Dependency
156
156
  name: rubocop
157
157
  requirement: !ruby/object:Gem::Requirement
@@ -351,7 +351,8 @@ files:
351
351
  homepage: https://github.com/paper-trail-gem/paper_trail
352
352
  licenses:
353
353
  - MIT
354
- metadata: {}
354
+ metadata:
355
+ changelog_uri: https://github.com/paper-trail-gem/paper_trail/blob/master/CHANGELOG.md
355
356
  post_install_message:
356
357
  rdoc_options: []
357
358
  require_paths:
@@ -360,14 +361,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
360
361
  requirements:
361
362
  - - ">="
362
363
  - !ruby/object:Gem::Version
363
- version: 2.7.0
364
+ version: 3.0.0
364
365
  required_rubygems_version: !ruby/object:Gem::Requirement
365
366
  requirements:
366
367
  - - ">="
367
368
  - !ruby/object:Gem::Version
368
369
  version: 1.3.6
369
370
  requirements: []
370
- rubygems_version: 3.3.7
371
+ rubygems_version: 3.4.19
371
372
  signing_key:
372
373
  specification_version: 4
373
374
  summary: Track changes to your models.