paper_trail 12.0.0 → 12.3.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: f2ce7f019e3413c485beff4eefa957f759b084fe0d29b0d9f3451b6ba3e4befe
4
- data.tar.gz: 3f73870b42c582f683936eee21fe288b17d915d1bfbff5327ac50dd8e9766732
3
+ metadata.gz: e0f6ef6591b15081a76d93a70d959527f8cdc53cc3a6343ab6f1b7c40ef6695e
4
+ data.tar.gz: 202ee3126567ab99b39f8c76039fb7e20a4eb5e10e0e0880e867f19f228ace63
5
5
  SHA512:
6
- metadata.gz: 6134547d0c2c69f94962aea4f868aec8d456bbb0afc4588b9b07971074a8d50476f29a59acb41d3e058dea581678d0c7129d8e92a04c370fa2016dbd6bf544b7
7
- data.tar.gz: a6ca4e41cf2f6bce774894a9927dc0b3e4abbcbbdbb39fbf4671d029398f26999ce8927143f1f14279a6ad8aa70194025f5b6e653780560a0cc4870dce9b754e
6
+ metadata.gz: c144126a2d8cec5537ef218b6637ee2ff6e6863de2e935facf6ad020dba6c2fb46bc5c13ed2ab674e9230c264d6e87905415ebc3c1e4bee5fbe50b1e73285663
7
+ data.tar.gz: 96530aebe8af35a95d47b6a5deadd3797b2d9255b28156f24a9f1be67fe629b254747d39ccad4d8f5d348d9fd74300b4b865ff9b32125c0951b14cf5d13ed60f
@@ -20,6 +20,12 @@ module PaperTrail
20
20
  default: false,
21
21
  desc: "Store changeset (diff) with each version"
22
22
  )
23
+ class_option(
24
+ :uuid,
25
+ type: :boolean,
26
+ default: false,
27
+ desc: "Use uuid instead of bigint for item_id type (use only if tables use UUIDs)"
28
+ )
23
29
 
24
30
  desc "Generates (but does not run) a migration to add a versions table." \
25
31
  " See section 5.c. Generators in README.md for more information."
@@ -28,7 +34,8 @@ module PaperTrail
28
34
  add_paper_trail_migration(
29
35
  "create_versions",
30
36
  item_type_options: item_type_options,
31
- versions_table_options: versions_table_options
37
+ versions_table_options: versions_table_options,
38
+ item_id_type_options: item_id_type_options
32
39
  )
33
40
  if options.with_changes?
34
41
  add_paper_trail_migration("add_object_changes_to_versions")
@@ -37,13 +44,18 @@ module PaperTrail
37
44
 
38
45
  private
39
46
 
47
+ # To use uuid instead of integer for primary key
48
+ def item_id_type_options
49
+ options.uuid? ? "string" : "bigint"
50
+ end
51
+
40
52
  # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
41
53
  # See https://github.com/paper-trail-gem/paper_trail/issues/651
42
54
  def item_type_options
43
55
  if mysql?
44
- ", { null: false, limit: 191 }"
56
+ ", null: false, limit: 191"
45
57
  else
46
- ", { null: false }"
58
+ ", null: false"
47
59
  end
48
60
  end
49
61
 
@@ -11,7 +11,7 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
11
11
  def change
12
12
  create_table :versions<%= versions_table_options %> do |t|
13
13
  t.string :item_type<%= item_type_options %>
14
- t.bigint :item_id, null: false
14
+ t.<%= item_id_type_options %> :item_id, null: false
15
15
  t.string :event, null: false
16
16
  t.string :whodunnit
17
17
  t.text :object, limit: TEXT_BYTES
@@ -28,7 +28,9 @@ class CreateVersions < ActiveRecord::Migration<%= migration_version %>
28
28
  # MySQL users should also upgrade to at least rails 4.2, which is the first
29
29
  # version of ActiveRecord with support for fractional seconds in MySQL.
30
30
  # (https://github.com/rails/rails/pull/14359)
31
- #
31
+ #
32
+ # MySQL users should use the following line for `created_at`
33
+ # t.datetime :created_at, limit: 6
32
34
  t.datetime :created_at
33
35
  end
34
36
  add_index :versions, %i(item_type item_id)
@@ -7,8 +7,10 @@ module PaperTrail
7
7
  class UpdateItemSubtypeGenerator < MigrationGenerator
8
8
  source_root File.expand_path("templates", __dir__)
9
9
 
10
- desc "Generates (but does not run) a migration to update item_subtype for STI entries in an "\
11
- "existing versions table."
10
+ desc(
11
+ "Generates (but does not run) a migration to update item_subtype for "\
12
+ "STI entries in an existing versions table."
13
+ )
12
14
 
13
15
  def create_migration_file
14
16
  add_paper_trail_migration("update_versions_for_item_subtype", sti_type_options: options)
@@ -32,6 +32,12 @@ 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)
36
+ # Because Rails 7 time attribute throws a delegation error when you deserialize
37
+ # it with the factory.
38
+ # See ActiveRecord::Type::Time::Value crashes when loaded from YAML on rails 7.0
39
+ # https://github.com/rails/rails/issues/43966
40
+ val.instance_variable_get(:@time)
35
41
  else
36
42
  AttributeSerializerFactory.for(@klass, attr).deserialize(val)
37
43
  end
@@ -8,7 +8,7 @@ module PaperTrail
8
8
  #
9
9
  # It is not safe to assume that a new version of rails will be compatible with
