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 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