Skip to content

Commit 619468d

Browse files
committed
Avoid N+1 queries coming from #version_limit when destroying records
Fixes #1510
1 parent e1a4cde commit 619468d

File tree

6 files changed

+38
-2
lines changed

6 files changed

+38
-2
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
1515

1616
### Fixed
1717

18-
- None
18+
- [#1511](https://github.com/paper-trail-gem/paper_trail/pull/1511) - Avoid N+1
19+
queries when destroying a record with a `has_many` `dependent: :destroy`
20+
association where the `has_many` target model is tracked by PaperTrail.
1921

2022
### Dependencies
2123

lib/paper_trail/version_concern.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ def sibling_versions
385385
#
386386
# @api private
387387
def version_limit
388-
klass = item.class
388+
klass = ((respond_to?(:item_subtype) && item_subtype) || item_type).constantize
389389
if limit_option?(klass)
390390
klass.paper_trail_options[:limit]
391391
elsif base_class_limit_option?(klass)

paper_trail.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ has been destroyed.
7070
s.add_development_dependency "ffaker", "~> 2.20"
7171
s.add_development_dependency "generator_spec", "~> 0.9.4"
7272
s.add_development_dependency "memory_profiler", "~> 1.0.0"
73+
s.add_development_dependency "n_plus_one_control", "~> 0.7.2"
7374

7475
# For `spec/dummy_app`. Technically, we only need `actionpack` (as of 2020).
7576
# However, that might change in the future, and the advantages of specifying a

spec/dummy_app/app/versions/post_version.rb

+15
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,19 @@
22

33
class PostVersion < PaperTrail::Version
44
self.table_name = "post_versions"
5+
6+
# BEGIN Hack to simulate a version class that doesn't have the optional `item_subtype` column. >>>
7+
def item_subtype
8+
raise "This method should not be called!"
9+
end
10+
11+
# We will follow Ruby's `respond_to?` method signature, including an optional boolean parameter.
12+
# rubocop:disable Style/OptionalBooleanParameter
13+
def respond_to?(method_name, include_private = false)
14+
# rubocop:enable Style/OptionalBooleanParameter
15+
return false if method_name.to_sym == :item_subtype
16+
17+
super
18+
end
19+
# <<< END Hack to simulate a version class that doesn't have the optional `item_subtype` column.
520
end

spec/models/order_spec.rb

+17
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,22 @@
1111

1212
expect(customer.versions.count).to(eq(3))
1313
end
14+
15+
# We need to use an instance variable to work with the `n_plus_one_control` gem in this spec.
16+
# rubocop:disable RSpec/InstanceVariable
17+
context "when the record has many associated records (N+1 check)", :n_plus_one do
18+
populate do |n|
19+
@customer = Customer.create!
20+
21+
n.times do
22+
@customer.orders.create!
23+
end
24+
end
25+
26+
it "does not cause an N+1 query" do
27+
expect { @customer.destroy! }.to perform_constant_number_of_queries
28+
end
29+
end
30+
# rubocop:enable RSpec/InstanceVariable
1431
end
1532
end

spec/spec_helper.rb

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
# by the `Bundler.require` in `config/application.rb`.
7070
require "paper_trail"
7171
require "ffaker"
72+
require "n_plus_one_control/rspec"
7273
require "rspec/rails"
7374
require "rails-controller-testing"
7475

0 commit comments

Comments
 (0)