paper_trail 9.1.1 → 9.2.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: aff3701e63c6bfcf374c2a62f04f60e2067f2cf4a5cd6fc0000dc2c62f16ba09
4
- data.tar.gz: 0d93bec07cca4894a0336e7e8c05877f8fcd9a27e363052da47aa4617850e392
3
+ metadata.gz: a2e53b5f5457783341505d9c0341c5fdc93c52191644db689260311c6c63de72
4
+ data.tar.gz: bd94edaeb1bd3ca9d329b925241c1fe8774e7f058e64f82e06411e04397833ae
5
5
  SHA512:
6
- metadata.gz: 037ae894bcfb07a7221832497b7d457d2c827c1dcca7a03ac5469a22fbb8a854b5a0e3062e9b0f304976538b0504f10951dcbbdf7fdc7da868f12543ad3b07d8
7
- data.tar.gz: acdd0a1482780e1c680a886666b74d1444f788ac707b63ea5d9f91afa59b9f3faf1fb1fc829a599ed9100b7ac4b8bc96eb143d43b9d323e9674e0a3cf9d68952
6
+ metadata.gz: 63d9377e20237a70d024e857f64dcd089126efc5bbce13f4dffc65a1c9f4d8210426269bfb60af9bdcdaa7d23211c2359d3a8676ceaa3346b0d0e414cd813ffd
7
+ data.tar.gz: 952ea2469901473c0fe66b5b5e6aaaf65070957c0211147c60372c5a172029415aea87c93c6daac2c00243a087aa4135ca304b0ac69ad0f32a1a4fb940543e50
@@ -23,12 +23,6 @@ module PaperTrail
23
23
  default: false,
24
24
  desc: "Store changeset (diff) with each version"
25
25
  )
26
- class_option(
27
- :with_associations,
28
- type: :boolean,
29
- default: false,
30
- desc: "Store transactional IDs to support association restoration"
31
- )
32
26
 
33
27
  desc "Generates (but does not run) a migration to add a versions table." \
34
28
  " Also generates an initializer file for configuring PaperTrail"
@@ -36,18 +30,6 @@ module PaperTrail
36
30
  def create_migration_file
37
31
  add_paper_trail_migration("create_versions")
38
32
  add_paper_trail_migration("add_object_changes_to_versions") if options.with_changes?
39
- if options.with_associations?
40
- add_paper_trail_migration("create_version_associations")
41
- add_paper_trail_migration("add_transaction_id_column_to_versions")
42
- end
43
- end
44
-
45
- def create_initializer
46
- create_file(
47
- "config/initializers/paper_trail.rb",
48
- "PaperTrail.config.track_associations = #{!!options.with_associations?}\n",
49
- "PaperTrail.config.association_reify_error_behaviour = :error"
50
- )
51
33
  end
52
34
 
53
35
  def self.next_migration_number(dirname)
data/lib/paper_trail.rb CHANGED
@@ -21,7 +21,6 @@ require "paper_trail/has_paper_trail"
21
21
  require "paper_trail/record_history"
22
22
  require "paper_trail/reifier"
23
23
  require "paper_trail/request"
24
- require "paper_trail/version_association_concern"
25
24
  require "paper_trail/version_concern"
26
25
  require "paper_trail/version_number"
27
26
  require "paper_trail/serializers/json"
@@ -45,16 +44,6 @@ module PaperTrail
45
44
  extend PaperTrail::Cleaner
46
45
 
47
46
  class << self
48
- # @api private
49
- def clear_transaction_id
50
- ::ActiveSupport::Deprecation.warn(
51
- "PaperTrail.clear_transaction_id is deprecated, " \
52
- "use PaperTrail.request.clear_transaction_id",
53
- caller(1)
54
- )
55
- request.clear_transaction_id
56
- end
57
-
58
47
  # Switches PaperTrail on or off, for all threads.
59
48
  # @api public