10
10
  # PaperTrail. PT is only compatible with the versions of rails that it is
11
- # tested against. See `.travis.yml`.
11
+ # tested against. See `.github/workflows/test.yml`.
12
12
  #
13
13
  # However, as of
14
14
  # [#1213](https://github.com/paper-trail-gem/paper_trail/pull/1213) our
@@ -18,7 +18,7 @@ module PaperTrail
18
18
  # versions.
19
19
  module Compatibility
20
20
  ACTIVERECORD_GTE = ">= 5.2" # enforced in gemspec
21
- ACTIVERECORD_LT = "< 6.2" # not enforced in gemspec
21
+ ACTIVERECORD_LT = "< 7.1" # not enforced in gemspec
22
22
 
23
23
  E_INCOMPATIBLE_AR = <<-EOS
24
24
  PaperTrail %s is not compatible with ActiveRecord %s. We allow PT
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ # Generic PaperTrail exception.
5
+ # @api public
6
+ class Error < StandardError
7
+ end
8
+
9
+ # An unexpected option, perhaps a typo, was passed to a public API method.
10
+ # @api public
11
+ class InvalidOption < Error
12
+ end
13
+
14
+ # The application's database schema is not supported.
15
+ # @api public
16
+ class UnsupportedSchema < Error
17
+ end
18
+
19
+ # The application's database column type is not supported.
20
+ # @api public
21
+ class UnsupportedColumnType < UnsupportedSchema
22
+ def initialize(method:, expected:, actual:)
23
+ super(
24
+ format(
25
+ "%s expected %s column, got %s",
26
+ method,
27
+ expected,
28
+ actual
29
+ )
30
+ )
31
+ end
32
+ end
33
+ end
@@ -109,19 +109,25 @@ module PaperTrail
109
109
  @changed_in_latest_version ||= changes_in_latest_version.keys
110
110
  end
111
111
 
112
- # Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
113
- # https://github.com/paper-trail-gem/paper_trail/pull/899
112
+ # Memoized to reduce memory usage
114
113
  #
115
114
  # @api private
116
115
  def changes_in_latest_version
117
- # Memoized to reduce memory usage
118
- @changes_in_latest_version ||= begin
119
- if @in_after_callback
120
- @record.saved_changes
121
- else
122
- @record.changes
123
- end
116
+ @changes_in_latest_version ||= load_changes_in_latest_version
117
+ end
118
+
119
+ # @api private
120
+ def evaluate_only
121
+ only = @record.paper_trail_options[:only].dup
122
+ # Remove Hash arguments and then evaluate whether the attributes (the
123
+ # keys of the hash) should also get pushed into the collection.
124
+ only.delete_if do |obj|
125
+ obj.is_a?(Hash) &&
126
+ obj.each { |attr, condition|
127
+ only << attr if condition.respond_to?(:call) && condition.call(@record)
128
+ }
124
129
  end
130
+ only
125
131
  end
126
132
 
127
133
  # An attributed is "ignored" if it is listed in the `:ignore` option
@@ -134,6 +140,18 @@ module PaperTrail
134
140
  ignored.any? && (changed_in_latest_version & ignored).any?
135
141
  end
136
142
 
143
+ # Rails 5.1 changed the API of `ActiveRecord::Dirty`. See
144
+ # https://github.com/paper-trail-gem/paper_trail/pull/899
145
+ #
146
+ # @api private
147
+ def load_changes_in_latest_version
148
+ if @in_after_callback
149
+ @record.saved_changes
150
+ else
151
+ @record.changes
152
+ end
153
+ end
154
+
137
155
  # PT 10 has a new optional column, `item_subtype`
138
156
  #
139
157
  # @api private
@@ -178,20 +196,28 @@ module PaperTrail
178
196
  if value.respond_to?(:call)
179
197
  value.call(@record)
180
198
  elsif value.is_a?(Symbol) && @record.respond_to?(value, true)
181
- # If it is an attribute that is changing in an existing object,
182
- # be sure to grab the current version.
183
- if event != "create" &&
184
- @record.has_attribute?(value) &&
185
- attribute_changed_in_latest_version?(value)
186
- attribute_in_previous_version(value, false)
187
- else
188
- @record.send(value)
189
- end
199
+ metadatum_from_model_method(event, value)
190
200
  else
191
201
  value
192
202
  end
193
203
  end
194
204
 
205
+ # The model method can either be an attribute or a non-attribute method.
206
+ #
207
+ # If it is an attribute that is changing in an existing object,
208
+ # be sure to grab the correct version.
209
+ #
210
+ # @api private
211
+ def metadatum_from_model_method(event, method)
212
+ if event != "create" &&
213
+ @record.has_attribute?(method) &&
214
+ attribute_changed_in_latest_version?(method)
215
+ attribute_in_previous_version(method, false)
216
+ else
217
+ @record.send(method)
218
+ end
219
+ end
220
+
195
221
  # @api private
196
222
  def notable_changes
