paper_trail 13.0.0 → 14.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: 776e912c4a06b036014b0301b30a1438974c00b8e63b6e0b78fbffe4dfca7824
4
- data.tar.gz: 691060404fbb2b4a3f66cf40c0b6187df7a9e8439854097ffbad433a7bdd6e75
3
+ metadata.gz: 8442e35802b28551f1ab7275a3e6a14fed55edcba6c1aa9beeff5e65a2a92626
4
+ data.tar.gz: 3014b42bde912764165fc14a8974b14cc16cdf9643a1f8c9af23083a4479c50d
5
5
  SHA512:
6
- metadata.gz: 7110ad57841a431cb50318a79db55ba65f279b839d72009dc2aef961a57c9b044d4d8d706062bbff4cea7fe9b7b2db0a3d83c966518eeffddd66cca6f9e63ae3
7
- data.tar.gz: d5ebe8b0a5f8dd4df889b1e6a71e81ab5fb1e752a034dd400decd5ef1bdb91441bce9b6061980db7d443796309995c745063175bf7cdb85819028f45fc20dc88
6
+ metadata.gz: 57f5248c0e5d448dd20e88530f06e74fddcf641c1f13f6b9d7e33ef54305168e5f0bb34322c4db7b0b9b5fe9c7c988116a72b17605500061c43ffc67f0519f9d
7
+ data.tar.gz: 6d84306539336b774e8b9cd5f1f2412fa59752c6ceb3033af6be9652045e19d5e11f95555f47cdde3742bf18e6165addcde3e3d5a5b2c7b8df7ecc908b16d6eb
@@ -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 PaperTrail::RAILS_GTE_7_0 && val.is_a?(ActiveRecord::Type::Time::Value)
35
+ elsif rails_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,6 +43,10 @@ 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
+
46
50
  def serialize(attr, val)
47
51
  AttributeSerializerFactory.for(@klass, attr).serialize(val)
48
52
  end
@@ -17,7 +17,7 @@ module PaperTrail
17
17
  # newer rails versions. Most PT users should avoid incompatible rails
18
18
  # versions.
19
19
  module Compatibility
20
- ACTIVERECORD_GTE = ">= 5.2" # enforced in gemspec
20
+ ACTIVERECORD_GTE = ">= 6.0" # enforced in gemspec
21
21
  ACTIVERECORD_LT = "< 7.1" # not enforced in gemspec
22
22
 
23
23
  E_INCOMPATIBLE_AR = <<-EOS
@@ -22,6 +22,19 @@ module PaperTrail
22
22
  #
23
23
  # @api private
24
24
  class Base
25
+ E_FORBIDDEN_METADATA_KEY = <<-EOS.squish
26
+ Forbidden metadata key: %s. As of PT 14, the following metadata keys are
27
+ forbidden: %s
28
+ EOS
29
+ FORBIDDEN_METADATA_KEYS = %i[
30
+ created_at
31
+ id
32
+ item_id
33
+ item_subtype
34
+ item_type
35
+ updated_at
36
+ ].freeze
37
+
25
38
  # @api private
26
39
  def initialize(record, in_after_callback)
27
40
  @record = record
@@ -44,6 +57,13 @@ module PaperTrail
44
57
 
45
58
  private
46
59
 
60
+ # @api private
61
+ def assert_metadatum_key_is_permitted(key)
62
+ return unless FORBIDDEN_METADATA_KEYS.include?(key.to_sym)
63
+ raise PaperTrail::InvalidOption,
64
+ format(E_FORBIDDEN_METADATA_KEY, key, FORBIDDEN_METADATA_KEYS)
65
+ end
66
+
47
67
  # Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
48
68
  # https://github.com/paper-trail-gem/paper_trail/pull/899
49
69
  #
@@ -175,7 +195,9 @@ module PaperTrail
175
195
  #
176
196
  # @api private
177
197
  def merge_metadata_from_controller_into(data)
