paper_trail 11.1.0 → 15.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/paper_trail/install/install_generator.rb +29 -5
  3. data/lib/generators/paper_trail/install/templates/create_versions.rb.erb +11 -6
  4. data/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +4 -2
  5. data/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +24 -10
  6. data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +10 -10
  7. data/lib/paper_trail/attribute_serializers/object_attribute.rb +13 -3
  8. data/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +13 -3
  9. data/lib/paper_trail/compatibility.rb +3 -3
  10. data/lib/paper_trail/errors.rb +33 -0
  11. data/lib/paper_trail/events/base.rb +84 -64
  12. data/lib/paper_trail/events/destroy.rb +1 -1
  13. data/lib/paper_trail/events/update.rb +23 -7
  14. data/lib/paper_trail/frameworks/active_record.rb +9 -2
  15. data/lib/paper_trail/frameworks/rails/controller.rb +0 -6
  16. data/lib/paper_trail/frameworks/rails/railtie.rb +34 -0
  17. data/lib/paper_trail/frameworks/rails.rb +1 -2
  18. data/lib/paper_trail/has_paper_trail.rb +4 -0
  19. data/lib/paper_trail/model_config.rb +51 -45
  20. data/lib/paper_trail/queries/versions/where_attribute_changes.rb +50 -0
  21. data/lib/paper_trail/queries/versions/where_object.rb +1 -1
  22. data/lib/paper_trail/queries/versions/where_object_changes.rb +9 -14
  23. data/lib/paper_trail/queries/versions/where_object_changes_from.rb +57 -0
  24. data/lib/paper_trail/queries/versions/where_object_changes_to.rb +57 -0
  25. data/lib/paper_trail/record_trail.rb +81 -64
  26. data/lib/paper_trail/reifier.rb +27 -10
  27. data/lib/paper_trail/request.rb +22 -25
  28. data/lib/paper_trail/serializers/json.rb +0 -10
  29. data/lib/paper_trail/serializers/yaml.rb +38 -13
  30. data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +1 -14
  31. data/lib/paper_trail/version_concern.rb +74 -22
  32. data/lib/paper_trail/version_number.rb +2 -2
  33. data/lib/paper_trail.rb +30 -40
  34. metadata +98 -45
  35. data/Gemfile +0 -4
  36. data/lib/paper_trail/frameworks/rails/engine.rb +0 -45
  37. data/paper_trail.gemspec +0 -69
@@ -60,9 +60,7 @@ module PaperTrail
60
60
  model = if options[:dup] == true || version.event == "destroy"
61
61
  klass.new
62
62
  else
63
- find_cond = { klass.primary_key => version.item_id }
64
-
65
- version.item || klass.unscoped.where(find_cond).first || klass.new
63
+ version.item || init_model_by_finding_item_id(klass, version) || klass.new
66
64
  end
67
65
 
68
66
  if options[:unversioned_attributes] == :nil && !model.new_record?
@@ -72,6 +70,11 @@ module PaperTrail
72
70
  model
73
71
  end
74
72
 
73
+ # @api private
74
+ def init_model_by_finding_item_id(klass, version)
75
+ klass.unscoped.where(klass.primary_key => version.item_id).first
76
+ end
77
+
75
78
  # Look for attributes that exist in `model` and not in this version.
76
79
  # These attributes should be set to nil. Modifies `attrs`.
77
80
  # @api private
@@ -109,21 +112,35 @@ module PaperTrail
109
112
  end
110
113
 
111
114
  # Given a `version`, return the class to reify. This method supports
112
- # Single Table Inheritance (STI) with custom inheritance columns.
115
+ # Single Table Inheritance (STI) with custom inheritance columns and
116
+ # custom inheritance column values.
113
117
  #
114
118
  # For example, imagine a `version` whose `item_type` is "Animal". The
115
119
  # `animals` table is an STI table (it has cats and dogs) and it has a
116
120
  # custom inheritance column, `species`. If `attrs["species"]` is "Dog",