197
223
  changes_in_latest_version.delete_if { |k, _v|
@@ -203,16 +229,9 @@ module PaperTrail
203
229
  def notably_changed
204
230
  # Memoized to reduce memory usage
205
231
  @notably_changed ||= begin
206
- only = @record.paper_trail_options[:only].dup
207
- # Remove Hash arguments and then evaluate whether the attributes (the
208
- # keys of the hash) should also get pushed into the collection.
209
- only.delete_if do |obj|
210
- obj.is_a?(Hash) &&
211
- obj.each { |attr, condition|
212
- only << attr if condition.respond_to?(:call) && condition.call(@record)
213
- }
214
- end
215
- only.empty? ? changed_and_not_ignored : (changed_and_not_ignored & only)
232
+ only = evaluate_only
233
+ cani = changed_and_not_ignored
234
+ only.empty? ? cani : (cani & only)
216
235
  end
217
236
  end
218
237
 
@@ -35,16 +35,35 @@ module PaperTrail
35
35
  if record_object?
36
36
  data[:object] = recordable_object(@is_touch)
37
37
  end
38
- if record_object_changes?
39
- changes = @force_changes.nil? ? notable_changes : @force_changes
40
- data[:object_changes] = prepare_object_changes(changes)
41
- end
38
+ merge_object_changes_into(data)
42
39
  merge_item_subtype_into(data)
43
40
  merge_metadata_into(data)
44
41
  end
45
42
 
43
+ # If it is a touch event, and changed are empty, it is assumed to be
44
+ # implicit `touch` mutation, and will a version is created.
45
+ #
46
+ # See https://github.com/rails/rails/commit/dcb825902d79d0f6baba956f7c6ec5767611353e
47
+ #
48
+ # @api private
49
+ def changed_notably?
50
+ if @is_touch && changes_in_latest_version.empty?
51
+ true
52
+ else
53
+ super
54
+ end
55
+ end
56
+
46
57
  private
47
58
 
59
+ # @api private
60
+ def merge_object_changes_into(data)
61
+ if record_object_changes?
62
+ changes = @force_changes.nil? ? notable_changes : @force_changes
63
+ data[:object_changes] = prepare_object_changes(changes)
64
+ end
65
+ end
66
+
48
67
  # `touch` cannot record `object_changes` because rails' `touch` does not
49
68
  # perform dirty-tracking. Specifically, methods from `Dirty`, like
50
69
  # `saved_changes`, return the same values before and after `touch`.
@@ -18,11 +18,6 @@ module PaperTrail
18
18
  `abstract_class`. This is fine, but all application models must be
19
19
  configured to use concrete (not abstract) version models.
20
20
  STR
21
- E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE = <<~STR.squish.freeze
22
- To use PaperTrail's per-model limit in your %s model, you must have an
23
- item_subtype column in your versions table. See documentation sections
24
- 2.e.1 Per-model limit, and 4.b.1 The optional item_subtype column.
25
- STR
26
21
  DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION = <<~STR.squish
27
22
  Passing versions association name as `has_paper_trail versions: %{versions_name}`
28
23
  is deprecated. Use `has_paper_trail versions: {name: %{versions_name}}` instead.
@@ -45,22 +40,14 @@ module PaperTrail
45
40
  @model_class.after_create { |r|
46
41
  r.paper_trail.record_create if r.paper_trail.save_version?
47
42
  }
48
- return if @model_class.paper_trail_options[:on].include?(:create)
49
- @model_class.paper_trail_options[:on] << :create
43
+ append_option_uniquely(:on, :create)
50
44
  end
51
45
 
52
46
  # Adds a callback that records a version before or after a "destroy" event.
53
47
  #
54
48
  # @api public
55
49
  def on_destroy(recording_order = "before")
56
- unless %w[after before].include?(recording_order.to_s)
57
- raise ArgumentError, 'recording order can only be "after" or "before"'
58
- end
59
-
60
- if recording_order.to_s == "after" && cannot_record_after_destroy?
61
- raise E_CANNOT_RECORD_AFTER_DESTROY
62
- end
63
-
50
+ assert_valid_recording_order_for_on_destroy(recording_order)
64
51
  @model_class.send(
65
52
  "#{recording_order}_destroy",
66
53
  lambda do |r|
@@ -68,9 +55,7 @@ module PaperTrail
68
55
  r.paper_trail.record_destroy(recording_order)
69
56
  end
70
57
  )
71
-
72
- return if @model_class.paper_trail_options[:on].include?(:destroy)
73
- @model_class.paper_trail_options[:on] << :destroy
58
+ append_option_uniquely(:on, :destroy)
74
59
  end
75
60
 
76
61
  # Adds a callback that records a version after an "update" event.
@@ -92,19 +77,27 @@ module PaperTrail
92
77
  @model_class.after_update { |r|
93
78
  r.paper_trail.clear_version_instance
94
79
  }
95
- return if @model_class.paper_trail_options[:on].include?(:update)
96
- @model_class.paper_trail_options[:on] << :update
80
+ append_option_uniquely(:on, :update)
97
81
  end
98
82
 
99
83
  # Adds a callback that records a version after a "touch" event.
84
+ #
85
+ # Rails < 6.0 has a bug where dirty-tracking does not occur during
86
+ # a `touch`. (https://github.com/rails/rails/issues/33429) See also:
87
+ # https://github.com/paper-trail-gem/paper_trail/issues/1121
88
+ # https://github.com/paper-trail-gem/paper_trail/issues/1161
89
+ # https://github.com/paper-trail-gem/paper_trail/pull/1285
90
+ #
100
91
  # @api public
101
92
  def on_touch
102
93
  @model_class.after_touch { |r|
103
- r.paper_trail.record_update(
104
- force: true,
105
- in_after_callback: true,
106
- is_touch: true
107
- )
94
+ if r.paper_trail.save_version?
95
+ r.paper_trail.record_update(
96
+ force: RAILS_LT_6_0,
97
+ in_after_callback: true,
98
+ is_touch: true
99
+ )
100
+ end
108
101
  }
109
102
  end
110
103
 
@@ -117,7 +110,6 @@ module PaperTrail
117
110
  @model_class.send :include, ::PaperTrail::Model::InstanceMethods
118
111
  setup_options(options)
119
112
  setup_associations(options)
120
- check_presence_of_item_subtype_column(options)
121
113
  @model_class.after_rollback { paper_trail.clear_rolled_back_versions }
122
114
  setup_callbacks_from_options options[:on]
123
115
  end
@@ -129,26 +121,37 @@ module PaperTrail
129
121
 
130
122
  private
131
123
 
124
+ RAILS_LT_6_0 = ::ActiveRecord.gem_version < ::Gem::Version.new("6.0.0")
125
+ private_constant :RAILS_LT_6_0
126
+
127
+ # @api private
128
+ def append_option_uniquely(option, value)
129
+ collection = @model_class.paper_trail_options.fetch(option)
130
+ return if collection.include?(value)
131
+ collection << value
132
+ end
133
+
132
134
  # Raises an error if the provided class is an `abstract_class`.
133
135
  # @api private
134
136
  def assert_concrete_activerecord_class(class_name)
135
137
  if class_name.constantize.abstract_class?
136
- raise format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
138
+ raise Error, format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
137
139
  end
138
140
  end
139
141
 
140
- def cannot_record_after_destroy?
141
- ::ActiveRecord::Base.belongs_to_required_by_default
142
+ # @api private
143
+ def assert_valid_recording_order_for_on_destroy(recording_order)
144
+ unless %w[after before].include?(recording_order.to_s)
145
+ raise ArgumentError, 'recording order can only be "after" or "before"'
146
+ end
147
+
148
+ if recording_order.to_s == "after" && cannot_record_after_destroy?
149
+ raise Error, E_CANNOT_RECORD_AFTER_DESTROY
150
+ end
142
151
  end
143
152
 
144
- # Some options require the presence of the `item_subtype` column. Currently
145
- # only `limit`, but in the future there may be others.
146
- #
147
- # @api private
148
- def check_presence_of_item_subtype_column(options)
149
- return unless options.key?(:limit)
150
- return if version_class.item_subtype_column_present?
151
- raise format(E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE, @model_class.name)
153
+ def cannot_record_after_destroy?
154
+ ::ActiveRecord::Base.belongs_to_required_by_default
152
155
  end
153
156
 
154
157
  def check_version_class_name(options)
@@ -206,6 +209,14 @@ module PaperTrail
206
209
  options
207
210
  end
208
211
 
212
+ # Process an `ignore`, `skip`, or `only` option.
213
+ def event_attribute_option(option_name)
214
+ [@model_class.paper_trail_options[option_name]].
215
+ flatten.
216
+ compact.
217
+ map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
218
+ end
219
+
209
220
  def get_versions_scope(options)
210
221
  options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
211
222
  end
@@ -240,12 +251,8 @@ module PaperTrail
240
251
  @model_class.paper_trail_options = options.dup
241
252
 
242
253
  %i[ignore skip only].each do |k|
243
- @model_class.paper_trail_options[k] = [@model_class.paper_trail_options[k]].
244
- flatten.
245
- compact.
246
- map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
254
+ @model_class.paper_trail_options[k] = event_attribute_option(k)
247
255
  end
248
-
249
256
  @model_class.paper_trail_options[:meta] ||= {}
250
257
  end
251
258
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Queries
5
+ module Versions
6
+ # For public API documentation, see `where_attribute_changes` in
7
+ # `paper_trail/version_concern.rb`.
8
+ # @api private
9
+ class WhereAttributeChanges
10
+ # - version_model_class - The class that VersionConcern was mixed into.
11
+ # - attribute - An attribute that changed. See the public API
12
+ # documentation for details.
13
+ # @api private
14
+ def initialize(version_model_class, attribute)
15
+ @version_model_class = version_model_class
16
+ @attribute = attribute
17
+ end
18
+
19
+ # @api private
20
+ def execute
21
+ if PaperTrail.config.object_changes_adapter.respond_to?(:where_attribute_changes)
22
+ return PaperTrail.config.object_changes_adapter.where_attribute_changes(
23
+ @version_model_class, @attribute
24
+ )
25
+ end
26
+ column_type = @version_model_class.columns_hash["object_changes"].type
27
+ case column_type
28
+ when :jsonb, :json
29
+ json
30
+ else
31
+ raise UnsupportedColumnType.new(
32
+ method: "where_attribute_changes",
33
+ expected: "json or jsonb",
34
+ actual: column_type
35
+ )
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @api private
42
+ def json
43
+ sql = "object_changes -> ? IS NOT NULL"
44
+
45
+ @version_model_class.where(sql, @attribute)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -19,7 +19,7 @@ module PaperTrail
19
19
  # @api private
20
20
  def execute
21
21
  column = @version_model_class.columns_hash["object"]
22
- raise "where_object can't be called without an object column" unless column
22
+ raise Error, "where_object requires an object column" unless column
23
23
 
24
24
  case column.type
25
25
  when :jsonb
@@ -28,13 +28,18 @@ module PaperTrail
28
28
  @version_model_class, @attributes
29
29
  )
