n_plus_one_control 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 942639e351ee999dc4a113c237d9604878d296cc
4
- data.tar.gz: 76d9b2b2fd28b02925ce4d219e3b5c6ac316d1fb
2
+ SHA256:
3
+ metadata.gz: 292888473b7ae70cfdc9c8e9324170dfaf1d7048fc27a8c33a3ef09ee7b3fc3a
4
+ data.tar.gz: 99bf992bbe635524c8eaea3f6536f4813e05b4420bc1f26ecea8ab26fecfc0f1
5
5
  SHA512:
6
- metadata.gz: 980d3feab9ebd9d9b51da847526a83ae0ed2b36e50509494d14bfad75578086b4c2ca6a6da60d5f518a6a2deb87c80af848c792046804e96b62249404f09535c
7
- data.tar.gz: ef2d79c707712f2a578ba4ff513c1f03b6c4cf351b72381875188ac6a3730c1f73b3fd11ab7a0538f5d76f2d67a5b6fb4f57775218e1ac8b1a0877e6936bd0a9
6
+ metadata.gz: cd45056252d7ae15236e8ae5d5880c0bda50f741ac3cf2079a42f5870cf378fb52d791b444c57c25218428d52e046841a5a762708a18ad868103d9e17f0d30f1
7
+ data.tar.gz: b17d7be7d184ffa37ce4f6162b694928eb96968cc5f4e05e42971d68954709ed33c43b36c5549d49823ac2578103d9420cf713dd4f6cbc12880e885203a87bb4
@@ -17,7 +17,10 @@ AllCops:
17
17
  Rails:
18
18
  Enabled: false
19
19
 
20
- Style/AccessorMethodName:
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
- Style/SpaceInsideStringInterpolation:
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,
@@ -16,4 +16,9 @@
16
16
  end
17
17
  ->(n) { ex.instance_exec(n, &ex.example_group.populate) }
18
18
  end
19
+
20
+ let(:n_plus_one_warmup) do |ex|
21
+ return if ex.example_group.warmup.nil?
22
+ -> { ex.instance_exec(&ex.example_group.warmup) }
23
+ end
19
24
  end
@@ -2,8 +2,17 @@
2
2
 
3
3
  module NPlusOneControl
4
4
  module RSpec
5
- # Extends RSpec ExampleGroup with populate method
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NPlusOneControl
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
4
4
  require "n_plus_one_control/rspec"
5
5
  require "benchmark"
6
6
  require "active_record"
@@ -3,7 +3,7 @@
3
3
  require "minitest/autorun"
4
4
  require "minitest/pride"
5
5
 
6
- $LOAD_PATH << File.expand_path("../../lib", __FILE__)
6
+ $LOAD_PATH << File.expand_path('../lib', __dir__)
7
7
  Thread.abort_on_exception = true
8
8
 
9
9
  require "n_plus_one_control/minitest"
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.2.1
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: 2017-10-31 00:00:00.000000000 Z
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.13
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