117
121
  # this method returns the constant `Dog`. If `attrs["species"]` is blank,
118
- # this method returns the constant `Animal`. You can see this particular
119
- # example in action in `spec/models/animal_spec.rb`.
122
+ # this method returns the constant `Animal`.
120
123
  #
121
- # TODO: Duplication: similar `constantize` in VersionConcern#version_limit
124
+ # The values contained in the inheritance columns may be non-camelized
125
+ # strings (e.g. 'dog' instead of 'Dog'). To reify classes in this case
126
+ # we need to call the parents class `sti_class_for` method to retrieve
127
+ # the correct record class.
128
+ #
129
+ # You can see these particular examples in action in
130
+ # `spec/models/animal_spec.rb` and `spec/models/plant_spec.rb`
122
131
  def version_reification_class(version, attrs)
123
- inheritance_column_name = version.item_type.constantize.inheritance_column
132
+ clazz = version.item_type.constantize
133
+ inheritance_column_name = clazz.inheritance_column
124
134
  inher_col_value = attrs[inheritance_column_name]
125
- class_name = inher_col_value.blank? ? version.item_type : inher_col_value
126
- class_name.constantize
135
+ return clazz if inher_col_value.blank?
136
+
137
+ # Rails 6.1 adds a public method for clients to use to customize STI classes. If that
138
+ # method is not available, fall back to using the private one
139
+ if clazz.public_methods.include?(:sti_class_for)
140
+ return clazz.sti_class_for(inher_col_value)
141
+ end
142
+
143
+ clazz.send(:find_sti_class, inher_col_value)
127
144
  end
128
145
  end
129
146
  end
@@ -12,9 +12,6 @@ module PaperTrail
12
12
  #
13
13
  # @api private
14
14
  module Request
15
- class InvalidOption < RuntimeError
16
- end
17
-
18
15
  class << self
19
16
  # Sets any data from the controller that you want PaperTrail to store.
20
17
  # See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
@@ -78,28 +75,6 @@ module PaperTrail
78
75
  !!store.fetch(:"enabled_for_#{model}", true)
79
76
  end
80
77
 
81
- # @api private
82
- def merge(options)
83
- options.to_h.each do |k, v|
84
- store[k] = v
85
- end
86
- end
87
-
88
- # @api private
89
- def set(options)
90
- store.clear
91
- merge(options)
92
- end
93
-
94
- # Returns a deep copy of the internal hash from our RequestStore. Keys are
95
- # all symbols. Values are mostly primitives, but whodunnit can be a Proc.
96
- # We cannot use Marshal.dump here because it doesn't support Proc. It is
97
- # unclear exactly how `deep_dup` handles a Proc, but it doesn't complain.
98
- # @api private
99
- def to_h
100
- store.deep_dup
101
- end
102
-
103
78
  # Temporarily set `options` and execute a block.
104
79
  # @api private
105
80
  def with(options)
@@ -136,6 +111,19 @@ module PaperTrail
136
111
 
137
112
  private
138
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
+
139
127
  # Returns a Hash, initializing with default values if necessary.
140
128
  # @api private
141
129
  def store
@@ -144,6 +132,15 @@ module PaperTrail
144
132
  }
145
133
  end
146
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
+
147
144
  # Provide a helpful error message if someone has a typo in one of their
148
145
  # option keys. We don't validate option values here. That's traditionally
149
146
  # been handled with casting (`to_s`, `!!`) in the accessor method.
@@ -31,16 +31,6 @@ module PaperTrail
31
31
  arel_field.matches("%\"#{field}\":#{json_value}%")
32
32
  end
33
33
  end
34
-
35
- def where_object_changes_condition(*)
36
- raise <<-STR.squish.freeze
37
- where_object_changes no longer supports reading JSON from a text
38
- column. The old implementation was inaccurate, returning more records
39
- than you wanted. This feature was deprecated in 7.1.0 and removed in
40
- 8.0.0. The json and jsonb datatypes are still supported. See the
41
- discussion at https://github.com/paper-trail-gem/paper_trail/issues/803
42
- STR
43
- end
44
34
  end
