n_plus_one_control 0.2.1 → 0.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 +5 -5
- data/.rubocop.yml +5 -2
- data/README.md +50 -0
- data/lib/n_plus_one_control/minitest.rb +8 -1
- data/lib/n_plus_one_control/rspec/context.rb +5 -0
- data/lib/n_plus_one_control/rspec/dsl.rb +10 -1
- data/lib/n_plus_one_control/rspec/matcher.rb +8 -0
- data/lib/n_plus_one_control/version.rb +1 -1
- data/spec/n_plus_one_control/rspec_spec.rb +29 -0
- data/spec/spec_helper.rb +1 -1
- data/tests/test_helper.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 292888473b7ae70cfdc9c8e9324170dfaf1d7048fc27a8c33a3ef09ee7b3fc3a
|
4
|
+
data.tar.gz: 99bf992bbe635524c8eaea3f6536f4813e05b4420bc1f26ecea8ab26fecfc0f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd45056252d7ae15236e8ae5d5880c0bda50f741ac3cf2079a42f5870cf378fb52d791b444c57c25218428d52e046841a5a762708a18ad868103d9e17f0d30f1
|
7
|
+
data.tar.gz: b17d7be7d184ffa37ce4f6162b694928eb96968cc5f4e05e42971d68954709ed33c43b36c5549d49823ac2578103d9420cf713dd4f6cbc12880e885203a87bb4
|
data/.rubocop.yml
CHANGED
@@ -17,7 +17,10 @@ AllCops:
|
|
17
17
|
Rails:
|
18
18
|
Enabled: false
|
19
19
|
|
20
|
-
|
20
|
+
Naming/UncommunicativeMethodParamName:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Naming/AccessorMethodName:
|
21
24
|
Enabled: false
|
22
25
|
|
23
26
|
Style/TrivialAccessors:
|
@@ -37,7 +40,7 @@ Style/RegexpLiteral:
|
|
37
40
|
Style/Lambda:
|
38
41
|
Enabled: false
|
39
42
|
|
40
|
-
|
43
|
+
Layout/SpaceInsideStringInterpolation:
|
41
44
|
EnforcedStyle: no_space
|
42
45
|
|
43
46
|
Style/ClassAndModuleChildren:
|
data/README.md
CHANGED
@@ -147,6 +147,56 @@ def test_no_n_plus_one_error
|
|
147
147
|
end
|
148
148
|
```
|
149
149
|
|
150
|
+
### With caching
|
151
|
+
|
152
|
+
If you use caching you can face the problem when first request performs more DB queries than others. The solution is:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# RSpec
|
156
|
+
|
157
|
+
context "N + 1", :n_plus_one do
|
158
|
+
populate { |n| create_list :post, n }
|
159
|
+
|
160
|
+
warmup { get :index } # cache something must be cached
|
161
|
+
|
162
|
+
specify do
|
163
|
+
expect { get :index }.to perform_constant_number_of_queries
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Minitest
|
168
|
+
|
169
|
+
def populate(n)
|
170
|
+
create_list(:post, n)
|
171
|
+
end
|
172
|
+
|
173
|
+
def warmup
|
174
|
+
get :index
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_no_n_plus_one_error
|
178
|
+
assert_perform_constant_number_of_queries do
|
179
|
+
get :index
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# or with params
|
184
|
+
|
185
|
+
def test_no_n_plus_one
|
186
|
+
populate = ->(n) { create_list(:post, n) }
|
187
|
+
warmup = -> { get :index }
|
188
|
+
|
189
|
+
assert_perform_constant_number_of_queries population: populate, warmup: warmup do
|
190
|
+
get :index
|
191
|
+
end
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
If your `warmup` and testing procs are identical, you can use:
|
196
|
+
```ruby
|
197
|
+
expext { get :index }.to perform_constant_number_of_queries.with_warming_up # RSpec only
|
198
|
+
```
|
199
|
+
|
150
200
|
### Configuration
|
151
201
|
|
152
202
|
There are some global configuration parameters (and their corresponding defaults):
|
@@ -5,14 +5,21 @@ require "n_plus_one_control"
|
|
5
5
|
module NPlusOneControl
|
6
6
|
# Minitest assertions
|
7
7
|
module MinitestHelper
|
8
|
+
def warming_up(warmup)
|
9
|
+
(warmup || methods.include?(:warmup) ? method(:warmup) : nil)&.call
|
10
|
+
end
|
11
|
+
|
8
12
|
def assert_perform_constant_number_of_queries(
|
9
13
|
populate: nil,
|
10
14
|
matching: nil,
|
11
|
-
scale_factors: nil
|
15
|
+
scale_factors: nil,
|
16
|
+
warmup: nil
|
12
17
|
)
|
13
18
|
|
14
19
|
raise ArgumentError, "Block is required" unless block_given?
|
15
20
|
|
21
|
+
warming_up warmup
|
22
|
+
|
16
23
|
queries = NPlusOneControl::Executor.call(
|
17
24
|
population: populate || method(:populate),
|
18
25
|
matching: matching || /^SELECT/i,
|
@@ -2,8 +2,17 @@
|
|
2
2
|
|
3
3
|
module NPlusOneControl
|
4
4
|
module RSpec
|
5
|
-
# Extends RSpec ExampleGroup with populate
|
5
|
+
# Extends RSpec ExampleGroup with populate & warmup methods
|
6
6
|
module DSL
|
7
|
+
# Setup warmup block, wich will run before matching
|
8
|
+
# for example, if using cache, then later queries
|
9
|
+
# will perform less DB queries than first
|
10
|
+
def warmup
|
11
|
+
return @warmup unless block_given?
|
12
|
+
|
13
|
+
@warmup = Proc.new
|
14
|
+
end
|
15
|
+
|
7
16
|
# Setup populate callback, which is used
|
8
17
|
# to prepare data for each run.
|
9
18
|
def populate
|
@@ -12,6 +12,10 @@
|
|
12
12
|
@pattern = pattern
|
13
13
|
end
|
14
14
|
|
15
|
+
chain :with_warming_up do
|
16
|
+
@warmup = true
|
17
|
+
end
|
18
|
+
|
15
19
|
match do |actual, *_args|
|
16
20
|
raise ArgumentError, "Block is required" unless actual.is_a? Proc
|
17
21
|
|
@@ -19,6 +23,9 @@
|
|
19
23
|
@matcher_execution_context.respond_to?(:n_plus_one_populate)
|
20
24
|
|
21
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?
|
22
29
|
|
23
30
|
# by default we're looking for select queries
|
24
31
|
pattern = @pattern || /^SELECT/i
|
@@ -41,3 +48,4 @@
|
|
41
48
|
|
42
49
|
failure_message { |_actual| NPlusOneControl.failure_message(@queries) }
|
43
50
|
end
|
51
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -81,4 +81,33 @@ describe NPlusOneControl::RSpec do
|
|
81
81
|
.to perform_constant_number_of_queries.matching(/posts/)
|
82
82
|
end
|
83
83
|
end
|
84
|
+
|
85
|
+
context 'with warming up', :n_plus_one do
|
86
|
+
let(:cache) { double "cache" }
|
87
|
+
|
88
|
+
before do
|
89
|
+
allow(cache).to receive(:setup).and_return(:result)
|
90
|
+
allow(NPlusOneControl::Executor).to receive(:call) { raise StandardError }
|
91
|
+
end
|
92
|
+
|
93
|
+
populate { |n| create_list(:post, n) }
|
94
|
+
|
95
|
+
warmup { cache.setup }
|
96
|
+
|
97
|
+
it "runs warmup before calling Executor" do
|
98
|
+
expect(cache).to receive(:setup)
|
99
|
+
expect do
|
100
|
+
expect { Post.find_each(&:id) }.to perform_constant_number_of_queries
|
101
|
+
end.to raise_error StandardError
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'with_warming_up', :n_plus_one do
|
106
|
+
populate { |n| create_list(:post, n) }
|
107
|
+
|
108
|
+
it "runs actual one more time" do
|
109
|
+
expect(Post).to receive(:all).exactly(3).times
|
110
|
+
expect { Post.all }.to perform_constant_number_of_queries.with_warming_up
|
111
|
+
end
|
112
|
+
end
|
84
113
|
end
|
data/spec/spec_helper.rb
CHANGED
data/tests/test_helper.rb
CHANGED
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -196,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
196
|
version: '0'
|
197
197
|
requirements: []
|
198
198
|
rubyforge_project:
|
199
|
-
rubygems_version: 2.6
|
199
|
+
rubygems_version: 2.7.6
|
200
200
|
signing_key:
|
201
201
|
specification_version: 4
|
202
202
|
summary: RSpec and Minitest matchers to prevent N+1 queries problem
|