30
30
  end
31
- case @version_model_class.columns_hash["object_changes"].type
31
+ column_type = @version_model_class.columns_hash["object_changes"].type
32
+ case column_type
32
33
  when :jsonb
33
34
  jsonb
34
35
  when :json
35
36
  json
36
37
  else
37
- text
38
+ raise UnsupportedColumnType.new(
39
+ method: "where_object_changes",
40
+ expected: "json or jsonb",
41
+ actual: column_type
42
+ )
38
43
  end
39
44
  end
40
45
 
@@ -59,16 +64,6 @@ module PaperTrail
59
64
  @attributes.each { |field, value| @attributes[field] = [value] }
60
65
  @version_model_class.where("object_changes @> ?", @attributes.to_json)
61
66
  end
62
-
63
- # @api private
64
- def text
65
- arel_field = @version_model_class.arel_table[:object_changes]
66
- where_conditions = @attributes.map { |field, value|
67
- ::PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
68
- }
69
- where_conditions = where_conditions.reduce { |a, e| a.and(e) }
70
- @version_model_class.where(where_conditions)
71
- end
72
67
  end
73
68
  end
74
69
  end
@@ -23,12 +23,16 @@ module PaperTrail
23
23
  @version_model_class, @attributes
24
24
  )
25
25
  end