60
49
  def enabled=(value)
@@ -207,29 +196,6 @@ module PaperTrail
207
196
  PaperTrail.config.serializer
208
197
  end
209
198
 
210
- # @api public
211
- def transaction?
212
- ::ActiveRecord::Base.connection.open_transactions.positive?
213
- end
214
-
215
- # @deprecated
216
- def transaction_id
217
- ::ActiveSupport::Deprecation.warn(
218
- "PaperTrail.transaction_id is deprecated without replacement.",
219
- caller(1)
220
- )
221
- request.transaction_id
222
- end
223
-
224
- # @deprecated
225
- def transaction_id=(id)
226
- ::ActiveSupport::Deprecation.warn(
227
- "PaperTrail.transaction_id= is deprecated without replacement.",
228
- caller(1)
229
- )
230
- request.transaction_id = id
231
- end
232
-
233
199
  # Returns PaperTrail's global configuration object, a singleton. These
234
200
  # settings affect all threads.
235
201
  # @api private
@@ -262,3 +228,7 @@ if defined?(::Rails)
262
228
  else
263
229
  require "paper_trail/frameworks/active_record"
264
230
  end
231
+
232
+ # https://github.com/paper-trail-gem/paper_trail/issues/1070
233
+ # https://github.com/westonganger/paper_trail-association_tracking/issues/2
234
+ require "paper_trail-association_tracking"
@@ -7,26 +7,9 @@ module PaperTrail
7
7
  # Global configuration affecting all threads. Some thread-specific
8
8
  # configuration can be found in `paper_trail.rb`, others in `controller.rb`.
9
9
  class Config