178
- data.merge(PaperTrail.request.controller_info || {})
198
+ metadata = PaperTrail.request.controller_info || {}
199
+ metadata.keys.each { |k| assert_metadatum_key_is_permitted(k) }
200
+ data.merge(metadata)
179
201
  end
180
202
 
181
203
  # Updates `data` from the model's `meta` option.
@@ -183,6 +205,7 @@ module PaperTrail
183
205
  # @api private
184
206
  def merge_metadata_from_model_into(data)
185
207
  @record.paper_trail_options[:meta].each do |k, v|
208
+ assert_metadatum_key_is_permitted(k)
186
209
  data[k] = model_metadatum(v, data[:event])
187
210
  end
188
211
  end
@@ -221,7 +244,7 @@ module PaperTrail
221
244
  # @api private
222
245
  def notable_changes
223
246
  changes_in_latest_version.delete_if { |k, _v|
224
- !notably_changed.include?(k)
247
+ notably_changed.exclude?(k)
225
248
  }
226
249
  end
227
250
 
@@ -29,9 +29,6 @@ module PaperTrail
29
29
  event: @record.paper_trail_event || "update",
30
30
  whodunnit: PaperTrail.request.whodunnit
31
31
  }
32
- if @record.respond_to?(:updated_at)
33
- data[:created_at] = @record.updated_at
34
- end
35
32
  if record_object?
36
33
  data[:object] = recordable_object(@is_touch)
37
34
  end