45
35
  end
46
36
  end
@@ -9,13 +9,23 @@ module PaperTrail
9
9
  extend self # makes all instance methods become module methods as well
10
10
 
11
11
  def load(string)
12
- ::YAML.load string
12
+ if use_safe_load?
13
+ ::YAML.safe_load(
14
+ string,
15
+ permitted_classes: yaml_column_permitted_classes,
16
+ aliases: true
17
+ )
18
+ elsif ::YAML.respond_to?(:unsafe_load)
19
+ ::YAML.unsafe_load(string)
20
+ else
21
+ ::YAML.load(string)
22
+ end
13
23
  end
14
24
 
15
25
  # @param object (Hash | HashWithIndifferentAccess) - Coming from
16
26
  # `recordable_object` `object` will be a plain `Hash`. However, due to
17
- # recent [memory optimizations](https://git.io/fjeYv), when coming from
18
- # `recordable_object_changes`, it will be a `HashWithIndifferentAccess`.
27
+ # recent [memory optimizations](https://github.com/paper-trail-gem/paper_trail/pull/1189),
28
+ # when coming from `recordable_object_changes`, it will be a `HashWithIndifferentAccess`.
19
29
  def dump(object)
20
30
  object = object.to_hash if object.is_a?(HashWithIndifferentAccess)
21
31
  ::YAML.dump object
@@ -27,16 +37,31 @@ module PaperTrail
27
37
  arel_field.matches("%\n#{field}: #{value}\n%")
28
38
  end
29
39
 
30
- # Returns a SQL LIKE condition to be used to match the given field and
31
- # value in the serialized `object_changes`.
32
- def where_object_changes_condition(*)
33
- raise <<-STR.squish.freeze
34
- where_object_changes no longer supports reading YAML from a text
35
- column. The old implementation was inaccurate, returning more records
36
- than you wanted. This feature was deprecated in 8.1.0 and removed in
37
- 9.0.0. The json and jsonb datatypes are still supported. See
38
- discussion at https://github.com/paper-trail-gem/paper_trail/pull/997
39
- STR
40
+ private
41
+
42
+ def use_safe_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
40
65
  end
41
66
  end
42
67
  end
@@ -11,15 +11,12 @@ module PaperTrail
11
11
  end
12
12
 
13
13
  def serialize(array)
14
- return serialize_with_ar(array) if active_record_pre_502?
15
14
  array
16
15
  end
17
16
 
18
17
  def deserialize(array)
19
- return deserialize_with_ar(array) if active_record_pre_502?
20
-
21
18
  case array
22
- # Needed for legacy reasons. If serialized array is a string
19
+ # Needed for legacy data. If serialized array is a string
23
20
  # then it was serialized with Rails < 5.0.2.
24
21
  when ::String then deserialize_with_ar(array)
25
22
  else array
@@ -28,16 +25,6 @@ module PaperTrail
28
25
 
29
26
  private
30
27
 
31
- def active_record_pre_502?
32
- ::ActiveRecord.gem_version < Gem::Version.new("5.0.2")
33
- end
34
-
35
- def serialize_with_ar(array)
36
- ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.
37
- new(@subtype, @delimiter).
38
- serialize(array)
39
- end
40
-
41
28
  def deserialize_with_ar(array)
42
29
  ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.
43
30
  new(@subtype, @delimiter).
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "paper_trail/attribute_serializers/object_changes_attribute"
4
+ require "paper_trail/queries/versions/where_attribute_changes"
4
5
  require "paper_trail/queries/versions/where_object"
5
6
  require "paper_trail/queries/versions/where_object_changes"
7
+ require "paper_trail/queries/versions/where_object_changes_from"
8
+ require "paper_trail/queries/versions/where_object_changes_to"
6
9
 
7
10
  module PaperTrail
8
11
  # Originally, PaperTrail did not provide this module, and all of this