10
- DPR_TRACK_ASSOC = <<~STR
11
- Association tracking is an endangered feature. For the past three or four
12
- years it has been an experimental feature, not recommended for production.
13
- It has a long list of known issues
14
- (https://github.com/paper-trail-gem/paper_trail#4b1-known-issues) and has no
15
- regular volunteers caring for it.
16
-
17
- If you don't use this feature, I strongly recommend disabling it.
18
-
19
- If you do use this feature, please head over to
20
- https://github.com/paper-trail-gem/paper_trail/issues/1070 and volunteer to work
21
- on the known issues.
22
-
23
- If we can't make a serious dent in the list of known issues over the next
24
- few years, then I'm inclined to delete it, though that would make me sad
25
- because I've put dozens of hours into it, and I know others have too.
26
- STR
27
-
28
10
  include Singleton
29
- attr_accessor :serializer, :version_limit, :association_reify_error_behaviour
11
+ attr_accessor :serializer, :version_limit, :association_reify_error_behaviour,
12
+ :object_changes_adapter
30
13
 
31
14
  def initialize
32
15
  # Variables which affect all threads, whose access is synchronized.
@@ -37,30 +20,6 @@ module PaperTrail
37
20
  @serializer = PaperTrail::Serializers::YAML
38
21
  end
39
22
 
40
- def track_associations=(value)
41
- @track_associations = !!value
42
- if @track_associations
43
- ::ActiveSupport::Deprecation.warn(DPR_TRACK_ASSOC, caller(1))
44
- end
45
- end
46
-
47
- # As of PaperTrail 5, `track_associations?` defaults to false. Tracking
48
- # associations is an experimental feature so we recommend setting
49
- # PaperTrail.config.track_associations = false in your
50
- # config/initializers/paper_trail.rb
51
- #
52
- # In PT 4, we checked `PaperTrail::VersionAssociation.table_exists?`
53
- # here, but that proved to be problematic in situations when the database
54
- # connection had not been established, or when the database does not exist
55
- # yet (as with `rake db:create`).
56
- def track_associations?
57
- if @track_associations.nil?
58
- false
59
- else
60
- @track_associations
61
- end
62
- end
63
-
64
23
  # Indicates whether PaperTrail is on or off. Default: true.
65
24
  def enabled
66
25
  @mutex.synchronize { !!@enabled }
@@ -2,5 +2,4 @@
2
2
 
3
3
  # This file only needs to be loaded if the gem is being used outside of Rails,
4
4
  # since otherwise the model(s) will get loaded in via the `Rails::Engine`.
5
- require "paper_trail/frameworks/active_record/models/paper_trail/version_association"
6
5
  require "paper_trail/frameworks/active_record/models/paper_trail/version"
@@ -4,9 +4,12 @@ require "paper_trail/version_concern"
4
4
 
5
5
  module PaperTrail
6
6
  # This is the default ActiveRecord model provided by PaperTrail. Most simple
7
- # applications will only use this and its partner, `VersionAssociation`, but
8
- # it is possible to sub-class, extend, or even do without this model entirely.
9
- # See the readme for details.
7
+ # applications will use this model as-is, but it is possible to sub-class,
8
+ # extend, or even do without this model entirely. See documentation section
9
+ # 6.a. Custom Version Classes.
10
+ #
11
+ # The paper_trail-association_tracking gem provides a related model,
12
+ # `VersionAssociation`.
10
13
  class Version < ::ActiveRecord::Base
11
14
  include PaperTrail::VersionConcern
12
15
  end
@@ -141,9 +141,8 @@ module PaperTrail
141
141
  @model_class.send :include, ::PaperTrail::Model::InstanceMethods
142
142
  setup_options(options)
143
143
  setup_associations(options)
144
- setup_transaction_callbacks
144
+ @model_class.after_rollback { paper_trail.clear_rolled_back_versions }
145
145
  setup_callbacks_from_options options[:on]
146
- setup_callbacks_for_habtm options[:join_tables]
147
146
  end
148
147
 
149
148
  def version_class
@@ -169,11 +168,6 @@ module PaperTrail
169
168
  ::ActiveRecord::Base.belongs_to_required_by_default
170
169
  end
171
170
 
172
- def habtm_assocs_not_skipped
173
- @model_class.reflect_on_all_associations(:has_and_belongs_to_many).
174
- reject { |a| @model_class.paper_trail_options[:skip].include?(a.name.to_s) }
175
- end
176
-
177
171
  def setup_associations(options)
178
172
  @model_class.class_attribute :version_association_name
179
173
  @model_class.version_association_name = options[:version] || :version
@@ -199,33 +193,12 @@ module PaperTrail
199
193
  )
200
194
  end
201
195
 
202
- # Adds callbacks to record changes to habtm associations such that on save
203
- # the previous version of the association (if changed) can be reconstructed.
204
- def setup_callbacks_for_habtm(join_tables)
205
- @model_class.send :attr_accessor, :paper_trail_habtm
206
- @model_class.class_attribute :paper_trail_save_join_tables
207
- @model_class.paper_trail_save_join_tables = Array.wrap(join_tables)
208
- habtm_assocs_not_skipped.each(&method(:setup_habtm_change_callbacks))
209
- end
210
-
211
196
  def setup_callbacks_from_options(options_on = [])
212
197
  options_on.each do |event|
213
198
  public_send("on_#{event}")
214
199
  end
215
200
  end
216
201
 
217
- def setup_habtm_change_callbacks(assoc)
218
- assoc_name = assoc.name
219
- %w[add remove].each do |verb|
220
- @model_class.send(:"before_#{verb}_for_#{assoc_name}").send(
221
- :<<,
222
- lambda do |*args|
223
- update_habtm_state(assoc_name, :"before_#{verb}", args[-2], args.last)
224
- end
225
- )
226
- end
227
- end
228
-
229
202
  def setup_options(options)
230
203
  @model_class.class_attribute :paper_trail_options
231
204
  @model_class.paper_trail_options = options.dup
@@ -242,29 +215,5 @@ module PaperTrail
242
215
  @model_class.paper_trail_options[:save_changes] = true