@@ -93,7 +93,7 @@ module PaperTrail
93
93
  @model_class.after_touch { |r|
94
94
  if r.paper_trail.save_version?
95
95
  r.paper_trail.record_update(
96
- force: RAILS_LT_6_0,
96
+ force: false,
97
97
  in_after_callback: true,
98
98
  is_touch: true
99
99
  )
@@ -121,9 +121,6 @@ module PaperTrail
121
121
 
122
122
  private
123
123
 
124
- RAILS_LT_6_0 = ::ActiveRecord.gem_version < ::Gem::Version.new("6.0.0")
125
- private_constant :RAILS_LT_6_0
126
-
127
124
  # @api private
128
125
  def append_option_uniquely(option, value)
129
126
  collection = @model_class.paper_trail_options.fetch(option)
@@ -15,7 +15,7 @@ module PaperTrail
15
15
  @version_model_class = version_model_class
16
16
 
17
17
  # Currently, this `deep_dup` is necessary because the `jsonb` branch
18
- # modifies `@attributes`, and that would be a nasty suprise for
18
+ # modifies `@attributes`, and that would be a nasty surprise for
19
19
  # consumers of this class.
20
20
  # TODO: Stop modifying `@attributes`, then remove `deep_dup`.
21
21
  @attributes = attributes.deep_dup
@@ -25,14 +25,6 @@ module PaperTrail
25
25
  @record.send("#{@record.class.version_association_name}=", nil)
26
26
  end
27
27
 
28
- # Is PT enabled for this particular record?
29
- # @api private
30
- def enabled?
31
- PaperTrail.enabled? &&
32
- PaperTrail.request.enabled? &&
33
- PaperTrail.request.enabled_for_model?(@record.class)
34
- end
35
-
36
28
  # Returns true if this instance is the current, live one;
37
29
  # returns false if this instance came from a previous version.
38
30
  def live?
@@ -75,13 +67,6 @@ module PaperTrail
75
67
  end
76
68
  end
77
69
 
78
- # PT-AT extends this method to add its transaction id.
79
- #
80
- # @api private
81
- def data_for_create
82
- {}
83
- end
84
-
85
70
  # `recording_order` is "after" or "before". See ModelConfig#on_destroy.
86
71
  #
87
72
  # @api private
@@ -105,14 +90,12 @@ module PaperTrail
105
90
  end
106
91
  end
107
92
 
108
- # PT-AT extends this method to add its transaction id.
109
- #
110
- # @api private
111
- def data_for_destroy
112
- {}
113
- end
114
-
115
93
  # @api private
94
+ # @param force [boolean] Insert a `Version` even if `@record` has not
95
+ # `changed_notably?`.
96
+ # @param in_after_callback [boolean] True when called from an `after_update`
97
+ # or `after_touch` callback.
98
+ # @param is_touch [boolean] True when called from an `after_touch` callback.
116
99
  # @return - The created version object, so that plugins can use it, e.g.
117
100
  # paper_trail-association_tracking
118
101
  def record_update(force:, in_after_callback:, is_touch:)
@@ -136,40 +119,6 @@ module PaperTrail
136
119
  end
137
120
  end
138
121
 
139
- # PT-AT extends this method to add its transaction id.
140
- #
141
- # @api private
142
- def data_for_update
143
- {}
144
- end
145
-
146
- # @api private
147
- # @return - The created version object, so that plugins can use it, e.g.
148
- # paper_trail-association_tracking
149
- def record_update_columns(changes)
150
- return unless enabled?
151
- event = Events::Update.new(@record, false, false, changes)
152
-
153
- # Merge data from `Event` with data from PT-AT. We no longer use
154
- # `data_for_update_columns` but PT-AT still does.
155
- data = event.data.merge(data_for_update_columns)
156
-
157
- versions_assoc = @record.send(@record.class.versions_association_name)
158
- version = versions_assoc.create(data)
159
- if version.errors.any?
160
- log_version_errors(version, :update)
161
- else
162
- version
163
- end
164
- end
165
-
166
- # PT-AT extends this method to add its transaction id.
167
- #
168
- # @api private
169
- def data_for_update_columns
170
- {}
171
- end
172
-
173
122
  # Invoked via callback when a user attempts to persist a reified
174
123
  # `Version`.
175
124
  def reset_timestamp_attrs_for_update_if_needed
@@ -269,11 +218,22 @@ module PaperTrail
269
218
  def build_version_on_update(force:, in_after_callback:, is_touch:)
270
219
  event = Events::Update.new(@record, in_after_callback, is_touch, nil)
271
220
  return unless force || event.changed_notably?
221
+ data = event.data
222
+
223
+ # Copy the (recently set) `updated_at` from the record to the `created_at`
224
+ # of the `Version`. Without this feature, these two timestamps would
225
+ # differ by a few milliseconds. To some people, it seems a little
226
+ # unnatural to tamper with creation timestamps in this way. But, this
227
+ # feature has existed for a long time, almost a decade now, and some users
228
+ # may rely on it now.
229
+ if @record.respond_to?(:updated_at)
230
+ data[:created_at] = @record.updated_at
231
+ end
272
232
 
273
233
  # Merge data from `Event` with data from PT-AT. We no longer use
274
234
  # `data_for_update` but PT-AT still does. To save memory, we use `merge!`
275
235
  # instead of `merge`.
276
- data = event.data.merge!(data_for_update)
236
+ data.merge!(data_for_update)
277
237
 
278
238
  # Using `version_class.new` reduces memory usage compared to
279
239
  # `versions_assoc.build`. It's a trade-off though. We have to clear
@@ -282,6 +242,42 @@ module PaperTrail
282
242
  @record.class.paper_trail.version_class.new(data)
283
243
  end
284
244
 
245
+ # PT-AT extends this method to add its transaction id.
246
+ #
247
+ # @api public
248
+ def data_for_create
249
+ {}
250
+ end
251
+
252
+ # PT-AT extends this method to add its transaction id.
253
+ #
254
+ # @api public
255
+ def data_for_destroy
256
+ {}
257
+ end
258
+
259
+ # PT-AT extends this method to add its transaction id.
260
+ #
261
+ # @api public
262
+ def data_for_update
263
+ {}
264
+ end
265
+
266
+ # PT-AT extends this method to add its transaction id.
267
+ #
268
+ # @api public
269
+ def data_for_update_columns
270
+ {}
271
+ end
272
+
273
+ # Is PT enabled for this particular record?
274
+ # @api private
275
+ def enabled?
276
+ PaperTrail.enabled? &&
277
+ PaperTrail.request.enabled? &&
278
+ PaperTrail.request.enabled_for_model?(@record.class)
279
+ end
280
+
285
281
  def log_version_errors(version, action)
286
282
  version.logger&.warn(
287
283
  "Unable to create version for #{action} of #{@record.class.name}" \
@@ -289,6 +285,26 @@ module PaperTrail
289
285
  )
290
286
  end
291
287
 
288
+ # @api private
289
+ # @return - The created version object, so that plugins can use it, e.g.
290
+ # paper_trail-association_tracking
291
+ def record_update_columns(changes)
292
+ return unless enabled?
293
+ data = Events::Update.new(@record, false, false, changes).data
294
+
295
+ # Merge data from `Event` with data from PT-AT. We no longer use
296
+ # `data_for_update_columns` but PT-AT still does.
297
+ data.merge!(data_for_update_columns)
298
+
299
+ 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
304
+ version
305
+ end
306
+ end
307
+
292
308
  def version
293
309
  @record.public_send(@record.class.version_association_name)
294
310
  end
@@ -75,28 +75,6 @@ module PaperTrail
75
75
  !!store.fetch(:"enabled_for_#{model}", true)
76
76
  end
77
77
 
78
- # @api private
79
- def merge(options)
80
- options.to_h.each do |k, v|
81
- store[k] = v
82
- end
83
- end
84
-
85
- # @api private
86
- def set(options)
87
- store.clear
88
- merge(options)
89
- end
90
-
91
- # Returns a deep copy of the internal hash from our RequestStore. Keys are
92
- # all symbols. Values are mostly primitives, but whodunnit can be a Proc.
93
- # We cannot use Marshal.dump here because it doesn't support Proc. It is
94
- # unclear exactly how `deep_dup` handles a Proc, but it doesn't complain.
95
- # @api private
96
- def to_h
97
- store.deep_dup
98
- end
99
-
100
78
  # Temporarily set `options` and execute a block.
101
79
  # @api private
102
80
  def with(options)
@@ -133,6 +111,19 @@ module PaperTrail
133
111
 
134
112
  private
135
113
 
114
+ # @api private
115
+ def merge(options)
116
+ options.to_h.each do |k, v|
117
+ store[k] = v
118
+ end
119
+ end
120
+
121
+ # @api private
122
+ def set(options)
123
+ store.clear
124
+ merge(options)
125
+ end
126
+
136
127
  # Returns a Hash, initializing with default values if necessary.
137
128
  # @api private
138
129
  def store
@@ -141,6 +132,15 @@ module PaperTrail
141
132
  }