@@ -12,23 +15,20 @@ module PaperTrail
12
15
  module VersionConcern
13
16
  extend ::ActiveSupport::Concern
14
17
 
15
- included do
16
- if ::ActiveRecord.gem_version >= Gem::Version.new("5.0")
17
- belongs_to :item, polymorphic: true, optional: true
18
- else
19
- belongs_to :item, polymorphic: true
20
- end
18
+ E_YAML_PERMITTED_CLASSES = <<-EOS.squish.freeze
19
+ PaperTrail encountered a Psych::DisallowedClass error during
20
+ deserialization of YAML column, indicating that
21
+ yaml_column_permitted_classes has not been configured correctly. %s
22
+ EOS
21
23
 
24
+ included do
25
+ belongs_to :item, polymorphic: true, optional: true, inverse_of: false
22
26
  validates_presence_of :event
23
27
  after_create :enforce_version_limit!
24
28
  end
25
29
 
26
30
  # :nodoc:
27
31
  module ClassMethods
28
- def item_subtype_column_present?
29
- column_names.include?("item_subtype")
30
- end
31
-
32
32
  def with_item_keys(item_type, item_id)
33
33
  where item_type: item_type, item_id: item_id
34
34
  end
@@ -46,7 +46,7 @@ module PaperTrail
46
46
  end
47
47
 
48
48
  def not_creates
49
- where "event <> ?", "create"
49
+ where.not(event: "create")
50
50
  end
51
51
 
52
52
  def between(start_time, end_time)
@@ -64,6 +64,18 @@ module PaperTrail
64
64
  end
65
65
  end
66
66
 
67
+ # Given an attribute like `"name"`, query the `versions.object_changes`
68
+ # column for any changes that modified the provided attribute.
69
+ #
70
+ # @api public
71
+ def where_attribute_changes(attribute)
72
+ unless attribute.is_a?(String) || attribute.is_a?(Symbol)
73
+ raise ArgumentError, "expected to receive a String or Symbol"
74
+ end
75
+
76
+ Queries::Versions::WhereAttributeChanges.new(self, attribute).execute
77
+ end
78
+
67
79
  # Given a hash of attributes like `name: 'Joan'`, query the
68
80
  # `versions.objects` column.
69
81
  #
@@ -120,6 +132,36 @@ module PaperTrail
120
132
  Queries::Versions::WhereObjectChanges.new(self, args).execute
121
133
  end
122
134
 
135
+ # Given a hash of attributes like `name: 'Joan'`, query the
136
+ # `versions.objects_changes` column for changes where the version changed
137
+ # from the hash of attributes to other values.
138
+ #
139
+ # This is useful for finding versions where the attribute started with a
140
+ # known value and changed to something else. This is in comparison to
141
+ # `where_object_changes` which will find both the changes before and
142
+ # after.
143
+ #
144
+ # @api public
145
+ def where_object_changes_from(args = {})
146
+ raise ArgumentError, "expected to receive a Hash" unless args.is_a?(Hash)
147
+ Queries::Versions::WhereObjectChangesFrom.new(self, args).execute
148
+ end
149
+
150
+ # Given a hash of attributes like `name: 'Joan'`, query the
151
+ # `versions.objects_changes` column for changes where the version changed
152
+ # to the hash of attributes from other values.
153
+ #
154
+ # This is useful for finding versions where the attribute started with an
155
+ # unknown value and changed to a known value. This is in comparison to
156
+ # `where_object_changes` which will find both the changes before and
157
+ # after.
158
+ #
159
+ # @api public
160
+ def where_object_changes_to(args = {})
161
+ raise ArgumentError, "expected to receive a Hash" unless args.is_a?(Hash)
162
+ Queries::Versions::WhereObjectChangesTo.new(self, args).execute
163
+ end
164
+
123
165
  def primary_key_is_int?
124
166
  @primary_key_is_int ||= columns_hash[primary_key].type == :integer
125
167
  rescue StandardError # TODO: Rescue something more specific