243
216
  end
244
217
  end
245
-
246
- # Reset the transaction id when the transaction is closed.
247
- def setup_transaction_callbacks
248
- @model_class.after_commit { PaperTrail.request.clear_transaction_id }
249
- @model_class.after_rollback { PaperTrail.request.clear_transaction_id }
250
- @model_class.after_rollback { paper_trail.clear_rolled_back_versions }
251
- end
252
-
253
- def update_habtm_state(name, callback, model, assoc)
254
- model.paper_trail_habtm ||= {}
255
- model.paper_trail_habtm[name] ||= { removed: [], added: [] }
256
- state = model.paper_trail_habtm[name]
257
- assoc_id = assoc.id
258
- case callback
259
- when :before_add
260
- state[:added] |= [assoc_id]
261
- state[:removed] -= [assoc_id]
262
- when :before_remove
263
- state[:removed] |= [assoc_id]
264
- state[:added] -= [assoc_id]
265
- else
266
- raise "Invalid callback: #{callback}"
267
- end
268
- end
269
218
  end
270
219
  end
@@ -23,6 +23,11 @@ module PaperTrail
23
23
 
24
24
  # @api private
25
25
  def execute
26
+ if PaperTrail.config.object_changes_adapter
27
+ return PaperTrail.config.object_changes_adapter.where_object_changes(
28
+ @version_model_class, @attributes
29
+ )
30
+ end
26
31
  case @version_model_class.columns_hash["object_changes"].type
27
32
  when :jsonb
28
33
  jsonb
@@ -45,34 +45,6 @@ module PaperTrail
45
45
  @in_after_callback = false
46
46
  end
47
47
 
48
- # Utility method for reifying. Anything executed inside the block will
49
- # appear like a new record.
50
- #
51
- # > .. as best as I can tell, the purpose of
52
- # > appear_as_new_record was to attempt to prevent the callbacks in
53
- # > AutosaveAssociation (which is the module responsible for persisting
54
- # > foreign key changes earlier than most people want most of the time
55
- # > because backwards compatibility or the maintainer hates himself or
56
- # > something) from running. By also stubbing out persisted? we can
57
- # > actually prevent those. A more stable option might be to use suppress
58
- # > instead, similar to the other branch in reify_has_one.
59
- # > -Sean Griffin (https://github.com/paper-trail-gem/paper_trail/pull/899)
60
- #
61
- # @api private
62
- def appear_as_new_record
63
- @record.instance_eval {
64
- alias :old_new_record? :new_record?
65
- alias :new_record? :present?
66
- alias :old_persisted? :persisted?
67
- alias :persisted? :nil?
68
- }
69
- yield
70
- @record.instance_eval {
71
- alias :new_record? :old_new_record?
72
- alias :persisted? :old_persisted?
73
- }
74
- end
75
-
76
48
  def attributes_before_change(is_touch)
77
49
  Hash[@record.attributes.map do |k, v|
78
50
  if @record.class.column_names.include?(k)
@@ -265,9 +237,7 @@ module PaperTrail
265
237
  @in_after_callback = true
266
238
  return unless enabled?
267
239
  versions_assoc = @record.send(@record.class.versions_association_name)
268
- version = versions_assoc.create! data_for_create
269
- update_transaction_id(version)
270
- save_associations(version)
240
+ versions_assoc.create! data_for_create
271
241
  ensure
272
242
  @in_after_callback = false
273
243
  end
@@ -285,13 +255,14 @@ module PaperTrail
285
255
  if record_object_changes? && changed_notably?
286
256
  data[:object_changes] = recordable_object_changes(changes)
287
257
  end
288
- add_transaction_id_to(data)
289
258
  merge_metadata_into(data)
290
259
  end
291
260
 
292
261
  # `recording_order` is "after" or "before". See ModelConfig#on_destroy.
293
262
  #
294
263
  # @api private
264
+ # @return - The created version object, so that plugins can use it, e.g.
265
+ # paper_trail-association_tracking
295
266
  def record_destroy(recording_order)
296
267
  @in_after_callback = recording_order == "after"
297
268
  if enabled? && !@record.new_record?
@@ -301,8 +272,7 @@ module PaperTrail
301
272
  else
302
273
  @record.send("#{@record.class.version_association_name}=", version)
303
274
  @record.send(@record.class.versions_association_name).reset
304
- update_transaction_id(version)
305
- save_associations(version)
275
+ version
306
276
  end
307
277
  end
308
278
  ensure
@@ -319,7 +289,6 @@ module PaperTrail
319
289
  object: recordable_object(false),
320
290
  whodunnit: PaperTrail.request.whodunnit
321
291
  }