26
-
27
- case @version_model_class.columns_hash["object_changes"].type
26
+ column_type = @version_model_class.columns_hash["object_changes"].type
27
+ case column_type
28
28
  when :jsonb, :json
29
29
  json
30
30
  else
31
- text
31
+ raise UnsupportedColumnType.new(
32
+ method: "where_object_changes_from",
33
+ expected: "json or jsonb",
34
+ actual: column_type
35
+ )
32
36
  end
33
37
  end
34
38
 
@@ -47,18 +51,6 @@ module PaperTrail
47
51
  sql = predicates.join(" and ")
48
52
  @version_model_class.where(sql, *values)
49
53
  end
50
-
51
- # @api private
52
- def text
53
- arel_field = @version_model_class.arel_table[:object_changes]
54
-
55
- where_conditions = @attributes.map do |field, value|
56
- ::PaperTrail.serializer.where_object_changes_from_condition(arel_field, field, value)
57
- end
58
-
59
- where_conditions = where_conditions.reduce { |a, e| a.and(e) }
60
- @version_model_class.where(where_conditions)
61
- end
62
54
  end
63
55
  end
64
56
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaperTrail
4
+ module Queries
5
+ module Versions
6
+ # For public API documentation, see `where_object_changes_to` in
7
+ # `paper_trail/version_concern.rb`.
8
+ # @api private
9
+ class WhereObjectChangesTo
10
+ # - version_model_class - The class that VersionConcern was mixed into.
11
+ # - attributes - A `Hash` of attributes and values. See the public API
12
+ # documentation for details.
13
+ # @api private
14
+ def initialize(version_model_class, attributes)
15
+ @version_model_class = version_model_class
16
+ @attributes = attributes
17
+ end
18
+
19
+ # @api private
20
+ def execute
21
+ if PaperTrail.config.object_changes_adapter.respond_to?(:where_object_changes_to)
22
+ return PaperTrail.config.object_changes_adapter.where_object_changes_to(
23
+ @version_model_class, @attributes
24
+ )
25
+ end
26
+ column_type = @version_model_class.columns_hash["object_changes"].type
27
+ case column_type
28
+ when :jsonb, :json
29
+ json
30
+ else
31
+ raise UnsupportedColumnType.new(
32
+ method: "where_object_changes_to",
33
+ expected: "json or jsonb",
34
+ actual: column_type
35
+ )
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @api private
42
+ def json
43
+ predicates = []
44
+ values = []
45
+ @attributes.each do |field, value|
46
+ predicates.push(
47
+ "(object_changes->>? ILIKE ?)"
48
+ )
49
+ values.concat([field, "[%#{value.to_json}]"])
50
+ end
51
+ sql = predicates.join(" and ")
52
+ @version_model_class.where(sql, *values)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -194,15 +194,17 @@ module PaperTrail
194
194
  # Save, and create a version record regardless of options such as `:on`,
195
195
  # `:if`, or `:unless`.
196
196
  #
197
- # Arguments are passed to `save`.
197
+ # `in_after_callback`: Indicates if this method is being called within an
198
+ # `after` callback. Defaults to `false`.
199
+ # `options`: Optional arguments passed to `save`.
198
200
  #
199
201
  # This is an "update" event. That is, we record the same data we would in
200
202
  # the case of a normal AR `update`.
201
- def save_with_version(**options)
203
+ def save_with_version(in_after_callback: false, **options)
202
204
  ::PaperTrail.request(enabled: false) do
203
205
  @record.save(**options)
204
206
  end
205
- record_update(force: true, in_after_callback: false, is_touch: false)
207
+ record_update(force: true, in_after_callback: in_after_callback, is_touch: false)
206
208
  end
207
209
 
208
210
  # Like the `update_column` method from `ActiveRecord::Persistence`, but also
@@ -283,7 +285,7 @@ module PaperTrail
283
285
  def log_version_errors(version, action)
284
286
  version.logger&.warn(
285
287
  "Unable to create version for #{action} of #{@record.class.name}" \
286
- "##{@record.id}: " + version.errors.full_messages.join(", ")
288
+ "##{@record.id}: " + version.errors.full_messages.join(", ")
287
289
  )
288
290
  end
289
291
 
