paper_trail 13.0.0 → 14.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="