322
- add_transaction_id_to(data)
323
292
  merge_metadata_into(data)
324
293
  end
325
294
 
@@ -331,6 +300,9 @@ module PaperTrail
331
300
  @record.class.paper_trail.version_class.column_names.include?("object_changes")
332
301
  end
333
302
 
303
+ # @api private
304
+ # @return - The created version object, so that plugins can use it, e.g.
305
+ # paper_trail-association_tracking
334
306
  def record_update(force:, in_after_callback:, is_touch:)
335
307
  @in_after_callback = in_after_callback
336
308
  if enabled? && (force || changed_notably?)
@@ -339,8 +311,7 @@ module PaperTrail
339
311
  if version.errors.any?
340
312
  log_version_errors(version, :update)
341
313
  else
342
- update_transaction_id(version)
343
- save_associations(version)
314
+ version
344
315
  end
345
316
  end
346
317
  ensure
@@ -363,11 +334,12 @@ module PaperTrail
363
334
  if record_object_changes?
364
335
  data[:object_changes] = recordable_object_changes(changes)
365
336
  end
366
- add_transaction_id_to(data)
367
337
  merge_metadata_into(data)
368
338
  end
369
339
 
370
340
  # @api private
341
+ # @return - The created version object, so that plugins can use it, e.g.
342
+ # paper_trail-association_tracking
371
343
  def record_update_columns(changes)
372
344
  return unless enabled?
373
345
  versions_assoc = @record.send(@record.class.versions_association_name)
@@ -375,8 +347,7 @@ module PaperTrail
375
347
  if version.errors.any?
376
348
  log_version_errors(version, :update)
377
349
  else
378
- update_transaction_id(version)
379
- save_associations(version)
350
+ version
380
351
  end
381
352
  end
382
353
 
@@ -391,7 +362,6 @@ module PaperTrail
391
362
  if record_object_changes?
392
363
  data[:object_changes] = recordable_object_changes(changes)
393
364
  end
394
- add_transaction_id_to(data)
395
365
  merge_metadata_into(data)
396
366
  end
397
367
 
@@ -418,6 +388,10 @@ module PaperTrail
418
388
  #
419
389
  # @api private
420
390
  def recordable_object_changes(changes)
391
+ if PaperTrail.config.object_changes_adapter
392
+ changes = PaperTrail.config.object_changes_adapter.diff(changes)
393
+ end
394
+
421
395
  if @record.class.paper_trail.version_class.object_changes_col_is_json?
422
396
  changes
423
397
  else
@@ -434,39 +408,6 @@ module PaperTrail
434
408
  end
435
409
  end
436
410
 