@@ -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`.
@@ -31,24 +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
-
45
- # Raises an exception as this operation is not allowed from text columns.
46
- def where_object_changes_from_condition(*)
47
- raise <<-STR.squish.freeze
48
- where_object_changes_from does not support reading JSON from a text
49
- column. The json and jsonb datatypes are supported.
50
- STR
51
- end
52
34
  end
53
35
  end
54
36
  end
@@ -9,7 +9,7 @@ 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
+ ::YAML.respond_to?(:unsafe_load) ? ::YAML.unsafe_load(string) : ::YAML.load(string)
13
13
  end
14
14
 
15
15
  # @param object (Hash | HashWithIndifferentAccess) - Coming from
@@ -26,26 +26,6 @@ module PaperTrail
26
26
  def where_object_condition(arel_field, field, value)
27
27
  arel_field.matches("%\n#{field}: #{value}\n%")
28
28
  end
29
-
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
- end
41
-
42
- # Raises an exception as this operation is not allowed with YAML.
43
- def where_object_changes_from_condition(*)
44
- raise <<-STR.squish.freeze
45
- where_object_changes_from does not support reading YAML from a text
46
- column. The json and jsonb datatypes are supported.
47
- STR
48
- end
49
29
  end
50
30
  end
51
31
  end
@@ -1,9 +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"
6
7
  require "paper_trail/queries/versions/where_object_changes_from"
8
+ require "paper_trail/queries/versions/where_object_changes_to"
7
9
 
8
10
  module PaperTrail
9
11
  # Originally, PaperTrail did not provide this module, and all of this
@@ -14,17 +16,13 @@ module PaperTrail
14
16
  extend ::ActiveSupport::Concern
15
17
 
16
18
  included do
17
- belongs_to :item, polymorphic: true, optional: true
19
+ belongs_to :item, polymorphic: true, optional: true, inverse_of: false
18
20
  validates_presence_of :event
19
21
  after_create :enforce_version_limit!
20
22
  end
21
23
 
22
24
  # :nodoc:
23
25
  module ClassMethods
24
- def item_subtype_column_present?
25
- column_names.include?("item_subtype")
26
- end
27
-
28
26
  def with_item_keys(item_type, item_id)
29
27
  where item_type: item_type, item_id: item_id
30
28
  end
@@ -42,7 +40,7 @@ module PaperTrail
42
40
  end
43
41
 
44
42
  def not_creates
45
- where "event <> ?", "create"
43
+ where.not(event: "create")
46
44
  end
47
45
 
48
46
  def between(start_time, end_time)
@@ -60,6 +58,18 @@ module PaperTrail
60
58
  end
61
59
  end
62
60
 
61
+ # Given an attribute like `"name"`, query the `versions.object_changes`
62
+ # column for any changes that modified the provided attribute.
63
+ #
64
+ # @api public
65
+ def where_attribute_changes(attribute)
66
+ unless attribute.is_a?(String) || attribute.is_a?(Symbol)
67
+ raise ArgumentError, "expected to receive a String or Symbol"
68
+ end
69
+
70
+ Queries::Versions::WhereAttributeChanges.new(self, attribute).execute
71
+ end
72
+
63
73
  # Given a hash of attributes like `name: 'Joan'`, query the
64
74
  # `versions.objects` column.
65
75
  #
@@ -131,6 +141,21 @@ module PaperTrail
131
141
  Queries::Versions::WhereObjectChangesFrom.new(self, args).execute
132
142
  end
133
143
 
144
+ # Given a hash of attributes like `name: 'Joan'`, query the
145
+ # `versions.objects_changes` column for changes where the version changed
146
+ # to the hash of attributes from other values.
147
+ #
148
+ # This is useful for finding versions where the attribute started with an
149
+ # unknown value and changed to a known value. This is in comparison to
150
+ # `where_object_changes` which will find both the changes before and
151
+ # after.
152
+ #
153
+ # @api public
154
+ def where_object_changes_to(args = {})
155
+ raise ArgumentError, "expected to receive a Hash" unless args.is_a?(Hash)
156
+ Queries::Versions::WhereObjectChangesTo.new(self, args).execute
157
+ end
158
+
134
159
  def primary_key_is_int?
135
160
  @primary_key_is_int ||= columns_hash[primary_key].type == :integer
136
161
  rescue StandardError # TODO: Rescue something more specific
@@ -237,7 +262,7 @@ module PaperTrail
237
262
  #
238
263
  def reify(options = {})
239
264
  unless self.class.column_names.include? "object"
240
- raise "reify can't be called without an object column"
265
+ raise Error, "reify requires an object column"
241
266
  end
242
267
  return nil if object.nil?
243
268
  ::PaperTrail::Reifier.reify(self, options)
@@ -350,16 +375,23 @@ module PaperTrail
350
375
  # The version limit can be global or per-model.
351
376
  #
352
377
  # @api private
353
- #
354
- # TODO: Duplication: similar `constantize` in Reifier#version_reification_class
355
378
  def version_limit
356
- if self.class.item_subtype_column_present?
357
- klass = (item_subtype || item_type).constantize
358
- if klass&.paper_trail_options&.key?(:limit)
359
- return klass.paper_trail_options[:limit]
360
- end
379
+ klass = item.class
380
+ if limit_option?(klass)
381
+ klass.paper_trail_options[:limit]
382
+ elsif base_class_limit_option?(klass)
383
+ klass.base_class.paper_trail_options[:limit]
384
+ else
385
+ PaperTrail.config.version_limit
361
386
  end
362
- PaperTrail.config.version_limit
387
+ end
388
+
389
+ def limit_option?(klass)
390
+ klass.respond_to?(:paper_trail_options) && klass.paper_trail_options.key?(:limit)
391
+ end
392
+
393
+ def base_class_limit_option?(klass)
394
+ klass.respond_to?(:base_class) && limit_option?(klass.base_class)
363
395
  end
364
396
  end
365
397
  end
@@ -8,7 +8,7 @@ module PaperTrail
8
8
  # People are encouraged to use `PaperTrail.gem_version` instead.
9
9
  module VERSION
10
10
  MAJOR = 12
11
- MINOR = 0
11
+ MINOR = 3
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,6 +8,7 @@
8
8
  # can revisit this decision.
9
9
  require "active_support/all"
10
10
 
11
+ require "paper_trail/errors"
11
12
  require "paper_trail/cleaner"
12
13
  require "paper_trail/compatibility"
13
14
  require "paper_trail/config"
@@ -25,6 +26,8 @@ module PaperTrail
25
26
  named created_at.
26
27
  EOS
27
28
 
29
+ RAILS_GTE_7_0 = ::ActiveRecord.gem_version >= ::Gem::Version.new("7.0.0")
30
+
28
31
  extend PaperTrail::Cleaner
29
32
 
30
33
  class << self
@@ -68,7 +71,7 @@ module PaperTrail
68
71
  #
69
72
  # @api public
70
73
  def request(options = nil, &block)
71
- if options.nil? && !block_given?
74
+ if options.nil? && !block
72
75
  Request
73
76
  else
74
77
  Request.with(options, &block)
@@ -78,7 +81,7 @@ module PaperTrail
78
81
  # Set the field which records when a version was created.
79
82
  # @api public
80
83
  def timestamp_field=(_field_name)
81
- raise(E_TIMESTAMP_FIELD_CONFIG)
84
+ raise Error, E_TIMESTAMP_FIELD_CONFIG
82
85
  end
83
86
 
84
87
  # Set the PaperTrail serializer. This setting affects all threads.
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: 12.0.0
4
+ version: 12.3.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: 2021-03-29 00:00:00.000000000 Z
13
+ date: 2022-03-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -46,42 +46,42 @@ dependencies:
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '2.2'
49
+ version: 2.4.1
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: '2.2'
56
+ version: 2.4.1
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: byebug
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '11.0'
63
+ version: '11.1'
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '11.0'
70
+ version: '11.1'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: ffaker
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - "~>"
76
76
  - !ruby/object:Gem::Version
77
- version: '2.11'
77
+ version: '2.20'
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - "~>"
83
83
  - !ruby/object:Gem::Version
84
- version: '2.11'
84
+ version: '2.20'
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: generator_spec
87
87
  requirement: !ruby/object:Gem::Requirement
@@ -102,14 +102,14 @@ dependencies:
102
102
  requirements:
103
103
  - - "~>"
104
104
  - !ruby/object:Gem::Version
105
- version: 0.9.14
105
+ version: 1.0.0
106
106
  type: :development
107
107
  prerelease: false
108
108
  version_requirements: !ruby/object:Gem::Requirement
109
109
  requirements:
110
110
  - - "~>"
111
111
  - !ruby/object:Gem::Version
112
- version: 0.9.14
112
+ version: 1.0.0
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rails
115
115
  requirement: !ruby/object:Gem::Requirement
@@ -144,118 +144,140 @@ dependencies:
144
144
  requirements:
145
145
  - - "~>"
146
146
  - !ruby/object:Gem::Version
147
- version: '4.0'
147
+ version: 5.0.2
148
148
  type: :development
149
149
  prerelease: false
150
150
  version_requirements: !ruby/object:Gem::Requirement
151
151
  requirements:
152
152
  - - "~>"
153
153
  - !ruby/object:Gem::Version
154
- version: '4.0'
154
+ version: 5.0.2
155
155
  - !ruby/object:Gem::Dependency
156
156
  name: rubocop
157
157
  requirement: !ruby/object:Gem::Requirement
158
158
  requirements:
159
159
  - - "~>"
160
160
  - !ruby/object:Gem::Version
161
- version: 1.11.0
161
+ version: 1.22.2
162
162
  type: :development
163
163
  prerelease: false
164
164
  version_requirements: !ruby/object:Gem::Requirement
165
165
  requirements:
166
166
  - - "~>"
167
167
  - !ruby/object:Gem::Version
168
- version: 1.11.0
168
+ version: 1.22.2
169
169
  - !ruby/object:Gem::Dependency
170
- name: rubocop-rails
170
+ name: rubocop-packaging
171
171
  requirement: !ruby/object:Gem::Requirement
172
172
  requirements:
173
173
  - - "~>"
174
174
  - !ruby/object:Gem::Version
175
- version: 2.9.1
175
+ version: 0.5.1
176
176
  type: :development
177
177
  prerelease: false
178
178
  version_requirements: !ruby/object:Gem::Requirement
179
179
  requirements:
180
180
  - - "~>"
181
181
  - !ruby/object:Gem::Version
182
- version: 2.9.1
182
+ version: 0.5.1
183
183
  - !ruby/object:Gem::Dependency
184
- name: rubocop-packaging
184
+ name: rubocop-performance
185
185
  requirement: !ruby/object:Gem::Requirement
186
186
  requirements:
187
187
  - - "~>"
188
188
  - !ruby/object:Gem::Version
189
- version: 0.5.1
189
+ version: 1.11.5
190
190
  type: :development
191
191
  prerelease: false
192
192
  version_requirements: !ruby/object:Gem::Requirement
193
193
  requirements:
194
194
  - - "~>"
195
195
  - !ruby/object:Gem::Version
196
- version: 0.5.1
196
+ version: 1.11.5
197
197
  - !ruby/object:Gem::Dependency
198
- name: rubocop-performance
198
+ name: rubocop-rails
199
+ requirement: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - "~>"
202
+ - !ruby/object:Gem::Version
203
+ version: 2.12.4
204
+ type: :development
205
+ prerelease: false
206
+ version_requirements: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - "~>"
209
+ - !ruby/object:Gem::Version
210
+ version: 2.12.4
211
+ - !ruby/object:Gem::Dependency
212
+ name: rubocop-rake
199
213
  requirement: !ruby/object:Gem::Requirement
200
214
  requirements:
201
215
  - - "~>"
202
216
  - !ruby/object:Gem::Version
203
- version: 1.10.1
217
+ version: 0.6.0
204
218
  type: :development
205
219
  prerelease: false
206
220
  version_requirements: !ruby/object:Gem::Requirement
207
221
  requirements:
208
222
  - - "~>"
209
223
  - !ruby/object:Gem::Version
210
- version: 1.10.1
224
+ version: 0.6.0
211
225
  - !ruby/object:Gem::Dependency
212
226
  name: rubocop-rspec
213
227
  requirement: !ruby/object:Gem::Requirement
214
228
  requirements:
215
229
  - - "~>"
216
230
  - !ruby/object:Gem::Version
217
- version: 2.2.0
231
+ version: 2.5.0
218
232
  type: :development
219
233
  prerelease: false
220
234
  version_requirements: !ruby/object:Gem::Requirement
221
235
  requirements:
222
236
  - - "~>"
223
237
  - !ruby/object:Gem::Version
224
- version: 2.2.0
238
+ version: 2.5.0
239
+ - !ruby/object:Gem::Dependency
240
+ name: simplecov
241
+ requirement: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - "~>"
244
+ - !ruby/object:Gem::Version
245
+ version: 0.21.2
246
+ type: :development
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - "~>"
251
+ - !ruby/object:Gem::Version
252
+ version: 0.21.2
225
253
  - !ruby/object:Gem::Dependency
226
254
  name: mysql2
227
255
  requirement: !ruby/object:Gem::Requirement
228
256
  requirements:
229
257
  - - "~>"
230
258
  - !ruby/object:Gem::Version
231
- version: '0.5'
259
+ version: 0.5.3
232
260
  type: :development
233
261
  prerelease: false
234
262
  version_requirements: !ruby/object:Gem::Requirement
235
263
  requirements:
236
264
  - - "~>"
237
265
  - !ruby/object:Gem::Version
238
- version: '0.5'
266
+ version: 0.5.3
239
267
  - !ruby/object:Gem::Dependency
240
268
  name: pg
241
269
  requirement: !ruby/object:Gem::Requirement
242
270
  requirements:
243
- - - ">="
244
- - !ruby/object:Gem::Version
245
- version: '0.18'
246
- - - "<"
271
+ - - "~>"
247
272
  - !ruby/object:Gem::Version
248
- version: '2.0'
273
+ version: '1.2'
249
274
  type: :development
250
275
  prerelease: false
251
276
  version_requirements: !ruby/object:Gem::Requirement
252
277
  requirements:
253
- - - ">="
254
- - !ruby/object:Gem::Version
255
- version: '0.18'
256
- - - "<"
278
+ - - "~>"
257
279
  - !ruby/object:Gem::Version
258
- version: '2.0'
280
+ version: '1.2'
259
281
  - !ruby/object:Gem::Dependency
260
282
  name: sqlite3
261
283
  requirement: !ruby/object:Gem::Requirement
@@ -297,6 +319,7 @@ files:
297
319
  - lib/paper_trail/cleaner.rb
298
320
  - lib/paper_trail/compatibility.rb
299
321
  - lib/paper_trail/config.rb
322
+ - lib/paper_trail/errors.rb
300
323
  - lib/paper_trail/events/base.rb
301
324
  - lib/paper_trail/events/create.rb
302
325
  - lib/paper_trail/events/destroy.rb
@@ -311,9 +334,11 @@ files:
311
334
  - lib/paper_trail/frameworks/rspec/helpers.rb
312
335
  - lib/paper_trail/has_paper_trail.rb
313
336
  - lib/paper_trail/model_config.rb
337
+ - lib/paper_trail/queries/versions/where_attribute_changes.rb
314
338
  - lib/paper_trail/queries/versions/where_object.rb
315
339
  - lib/paper_trail/queries/versions/where_object_changes.rb
316
340
  - lib/paper_trail/queries/versions/where_object_changes_from.rb
341
+ - lib/paper_trail/queries/versions/where_object_changes_to.rb
317
342
  - lib/paper_trail/record_history.rb
318
343
  - lib/paper_trail/record_trail.rb
319
344
  - lib/paper_trail/reifier.rb
@@ -335,15 +360,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
335
360
  requirements:
336
361
  - - ">="
337
362
  - !ruby/object:Gem::Version
338
- version: 2.5.0
363
+ version: 2.6.0
339
364
  required_rubygems_version: !ruby/object:Gem::Requirement
340
365
  requirements:
341
366
  - - ">="
342
367
  - !ruby/object:Gem::Version
343
368
  version: 1.3.6
344
369
  requirements: []
345
- rubyforge_project:
346
- rubygems_version: 2.7.6.2
370
+ rubygems_version: 3.2.22
347
371
  signing_key:
348
372
  specification_version: 4
349
373
  summary: Track changes to your models.