@@ -226,7 +268,7 @@ module PaperTrail
226
268
  #
227
269
  def reify(options = {})
228
270
  unless self.class.column_names.include? "object"
229
- raise "reify can't be called without an object column"
271
+ raise Error, "reify requires an object column"
230
272
  end
231
273
  return nil if object.nil?
232
274
  ::PaperTrail::Reifier.reify(self, options)
@@ -273,7 +315,7 @@ module PaperTrail
273
315
 
274
316
  # @api private
275
317
  def load_changeset
276
- if PaperTrail.config.object_changes_adapter&.respond_to?(:load_changeset)
318
+ if PaperTrail.config.object_changes_adapter.respond_to?(:load_changeset)
277
319
  return PaperTrail.config.object_changes_adapter.load_changeset(self)
278
320
  end
279
321
 
@@ -312,7 +354,10 @@ module PaperTrail
312
354
  else
313
355
  begin
314
356
  PaperTrail.serializer.load(object_changes)
315
- rescue StandardError # TODO: Rescue something more specific
357
+ rescue StandardError => e
358
+ if defined?(::Psych::Exception) && e.instance_of?(::Psych::Exception)
359
+ ::Kernel.warn format(E_YAML_PERMITTED_CLASSES, e)
360
+ end
316
361
  {}
317
362
  end
318
363
  end
@@ -339,16 +384,23 @@ module PaperTrail
339
384
  # The version limit can be global or per-model.
340
385
  #
341
386
  # @api private
342
- #
343
- # TODO: Duplication: similar `constantize` in Reifier#version_reification_class
344
387
  def version_limit
345
- if self.class.item_subtype_column_present?
346
- klass = (item_subtype || item_type).constantize
347
- if klass&.paper_trail_options&.key?(:limit)
348
- return klass.paper_trail_options[:limit]
349
- end
388
+ klass = item.class
389
+ if limit_option?(klass)
390
+ klass.paper_trail_options[:limit]
391
+ elsif base_class_limit_option?(klass)
392
+ klass.base_class.paper_trail_options[:limit]
393
+ else
394
+ PaperTrail.config.version_limit
350
395
  end
351
- PaperTrail.config.version_limit
396
+ end
397
+
398
+ def limit_option?(klass)
399
+ klass.respond_to?(:paper_trail_options) && klass.paper_trail_options.key?(:limit)
400
+ end
401
+
402
+ def base_class_limit_option?(klass)
403
+ klass.respond_to?(:base_class) && limit_option?(klass.base_class)
352
404
  end
353
405
  end
354
406
  end
@@ -7,8 +7,8 @@ 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 = 11
11
- MINOR = 1
10
+ MAJOR = 15
11
+ MINOR = 2
12
12
  TINY = 0
13
13
 
14
14
  # Set PRE to nil unless it's a pre-release (beta, rc, etc.)
data/lib/paper_trail.rb CHANGED
@@ -8,34 +8,24 @@
8
8
  # can revisit this decision.
9
9
  require "active_support/all"
10
10
 
11
- # AR is required for, eg. has_paper_trail.rb, so we could put this `require` in
12
- # all of those files, but it seems easier to troubleshoot if we just make sure
13
- # AR is loaded here before loading *any* of PT. See discussion of
14
- # performance/simplicity tradeoff for activesupport above.
15
- require "active_record"
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
16
 
17
- require "request_store"
17
+ require "paper_trail/errors"
18
18
  require "paper_trail/cleaner"
19
19
  require "paper_trail/compatibility"
20
20
  require "paper_trail/config"
21
- require "paper_trail/has_paper_trail"
22
21
  require "paper_trail/record_history"
23
- require "paper_trail/reifier"
24
22
  require "paper_trail/request"
25
- require "paper_trail/version_concern"
26
23
  require "paper_trail/version_number"
27
24
  require "paper_trail/serializers/json"
28
- require "paper_trail/serializers/yaml"
29
25
 
30
26
  # An ActiveRecord extension that tracks changes to your models, for auditing or