437
- # Saves associations if the join table for `VersionAssociation` exists.
438
- def save_associations(version)
439
- return unless PaperTrail.config.track_associations?
440
- save_bt_associations(version)
441
- save_habtm_associations(version)
442
- end
443
-
444
- # Save all `belongs_to` associations.
445
- # @api private
446
- def save_bt_associations(version)
447
- @record.class.reflect_on_all_associations(:belongs_to).each do |assoc|
448
- save_bt_association(assoc, version)
449
- end
450
- end
451
-
452
- # When a record is created, updated, or destroyed, we determine what the
453
- # HABTM associations looked like before any changes were made, by using
454
- # the `paper_trail_habtm` data structure. Then, we create
455
- # `VersionAssociation` records for each of the associated records.
456
- # @api private
457
- def save_habtm_associations(version)
458
- @record.class.reflect_on_all_associations(:has_and_belongs_to_many).each do |a|
459
- next unless save_habtm_association?(a)
460
- habtm_assoc_ids(a).each do |id|
461
- PaperTrail::VersionAssociation.create(
462
- version_id: version.transaction_id,
463
- foreign_key_name: a.name,
464
- foreign_key_id: id
465
- )
466
- end
467
- end
468
- end
469
-
470
411
  # AR callback.
471
412
  # @api private
472
413
  def save_version?
@@ -598,11 +539,6 @@ module PaperTrail
598
539
 
599
540
  private
600
541
 
601
- def add_transaction_id_to(data)
602
- return unless @record.class.paper_trail.version_class.column_names.include?("transaction_id")
603
- data[:transaction_id] = PaperTrail.request.transaction_id
604
- end
605
-
606
542
  # Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
607
543
  # https://github.com/paper-trail-gem/paper_trail/pull/899
608
544
  #
@@ -661,16 +597,6 @@ module PaperTrail
661
597
  end
662
598
  end
663
599
 
664
- # Given a HABTM association, returns an array of ids.
665
- #
666
- # @api private
667
- def habtm_assoc_ids(habtm_assoc)
668
- current = @record.send(habtm_assoc.name).to_a.map(&:id) # TODO: `pluck` would use less memory
669
- removed = @record.paper_trail_habtm.try(:[], habtm_assoc.name).try(:[], :removed) || []
670
- added = @record.paper_trail_habtm.try(:[], habtm_assoc.name).try(:[], :added) || []
671
- current + removed - added
672
- end
673
-
674
600
  def log_version_errors(version, action)
675
601
  version.logger&.warn(
676
602
  "Unable to create version for #{action} of #{@record.class.name}" \
@@ -678,44 +604,6 @@ module PaperTrail
678
604
  )
679
605
  end
680
606
 
681
- # Save a single `belongs_to` association.
682
- # @api private
683
- def save_bt_association(assoc, version)
684
- assoc_version_args = {
685
- version_id: version.id,
686
- foreign_key_name: assoc.foreign_key
687
- }
688
-
689
- if assoc.options[:polymorphic]
690
- associated_record = @record.send(assoc.name) if @record.send(assoc.foreign_type)
691
- if associated_record && PaperTrail.request.enabled_for_model?(associated_record.class)
692
- assoc_version_args[:foreign_key_id] = associated_record.id
693
- end
694
- elsif PaperTrail.request.enabled_for_model?(assoc.klass)
695
- assoc_version_args[:foreign_key_id] = @record.send(assoc.foreign_key)
696
- end
697
-
698
- if assoc_version_args.key?(:foreign_key_id)
699
- PaperTrail::VersionAssociation.create(assoc_version_args)
700
- end
701
- end
702
-
703
- # Returns true if the given HABTM association should be saved.
704
- # @api private
705
- def save_habtm_association?(assoc)
706
- @record.class.paper_trail_save_join_tables.include?(assoc.name) ||
707
- PaperTrail.request.enabled_for_model?(assoc.klass)
708
- end
709
-
710
- def update_transaction_id(version)
711
- return unless @record.class.paper_trail.version_class.column_names.include?("transaction_id")
712
- if PaperTrail.transaction? && PaperTrail.request.transaction_id.nil?
713
- PaperTrail.request.transaction_id = version.id
714
- version.transaction_id = version.id
715
- version.save
716
- end
717
- end
718
-
719
607
  def version
720
608
  @record.public_send(@record.class.version_association_name)
721
609
  end