142
133
  end
143
134
 
135
+ # Returns a deep copy of the internal hash from our RequestStore. Keys are
136
+ # all symbols. Values are mostly primitives, but whodunnit can be a Proc.
137
+ # We cannot use Marshal.dump here because it doesn't support Proc. It is
138
+ # unclear exactly how `deep_dup` handles a Proc, but it doesn't complain.
139
+ # @api private
140
+ def to_h
141
+ store.deep_dup
142
+ end
143
+
144
144
  # Provide a helpful error message if someone has a typo in one of their
145
145
  # option keys. We don't validate option values here. That's traditionally
146
146
  # been handled with casting (`to_s`, `!!`) in the accessor method.
@@ -12,7 +12,7 @@ module PaperTrail
12
12
  if use_safe_load?
13
13
  ::YAML.safe_load(
14
14
  string,
15
- permitted_classes: ::ActiveRecord.yaml_column_permitted_classes,
15
+ permitted_classes: yaml_column_permitted_classes,
16
16
  aliases: true
17
17
  )
18
18
  elsif ::YAML.respond_to?(:unsafe_load)
@@ -39,10 +39,29 @@ module PaperTrail
39
39
 
40
40
  private
41
41
 
42
- # `use_yaml_unsafe_load` was added in 7.0.3.1, will be removed in 7.1.0?
43
42
  def use_safe_load?