31
27
  # versioning.
32
28
  module PaperTrail
33
- E_RAILS_NOT_LOADED = <<-EOS.squish.freeze
34
- PaperTrail has been loaded too early, before rails is loaded. This can
35
- happen when another gem defines the ::Rails namespace, then PT is loaded,
36
- all before rails is loaded. You may want to reorder your Gemfile, or defer
37
- the loading of PT by using `require: false` and a manual require elsewhere.
38
- EOS
39
29
  E_TIMESTAMP_FIELD_CONFIG = <<-EOS.squish.freeze
40
30
  PaperTrail.timestamp_field= has been removed, without replacement. It is no
41
31
  longer configurable. The timestamp column in the versions table must now be
@@ -85,7 +75,7 @@ module PaperTrail
85
75
  #
86
76
  # @api public
87
77
  def request(options = nil, &block)
88
- if options.nil? && !block_given?
78
+ if options.nil? && !block
89
79
  Request
90
80
  else
91
81
  Request.with(options, &block)
@@ -95,7 +85,7 @@ module PaperTrail
95
85
  # Set the field which records when a version was created.
96
86
  # @api public
97
87
  def timestamp_field=(_field_name)
98
- raise(E_TIMESTAMP_FIELD_CONFIG)
88
+ raise Error, E_TIMESTAMP_FIELD_CONFIG
99
89
  end
100
90
 
101
91
  # Set the PaperTrail serializer. This setting affects all threads.
@@ -112,7 +102,7 @@ module PaperTrail
112
102
 
113
103
  # Returns PaperTrail's global configuration object, a singleton. These
114
104
  # settings affect all threads.
115
- # @api private
105
+ # @api public
116
106
  def config
117
107
  @config ||= PaperTrail::Config.instance
118
108
  yield @config if block_given?
@@ -120,33 +110,33 @@ module PaperTrail
120
110
  end
121
111
  alias configure config
122
112
 
113
+ # @api public
123
114
  def version
124
115
  VERSION::STRING
125
116
  end
126
- end
127
- end
128
117
 
129
- # We use the `on_load` "hook" instead of `ActiveRecord::Base.include` because we
130
- # don't want to cause all of AR to be autloaded yet. See
131
- # https://guides.rubyonrails.org/engines.html#what-are-on-load-hooks-questionmark
132
- # to learn more about `on_load`.
133
- ActiveSupport.on_load(:active_record) do
134
- include PaperTrail::Model
135
- end
118
+ def active_record_gte_7_0?
119
+ @active_record_gte_7_0 ||= ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0")
120
+ end
136
121
 
137
- # Require frameworks
138
- if defined?(::Rails)
139
- # Rails module is sometimes defined by gems like rails-html-sanitizer
140
- # so we check for presence of Rails.application.
141
- if defined?(::Rails.application)
142
- require "paper_trail/frameworks/rails"
143
- else
144
- ::Kernel.warn(::PaperTrail::E_RAILS_NOT_LOADED)
122
+ def deprecator
123
+ @deprecator ||= ActiveSupport::Deprecation.new("16.0", "PaperTrail")
124
+ end
145
125
  end
146
- else
147
- require "paper_trail/frameworks/active_record"
148
126
  end
149
127
 
150
- if defined?(::ActiveRecord)
151
- ::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version)
128
+ # PT is built on ActiveRecord, but does not require Rails. If Rails is defined,
129
+ # our Railtie makes sure not to load the AR-dependent parts of PT until AR is
130
+ # ready. A typical Rails `application.rb` has:
131
+ #
132
+ # ```
133
+ # require 'rails/all' # Defines `Rails`
134
+ # Bundler.require(*Rails.groups) # require 'paper_trail' (this file)
135
+ # ```
136
+ #
137
+ # Non-rails applications should take similar care to load AR before PT.
138
+ if defined?(Rails)
139
+ require "paper_trail/frameworks/rails"
140
+ else
141
+ require "paper_trail/frameworks/active_record"
152
142
  end