paper_trail 9.1.1 → 9.2.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: 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