paper_trail 11.1.0 → 15.2.0

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