n_plus_one_control 0.5.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +29 -2
- data/lib/n_plus_one_control.rb +8 -3
- data/lib/n_plus_one_control/minitest.rb +34 -1
- data/lib/n_plus_one_control/rspec.rb +2 -1
- data/lib/n_plus_one_control/rspec/{matcher.rb → matchers/perform_constant_number_of_queries.rb} +1 -1
- data/lib/n_plus_one_control/rspec/matchers/perform_linear_number_of_queries.rb +53 -0
- data/lib/n_plus_one_control/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48ad0338e6227c2b71a09254f3613db415d64fe5b40ee7df55ee1daddc46c0f5
|
4
|
+
data.tar.gz: c55895975614627ea2308dde866079bad496f5ddef81f295267f5b0d8b2670ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4f6b44d7ecccd6e3f53d65e4df916982b144b8381049ebec5659cdf5792cc9c1d1c7fdee22cb8610df155020220286cc2daf78a0dbe8b76fe6bceb778446c81
|
7
|
+
data.tar.gz: 63ac722a3b82c6c3263a79f0065f554352bf4ce2043bdec98f37139f8b21f441e9327bc575ff6e3bdde05e8eac95ce636615e74ed4e3415a0ae28312da43d2a7
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
##
|
1
|
+
## 0.6.0 (2020-11-27)
|
2
|
+
|
3
|
+
- Fix table stats summary when queries use backticks to surround table names ([@andrewhampton][])
|
4
|
+
- Add support to test for linear query. ([@caalberts][])
|
2
5
|
|
3
6
|
## 0.5.0 (2020-09-07)
|
4
7
|
|
@@ -26,3 +29,5 @@ Could be specified via `NPLUSONE_BACKTRACE` env var.
|
|
26
29
|
|
27
30
|
[@Earendil95]: https://github.com/Earendil95
|
28
31
|
[@palkan]: https://github.com/palkan
|
32
|
+
[@caalberts]: https://github.com/caalberts
|
33
|
+
[@andrewhampton]: https://github.com/andrewhampton
|
data/README.md
CHANGED
@@ -85,10 +85,10 @@ Availables modifiers:
|
|
85
85
|
```ruby
|
86
86
|
# You can specify the RegExp to filter queries.
|
87
87
|
# By default, it only considers SELECT queries.
|
88
|
-
expect {
|
88
|
+
expect { get :index }.to perform_constant_number_of_queries.matching(/INSERT/)
|
89
89
|
|
90
90
|
# You can also provide custom scale factors
|
91
|
-
expect {
|
91
|
+
expect { get :index }.to perform_constant_number_of_queries.with_scale_factors(10, 100)
|
92
92
|
```
|
93
93
|
|
94
94
|
#### Using scale factor in spec
|
@@ -111,6 +111,21 @@ context "N+1", :n_plus_one do
|
|
111
111
|
end
|
112
112
|
```
|
113
113
|
|
114
|
+
#### Other available matchers
|
115
|
+
|
116
|
+
`perform_linear_number_of_queries(slope: 1)` allows you to test that a query generates linear number of queries with the given slope.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
context "when has linear query", :n_plus_one do
|
120
|
+
populate { |n| create_list(:post, n) }
|
121
|
+
|
122
|
+
specify do
|
123
|
+
expect { Post.find_each { |p| p.user.name } }
|
124
|
+
.to perform_linear_number_of_queries(slope: 1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
114
129
|
### Minitest
|
115
130
|
|
116
131
|
First, add NPlusOneControl to your `test_helper.rb`:
|
@@ -132,6 +147,18 @@ def test_no_n_plus_one_error
|
|
132
147
|
end
|
133
148
|
```
|
134
149
|
|
150
|
+
You can also use `assert_perform_linear_number_of_queries` to test for linear queries:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
def test_no_n_plus_one_error
|
154
|
+
populate = ->(n) { create_list(:post, n) }
|
155
|
+
|
156
|
+
assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
|
157
|
+
Post.find_each { |p| p.user.name }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
135
162
|
You can also specify custom scale factors or filter patterns:
|
136
163
|
|
137
164
|
```ruby
|
data/lib/n_plus_one_control.rb
CHANGED
@@ -6,7 +6,7 @@ require "n_plus_one_control/executor"
|
|
6
6
|
# RSpec and Minitest matchers to prevent N+1 queries problem.
|
7
7
|
module NPlusOneControl
|
8
8
|
# Used to extract a table name from a query
|
9
|
-
EXTRACT_TABLE_RXP = /(insert into|update|delete from|from) ['"](\S+)['"]/i.freeze
|
9
|
+
EXTRACT_TABLE_RXP = /(insert into|update|delete from|from) ['"`](\S+)['"`]/i.freeze
|
10
10
|
|
11
11
|
# Used to convert a query part extracted by the regexp above to the corresponding
|
12
12
|
# human-friendly type
|
@@ -23,8 +23,13 @@ module NPlusOneControl
|
|
23
23
|
|
24
24
|
attr_reader :default_matching
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
FAILURE_MESSAGES = {
|
27
|
+
constant_queries: "Expected to make the same number of queries",
|
28
|
+
linear_queries: "Expected to make linear number of queries"
|
29
|
+
}
|
30
|
+
|
31
|
+
def failure_message(type, queries) # rubocop:disable Metrics/MethodLength
|
32
|
+
msg = ["#{FAILURE_MESSAGES[type]}, but got:\n"]
|
28
33
|
queries.each do |(scale, data)|
|
29
34
|
msg << " #{data.size} for N=#{scale}\n"
|
30
35
|
end
|
@@ -26,7 +26,30 @@ module NPlusOneControl
|
|
26
26
|
|
27
27
|
counts = queries.map(&:last).map(&:size)
|
28
28
|
|
29
|
-
assert counts.max == counts.min, NPlusOneControl.failure_message(queries)
|
29
|
+
assert counts.max == counts.min, NPlusOneControl.failure_message(:constant_queries, queries)
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_perform_linear_number_of_queries(
|
33
|
+
slope: 1,
|
34
|
+
populate: nil,
|
35
|
+
matching: nil,
|
36
|
+
scale_factors: nil,
|
37
|
+
warmup: nil
|
38
|
+
)
|
39
|
+
|
40
|
+
raise ArgumentError, "Block is required" unless block_given?
|
41
|
+
|
42
|
+
warming_up warmup
|
43
|
+
|
44
|
+
@executor = NPlusOneControl::Executor.new(
|
45
|
+
population: populate || population_method,
|
46
|
+
matching: matching || NPlusOneControl.default_matching,
|
47
|
+
scale_factors: scale_factors || NPlusOneControl.default_scale_factors
|
48
|
+
)
|
49
|
+
|
50
|
+
queries = @executor.call { yield }
|
51
|
+
|
52
|
+
assert linear?(queries, slope: slope), NPlusOneControl.failure_message(:linear_queries, queries)
|
30
53
|
end
|
31
54
|
|
32
55
|
def current_scale
|
@@ -42,6 +65,16 @@ module NPlusOneControl
|
|
42
65
|
def population_method
|
43
66
|
methods.include?(:populate) ? method(:populate) : nil
|
44
67
|
end
|
68
|
+
|
69
|
+
def linear?(queries, slope:)
|
70
|
+
queries.each_cons(2).all? do |pair|
|
71
|
+
scales = pair.map(&:first)
|
72
|
+
query_lists = pair.map(&:last)
|
73
|
+
|
74
|
+
actual_slope = (query_lists[1].size - query_lists[0].size) / (scales[1] - scales[0])
|
75
|
+
actual_slope <= slope
|
76
|
+
end
|
77
|
+
end
|
45
78
|
end
|
46
79
|
end
|
47
80
|
|
@@ -4,7 +4,8 @@ gem "rspec-core", ">= 3.5"
|
|
4
4
|
|
5
5
|
require "n_plus_one_control"
|
6
6
|
require "n_plus_one_control/rspec/dsl"
|
7
|
-
require "n_plus_one_control/rspec/
|
7
|
+
require "n_plus_one_control/rspec/matchers/perform_constant_number_of_queries"
|
8
|
+
require "n_plus_one_control/rspec/matchers/perform_linear_number_of_queries"
|
8
9
|
require "n_plus_one_control/rspec/context"
|
9
10
|
|
10
11
|
module NPlusOneControl
|
data/lib/n_plus_one_control/rspec/{matcher.rb → matchers/perform_constant_number_of_queries.rb}
RENAMED
@@ -46,6 +46,6 @@
|
|
46
46
|
raise "This matcher doesn't support negation"
|
47
47
|
end
|
48
48
|
|
49
|
-
failure_message { |_actual| NPlusOneControl.failure_message(@queries) }
|
49
|
+
failure_message { |_actual| NPlusOneControl.failure_message(:constant_queries, @queries) }
|
50
50
|
end
|
51
51
|
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
4
|
+
::RSpec::Matchers.define :perform_linear_number_of_queries do |slope: 1|
|
5
|
+
supports_block_expectations
|
6
|
+
|
7
|
+
chain :with_scale_factors do |*factors|
|
8
|
+
@factors = factors
|
9
|
+
end
|
10
|
+
|
11
|
+
chain :matching do |pattern|
|
12
|
+
@pattern = pattern
|
13
|
+
end
|
14
|
+
|
15
|
+
chain :with_warming_up do
|
16
|
+
@warmup = true
|
17
|
+
end
|
18
|
+
|
19
|
+
match do |actual, *_args|
|
20
|
+
raise ArgumentError, "Block is required" unless actual.is_a? Proc
|
21
|
+
|
22
|
+
raise "Missing tag :n_plus_one" unless
|
23
|
+
@matcher_execution_context.respond_to?(:n_plus_one_populate)
|
24
|
+
|
25
|
+
populate = @matcher_execution_context.n_plus_one_populate
|
26
|
+
warmup = @warmup ? actual : @matcher_execution_context.n_plus_one_warmup
|
27
|
+
|
28
|
+
warmup.call if warmup.present?
|
29
|
+
|
30
|
+
@matcher_execution_context.executor = NPlusOneControl::Executor.new(
|
31
|
+
population: populate,
|
32
|
+
matching: nil,
|
33
|
+
scale_factors: @factors
|
34
|
+
)
|
35
|
+
|
36
|
+
@queries = @matcher_execution_context.executor.call(&actual)
|
37
|
+
|
38
|
+
@queries.each_cons(2).all? do |pair|
|
39
|
+
scales = pair.map(&:first)
|
40
|
+
query_lists = pair.map(&:last)
|
41
|
+
|
42
|
+
actual_slope = (query_lists[1].size - query_lists[0].size) / (scales[1] - scales[0])
|
43
|
+
actual_slope <= slope
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
match_when_negated do |_actual|
|
48
|
+
raise "This matcher doesn't support negation"
|
49
|
+
end
|
50
|
+
|
51
|
+
failure_message { |_actual| NPlusOneControl.failure_message(:linear_queries, @queries) }
|
52
|
+
end
|
53
|
+
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: n_plus_one_control
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,7 +100,8 @@ files:
|
|
100
100
|
- lib/n_plus_one_control/rspec.rb
|
101
101
|
- lib/n_plus_one_control/rspec/context.rb
|
102
102
|
- lib/n_plus_one_control/rspec/dsl.rb
|
103
|
-
- lib/n_plus_one_control/rspec/
|
103
|
+
- lib/n_plus_one_control/rspec/matchers/perform_constant_number_of_queries.rb
|
104
|
+
- lib/n_plus_one_control/rspec/matchers/perform_linear_number_of_queries.rb
|
104
105
|
- lib/n_plus_one_control/version.rb
|
105
106
|
homepage: http://github.com/palkan/n_plus_one_control
|
106
107
|
licenses:
|
@@ -111,7 +112,7 @@ metadata:
|
|
111
112
|
documentation_uri: http://github.com/palkan/n_plus_one_control
|
112
113
|
homepage_uri: http://github.com/palkan/n_plus_one_control
|
113
114
|
source_code_uri: http://github.com/palkan/n_plus_one_control
|
114
|
-
post_install_message:
|
115
|
+
post_install_message:
|
115
116
|
rdoc_options: []
|
116
117
|
require_paths:
|
117
118
|
- lib
|
@@ -126,8 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
127
|
- !ruby/object:Gem::Version
|
127
128
|
version: '0'
|
128
129
|
requirements: []
|
129
|
-
rubygems_version: 3.0.
|
130
|
-
signing_key:
|
130
|
+
rubygems_version: 3.0.3
|
131
|
+
signing_key:
|
131
132
|
specification_version: 4
|
132
133
|
summary: RSpec and Minitest matchers to prevent N+1 queries problem
|
133
134
|
test_files: []
|