44
- defined?(ActiveRecord.use_yaml_unsafe_load) &&
45
- !ActiveRecord.use_yaml_unsafe_load
43
+ if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
44
+ # `use_yaml_unsafe_load` may be removed in the future, at which point
45
+ # safe loading will be the default.
46
+ !defined?(ActiveRecord.use_yaml_unsafe_load) || !ActiveRecord.use_yaml_unsafe_load
47
+ elsif defined?(ActiveRecord::Base.use_yaml_unsafe_load)
48
+ # Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
49
+ !ActiveRecord::Base.use_yaml_unsafe_load
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ def yaml_column_permitted_classes
56
+ if defined?(ActiveRecord.yaml_column_permitted_classes)
57
+ # Rails >= 7.0.3.1
58
+ ActiveRecord.yaml_column_permitted_classes
59
+ elsif defined?(ActiveRecord::Base.yaml_column_permitted_classes)
60
+ # Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
61
+ ActiveRecord::Base.yaml_column_permitted_classes
62
+ else
63
+ []
64
+ end
46
65
  end
47
66
  end
48
67
  end
@@ -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 = 13
10
+ MAJOR = 14
11
11
  MINOR = 0
12
12
  TINY = 0
13
13
 
data/lib/paper_trail.rb CHANGED
@@ -8,6 +8,12 @@
8
8
  # can revisit this decision.
9
9
  require "active_support/all"
10
10
 
11
+ # We used to `require "active_record"` here, but that was [replaced with a
12
+ # Railtie](https://github.com/paper-trail-gem/paper_trail/pull/1281) in PT 12.
13
+ # As a result, we cannot reference `ActiveRecord` in this file (ie. until our
14
+ # Railtie has loaded). If we did, it would cause [problems with non-Rails
15
+ # projects](https://github.com/paper-trail-gem/paper_trail/pull/1401).
16
+
11
17
  require "paper_trail/errors"
12
18
  require "paper_trail/cleaner"
13
19
  require "paper_trail/compatibility"
@@ -26,8 +32,6 @@ module PaperTrail
26
32
  named created_at.
27
33
  EOS
28
34
 
29
- RAILS_GTE_7_0 = ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0")
30
-
31
35
  extend PaperTrail::Cleaner
32
36
 
33
37
  class << self
@@ -98,7 +102,7 @@ module PaperTrail
98
102
 
99
103
  # Returns PaperTrail's global configuration object, a singleton. These
100
104
  # settings affect all threads.
101
- # @api private
105
+ # @api public
102
106
  def config
103
107
  @config ||= PaperTrail::Config.instance
104
108
  yield @config if block_given?
@@ -106,6 +110,7 @@ module PaperTrail
106
110
  end
107
111
  alias configure config
108
112
 
113
+ # @api public
109
114
  def version
110
115
  VERSION::STRING
111
116
  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: 13.0.0
4
+ version: 14.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-08-16 00:00:00.000000000 Z
13
+ date: 2022-11-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -18,28 +18,28 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '5.2'
21
+ version: '6.0'
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: '5.2'
28
+ version: '6.0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: request_store
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '1.1'
35
+ version: '1.4'
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '1.1'
42
+ version: '1.4'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: appraisal
45
45
  requirement: !ruby/object:Gem::Requirement
@@ -116,14 +116,14 @@ dependencies:
116
116
  requirements:
117
117
  - - ">="
118
118
  - !ruby/object:Gem::Version
119
- version: '5.2'
119
+ version: '6.0'
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: '5.2'
126
+ version: '6.0'
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: rake
129
129
  requirement: !ruby/object:Gem::Requirement
@@ -360,7 +360,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
360
360
  requirements:
361
361
  - - ">="
362
362
  - !ruby/object:Gem::Version
363
- version: 2.6.0
363
+ version: 2.7.0
364
364
  required_rubygems_version: !ruby/object:Gem::Requirement
365
365
  requirements:
366
366
  - - ">="