n_plus_one_control 0.4.1 → 0.6.2

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
2
  SHA256:
3
- metadata.gz: b20b8f4269aac76f3da642b271b93ddee2adf508539bba6852e02225695de155
4
- data.tar.gz: e739f342b4d46cc451229f3a065cb0e2b83ee28c301d99045d0f094e74bc137b
3
+ metadata.gz: 95b1fada3d7db7a761650c8cd63b93b93a463dcdbfcf2df2b6e3ea25ac04355a
4
+ data.tar.gz: 3a3937ad74d66ea0bc0e56b50954c585c36c401b67d9af4dfd2a94e4202ae0be
5
5
  SHA512:
6
- metadata.gz: 74805b4ea497ff96bb882557ed8a14e04d32cf5ea5c62e9c65167647c880275aec9d0f8a405a298554e9cfecbb20049d99f630b977475a75122d847c7b27e1a9
7
- data.tar.gz: 19063ec1fb2a84edcfd0e38075a3858e239a438803ffb40e03916bc8eb5d49dda9e8b03a30a60788e4e00f9c5484811740448fe3241e64658a78ccf884619321
6
+ metadata.gz: 9ce963308cf99f1c836e7d883a71dbab0b22f8326692444aa3c6639dfc87065fd3e6866ee1af5b358b42cbc67af4a1640677fc051264c8ba6bc35a1604953bd2
7
+ data.tar.gz: f914d8e34d49f7ace51c872fde24487f216b481f39b8236b9c7d24bef26530ba5cbd1040b71d0b445a73f5769762e942fd3b00a7d4211a704fa9dcebf3bd34d9
data/CHANGELOG.md CHANGED
@@ -1,4 +1,34 @@
1
- ## master (unreleased)
1
+ # Change log
2
+
3
+ ## master
4
+
5
+ ## 0.6.2 (2021-10-26)
6
+
7
+ - Fix .ignore setting (.ignore setting was ignored by the Collector ;-))
8
+ - Fix rspec matchers to allow expectations inside execution block
9
+
10
+ ## 0.6.1 (2021-03-05)
11
+
12
+ - Ruby 3.0 compatibility. ([@palkan][])
13
+
14
+ ## 0.6.0 (2020-11-27)
15
+
16
+ - Fix table stats summary when queries use backticks to surround table names ([@andrewhampton][])
17
+ - Add support to test for linear query. ([@caalberts][])
18
+
19
+ ## 0.5.0 (2020-09-07)
20
+
21
+ - **Ruby 2.5+ is required**. ([@palkan][])
22
+
23
+ - Add support for multiple backtrace lines in verbose output. ([@palkan][])
24
+
25
+ Could be specified via `NPLUSONE_BACKTRACE` env var.
26
+
27
+ - Add `NPLUSONE_TRUNCATE` env var to truncate queries in verbose mode. ([@palkan][])
28
+
29
+ - Support passing default filter via `NPLUSONE_FILTER` env var. ([@palkan][])
30
+
31
+ - Add location tracing to SQLs in verbose mode. ([@palkan][])
2
32
 
3
33
  ## 0.4.1 (2020-09-04)
4
34
 
@@ -12,3 +42,5 @@
12
42
 
13
43
  [@Earendil95]: https://github.com/Earendil95
14
44
  [@palkan]: https://github.com/palkan
45
+ [@caalberts]: https://github.com/caalberts
46
+ [@andrewhampton]: https://github.com/andrewhampton
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/n_plus_one_control.svg)](https://rubygems.org/gems/n_plus_one_control) [![Build Status](https://travis-ci.org/palkan/n_plus_one_control.svg?branch=master)](https://travis-ci.org/palkan/n_plus_one_control)
1
+ [![Gem Version](https://badge.fury.io/rb/n_plus_one_control.svg)](https://rubygems.org/gems/n_plus_one_control)
2
+ ![Build](https://github.com/palkan/n_plus_one_control/workflows/Build/badge.svg)
2
3
 
3
4
  # N + 1 Control
4
5
 
@@ -31,7 +32,7 @@ Add this line to your application's Gemfile:
31
32
 
32
33
  ```ruby
33
34
  group :test do
34
- gem 'n_plus_one_control'
35
+ gem "n_plus_one_control"
35
36
  end
36
37
  ```
37
38
 
@@ -47,8 +48,6 @@ First, add NPlusOneControl to your `spec_helper.rb`:
47
48
 
48
49
  ```ruby
49
50
  # spec_helper.rb
50
- ...
51
-
52
51
  require "n_plus_one_control/rspec"
53
52
  ```
54
53
 
@@ -86,10 +85,10 @@ Availables modifiers:
86
85
  ```ruby
87
86
  # You can specify the RegExp to filter queries.
88
87
  # By default, it only considers SELECT queries.
89
- expect { ... }.to perform_constant_number_of_queries.matching(/INSERT/)
88
+ expect { get :index }.to perform_constant_number_of_queries.matching(/INSERT/)
90
89
 
91
90
  # You can also provide custom scale factors
92
- expect { ... }.to perform_constant_number_of_queries.with_scale_factors(10, 100)
91
+ expect { get :index }.to perform_constant_number_of_queries.with_scale_factors(10, 100)
93
92
  ```
94
93
 
95
94
  #### Using scale factor in spec
@@ -97,7 +96,7 @@ expect { ... }.to perform_constant_number_of_queries.with_scale_factors(10, 100)
97
96
  Let's suppose your action accepts parameter, which can make impact on the number of returned records:
98
97
 
99
98
  ```ruby
100
- get :index, params: { per_page: 10 }
99
+ get :index, params: {per_page: 10}
101
100
  ```
102
101
 
103
102
  Then it is enough to just change `per_page` parameter between executions and do not recreate records in DB. For this purpose, you can use `current_scale` method in your example:
@@ -107,7 +106,36 @@ context "N+1", :n_plus_one do
107
106
  before { create_list :post, 3 }
108
107
 
109
108
  specify do
110
- expect { get :index, params: { per_page: current_scale } }.to perform_constant_number_of_queries
109
+ expect { get :index, params: {per_page: current_scale} }.to perform_constant_number_of_queries
110
+ end
111
+ end
112
+ ```
113
+
114
+ ### Expectations in execution block
115
+
116
+ Both rspec matchers allows you to put additional expectations inside execution block to ensure that tested piece of code actually does what expected.
117
+
118
+ ```ruby
119
+ context "N+1", :n_plus_one do
120
+ specify do
121
+ expect do
122
+ expect(my_query).to eq(actuall_results)
123
+ end.to perform_constant_number_of_queries
124
+ end
125
+ end
126
+ ```
127
+
128
+ #### Other available matchers
129
+
130
+ `perform_linear_number_of_queries(slope: 1)` allows you to test that a query generates linear number of queries with the given slope.
131
+
132
+ ```ruby
133
+ context "when has linear query", :n_plus_one do
134
+ populate { |n| create_list(:post, n) }
135
+
136
+ specify do
137
+ expect { Post.find_each { |p| p.user.name } }
138
+ .to perform_linear_number_of_queries(slope: 1)
111
139
  end
112
140
  end
113
141
  ```
@@ -118,8 +146,6 @@ First, add NPlusOneControl to your `test_helper.rb`:
118
146
 
119
147
  ```ruby
120
148
  # test_helper.rb
121
- ...
122
-
123
149
  require "n_plus_one_control/minitest"
124
150
  ```
125
151
 
@@ -135,6 +161,18 @@ def test_no_n_plus_one_error
135
161
  end
136
162
  ```
137
163
 
164
+ You can also use `assert_perform_linear_number_of_queries` to test for linear queries:
165
+
166
+ ```ruby
167
+ def test_no_n_plus_one_error
168
+ populate = ->(n) { create_list(:post, n) }
169
+
170
+ assert_perform_linear_number_of_queries(slope: 1, populate: populate) do
171
+ Post.find_each { |p| p.user.name }
172
+ end
173
+ end
174
+ ```
175
+
138
176
  You can also specify custom scale factors or filter patterns:
139
177
 
140
178
  ```ruby
@@ -153,6 +191,12 @@ assert_perform_constant_number_of_queries(
153
191
  end
154
192
  ```
155
193
 
194
+ It's possible to specify a filter via `NPLUSONE_FILTER` env var, e.g.:
195
+
196
+ ```ruby
197
+ NPLUSONE_FILTER = users bundle exec rake test
198
+ ```
199
+
156
200
  You can also specify `populate` as a test class instance method:
157
201
 
158
202
  ```ruby
@@ -165,6 +209,7 @@ def test_no_n_plus_one_error
165
209
  get :index
166
210
  end
167
211
  end
212
+
168
213
  ```
169
214
 
170
215
  As in RSpec, you can use `current_scale` factor instead of `populate` block:
@@ -172,7 +217,7 @@ As in RSpec, you can use `current_scale` factor instead of `populate` block:
172
217
  ```ruby
173
218
  def test_no_n_plus_one_error
174
219
  assert_perform_constant_number_of_queries do
175
- get :index, params: { per_page: current_scale }
220
+ get :index, params: {per_page: current_scale}
176
221
  end
177
222
  end
178
223
  ```
@@ -223,6 +268,7 @@ end
223
268
  ```
224
269
 
225
270
  If your `warmup` and testing procs are identical, you can use:
271
+
226
272
  ```ruby
227
273
  expext { get :index }.to perform_constant_number_of_queries.with_warming_up # RSpec only
228
274
  ```
@@ -254,7 +300,7 @@ NPlusOneControl.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
254
300
  # ActiveSupport notifications event to track queries.
255
301
  # We track ActiveRecord event by default,
256
302
  # but can also track rom-rb events ('sql.rom') as well.
257
- NPlusOneControl.event = 'sql.active_record'
303
+ NPlusOneControl.event = "sql.active_record"
258
304
 
259
305
  # configure transactional behavour for populate method
260
306
  # in case of use multiple database connections
@@ -268,6 +314,21 @@ NPlusOneControl::Executor.tap do |executor|
268
314
  connections.each(&:rollback_transaction)
269
315
  end
270
316
  end
317
+
318
+ # Provide a backtrace cleaner callable object used to filter SQL caller location to display in the verbose mode
319
+ # Set it to nil to disable tracing.
320
+ #
321
+ # In Rails apps, we use Rails.backtrace_cleaner by default.
322
+ NPlusOneControl.backtrace_cleaner = ->(locations_array) { do_some_filtering(locations_array) }
323
+
324
+ # You can also specify the number of backtrace lines to show.
325
+ # MOTE: It could be specified via NPLUSONE_BACKTRACE env var
326
+ NPlusOneControl.backtrace_length = 1
327
+
328
+ # Sometime queries could be too large to provide any meaningful insight.
329
+ # You can configure an output length limit for quries in verbose mode by setting the follwing option
330
+ # NOTE: It could be specified via NPLUSONE_TRUNCATE env var
331
+ NPlusOneControl.truncate_query_size = 100
271
332
  ```
272
333
 
273
334
  ## How does it work?
@@ -276,22 +337,29 @@ Take a look at our [Executor](https://github.com/palkan/n_plus_one_control/blob/
276
337
 
277
338
  ## What's next?
278
339
 
340
+ - More matchers.
341
+
279
342
  It may be useful to provide more matchers/assertions, for example:
280
343
 
281
344
  ```ruby
282
345
 
283
346
  # Actually, that means that it is N+1))
284
- assert_linear_number_of_queries { ... }
347
+ assert_linear_number_of_queries { some_code }
285
348
 
286
349
  # But we can tune it with `coef` and handle such cases as selecting in batches
287
350
  assert_linear_number_of_queries(coef: 0.1) do
288
- Post.find_in_batches { ... }
351
+ Post.find_in_batches { some_code }
289
352
  end
290
353
 
291
354
  # probably, also make sense to add another curve types
292
- assert_logarithmic_number_of_queries { ... }
355
+ assert_logarithmic_number_of_queries { some_code }
293
356
  ```
294
357
 
358
+ - Support custom non-SQL events.
359
+
360
+ N+1 problem is not a database specific: we can have N+1 Redis calls, N+1 HTTP external requests, etc.
361
+ We can make `n_plus_one_control` customizable to support these scenarios (technically, we need to make it possible to handle different payload in the event subscriber).
362
+
295
363
  If you want to discuss or implement any of these, feel free to open an [issue](https://github.com/palkan/n_plus_one_control/issues) or propose a [pull request](https://github.com/palkan/n_plus_one_control/pulls).
296
364
 
297
365
  ## Development
@@ -19,10 +19,28 @@ module NPlusOneControl
19
19
  @queries
20
20
  end
21
21
 
22
- def callback(_name, _start, _finish, _message_id, values)
22
+ def callback(_name, _start, _finish, _message_id, values) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/LineLength
23
23
  return if %w[CACHE SCHEMA].include? values[:name]
24
+ return if values[:sql].match?(NPlusOneControl.ignore)
24
25
 
25
- @queries << values[:sql] if @pattern.nil? || (values[:sql] =~ @pattern)
26
+ return unless @pattern.nil? || (values[:sql] =~ @pattern)
27
+
28
+ query = values[:sql]
29
+
30
+ if NPlusOneControl.backtrace_cleaner && NPlusOneControl.verbose
31
+ source = extract_query_source_location(caller)
32
+
33
+ query = "#{query}\n ↳ #{source.join("\n")}" unless source.empty?
34
+ end
35
+
36
+ @queries << query
37
+ end
38
+
39
+ private
40
+
41
+ def extract_query_source_location(locations)
42
+ NPlusOneControl.backtrace_cleaner.call(locations.lazy)
43
+ .take(NPlusOneControl.backtrace_length).to_a
26
44
  end
27
45
  end
28
46
 
@@ -18,7 +18,7 @@ module NPlusOneControl
18
18
 
19
19
  @executor = NPlusOneControl::Executor.new(
20
20
  population: populate || population_method,
21
- matching: matching || /^SELECT/i,
21
+ matching: matching || NPlusOneControl.default_matching,
22
22
  scale_factors: scale_factors || NPlusOneControl.default_scale_factors
23
23
  )
24
24
 
@@ -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
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NPlusOneControl # :nodoc:
4
+ class Railtie < ::Rails::Railtie # :nodoc:
5
+ initializer "n_plus_one_control.backtrace_cleaner" do
6
+ ActiveSupport.on_load(:active_record) do
7
+ NPlusOneControl.backtrace_cleaner = lambda do |locations|
8
+ ::Rails.backtrace_cleaner.clean(locations)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -9,18 +9,18 @@ module NPlusOneControl
9
9
  # Setup warmup block, wich will run before matching
10
10
  # for example, if using cache, then later queries
11
11
  # will perform less DB queries than first
12
- def warmup
13
- return @warmup unless block_given?
12
+ def warmup(&block)
13
+ return @warmup unless block
14
14
 
15
- @warmup = Proc.new
15
+ @warmup = block
16
16
  end
17
17
 
18
18
  # Setup populate callback, which is used
19
19
  # to prepare data for each run.
20
- def populate
21
- return @populate unless block_given?
20
+ def populate(&block)
21
+ return @populate unless block
22
22
 
23
- @populate = Proc.new
23
+ @populate = block
24
24
  end
25
25
  end
26
26
 
@@ -16,7 +16,7 @@
16
16
  @warmup = true
17
17
  end
18
18
 
19
- match do |actual, *_args|
19
+ match(notify_expectation_failures: true) do |actual, *_args|
20
20
  raise ArgumentError, "Block is required" unless actual.is_a? Proc
21
21
 
22
22
  raise "Missing tag :n_plus_one" unless
@@ -27,8 +27,7 @@
27
27
 
28
28
  warmup.call if warmup.present?
29
29
 
30
- # by default we're looking for select queries
31
- pattern = @pattern || /^SELECT/i
30
+ pattern = @pattern || NPlusOneControl.default_matching
32
31
 
33
32
  @matcher_execution_context.executor = NPlusOneControl::Executor.new(
34
33
  population: populate,
@@ -47,6 +46,6 @@
47
46
  raise "This matcher doesn't support negation"
48
47
  end
49
48
 
50
- failure_message { |_actual| NPlusOneControl.failure_message(@queries) }
49
+ failure_message { |_actual| NPlusOneControl.failure_message(:constant_queries, @queries) }
51
50
  end
52
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(notify_expectation_failures: true) 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
@@ -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/matcher"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NPlusOneControl
4
- VERSION = "0.4.1"
4
+ VERSION = "0.6.2"
5
5
  end
@@ -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
@@ -18,10 +18,18 @@ module NPlusOneControl
18
18
  }.freeze
19
19
 
20
20
  class << self
21
- attr_accessor :default_scale_factors, :verbose, :show_table_stats, :ignore, :event
21
+ attr_accessor :default_scale_factors, :verbose, :show_table_stats, :ignore, :event,
22
+ :backtrace_cleaner, :backtrace_length, :truncate_query_size
22
23
 
23
- def failure_message(queries) # rubocop:disable Metrics/MethodLength
24
- msg = ["Expected to make the same number of queries, but got:\n"]
24
+ attr_reader :default_matching
25
+
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"]
25
33
  queries.each do |(scale, data)|
26
34
  msg << " #{data.size} for N=#{scale}\n"
27
35
  end
@@ -30,8 +38,8 @@ module NPlusOneControl
30
38
 
31
39
  if verbose
32
40
  queries.each do |(scale, data)|
33
- msg << " Queries for N=#{scale}\n"
34
- msg << data.map { |sql| " #{sql}\n" }.join.to_s
41
+ msg << "Queries for N=#{scale}\n"
42
+ msg << data.map { |sql| " #{truncate_query(sql)}\n" }.join.to_s
35
43
  end
36
44
  end
37
45
 
@@ -39,7 +47,7 @@ module NPlusOneControl
39
47
  end
40
48
 
41
49
  def table_usage_stats(runs) # rubocop:disable Metrics/MethodLength
42
- msg = ["\nUnmatched query numbers by tables:\n"]
50
+ msg = ["Unmatched query numbers by tables:\n"]
43
51
 
44
52
  before, after = runs.map do |queries|
45
53
  queries.group_by do |query|
@@ -58,6 +66,38 @@ module NPlusOneControl
58
66
 
59
67
  msg
60
68
  end
69
+
70
+ def default_matching=(val)
71
+ unless val
72
+ @default_matching = nil
73
+ return
74
+ end
75
+
76
+ @default_matching =
77
+ if val.is_a?(Regexp)
78
+ val
79
+ else
80
+ Regexp.new(val, Regexp::MULTILINE | Regexp::IGNORECASE)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def truncate_query(sql)
87
+ return sql unless truncate_query_size
88
+
89
+ # Only truncate query, leave tracing (if any) as is
90
+ parts = sql.split(/(\s+↳)/)
91
+
92
+ parts[0] =
93
+ if truncate_query_size < 4
94
+ "..."
95
+ else
96
+ parts[0][0..(truncate_query_size - 4)] + "..."
97
+ end
98
+
99
+ parts.join
100
+ end
61
101
  end
62
102
 
63
103
  # Scale factors to use.
@@ -65,7 +105,7 @@ module NPlusOneControl
65
105
  self.default_scale_factors = [2, 3]
66
106
 
67
107
  # Print performed queries if true
68
- self.verbose = ENV['NPLUSONE_VERBOSE'] == '1'
108
+ self.verbose = ENV["NPLUSONE_VERBOSE"] == "1"
69
109
 
70
110
  # Print table hits difference
71
111
  self.show_table_stats = true
@@ -76,5 +116,16 @@ module NPlusOneControl
76
116
  # ActiveSupport notifications event to track queries.
77
117
  # We track ActiveRecord event by default,
78
118
  # but can also track rom-rb events ('sql.rom') as well.
79
- self.event = 'sql.active_record'
119
+ self.event = "sql.active_record"
120
+
121
+ # Default query filtering applied if none provided explicitly
122
+ self.default_matching = ENV["NPLUSONE_FILTER"] || /^SELECT/i
123
+
124
+ # Truncate queries in verbose mode to fit the length
125
+ self.truncate_query_size = ENV["NPLUSONE_TRUNCATE"]&.to_i
126
+
127
+ # Define the number of backtrace lines to show
128
+ self.backtrace_length = ENV.fetch("NPLUSONE_BACKTRACE", 1).to_i
80
129
  end
130
+
131
+ require "n_plus_one_control/railtie" if defined?(Rails::Railtie)
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.1
4
+ version: 0.6.2
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-09-04 00:00:00.000000000 Z
11
+ date: 2021-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -80,62 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 4.8.0
83
- - !ruby/object:Gem::Dependency
84
- name: rubocop
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 0.61.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 0.61.0
97
- - !ruby/object:Gem::Dependency
98
- name: activerecord
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '5.1'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '5.1'
111
- - !ruby/object:Gem::Dependency
112
- name: sqlite3
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: 1.3.6
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: 1.3.6
125
- - !ruby/object:Gem::Dependency
126
- name: pry-byebug
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
83
  description: "\n RSpec and Minitest matchers to prevent N+1 queries problem.\n\n
140
84
  \ Evaluates code under consideration several times with different scale factors\n
141
85
  \ to make sure that the number of DB queries behaves as expected (i.e. O(1) instead
@@ -152,10 +96,12 @@ files:
152
96
  - lib/n_plus_one_control.rb
153
97
  - lib/n_plus_one_control/executor.rb
154
98
  - lib/n_plus_one_control/minitest.rb
99
+ - lib/n_plus_one_control/railtie.rb
155
100
  - lib/n_plus_one_control/rspec.rb
156
101
  - lib/n_plus_one_control/rspec/context.rb
157
102
  - lib/n_plus_one_control/rspec/dsl.rb
158
- - lib/n_plus_one_control/rspec/matcher.rb
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
159
105
  - lib/n_plus_one_control/version.rb
160
106
  homepage: http://github.com/palkan/n_plus_one_control
161
107
  licenses:
@@ -166,7 +112,7 @@ metadata:
166
112
  documentation_uri: http://github.com/palkan/n_plus_one_control
167
113
  homepage_uri: http://github.com/palkan/n_plus_one_control
168
114
  source_code_uri: http://github.com/palkan/n_plus_one_control
169
- post_install_message:
115
+ post_install_message:
170
116
  rdoc_options: []
171
117
  require_paths:
172
118
  - lib
@@ -174,15 +120,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
120
  requirements:
175
121
  - - ">="
176
122
  - !ruby/object:Gem::Version
177
- version: 2.0.0
123
+ version: 2.5.0
178
124
  required_rubygems_version: !ruby/object:Gem::Requirement
179
125
  requirements:
180
126
  - - ">="
181
127
  - !ruby/object:Gem::Version
182
128
  version: '0'
183
129
  requirements: []
184
- rubygems_version: 3.0.6
185
- signing_key:
130
+ rubygems_version: 3.0.3
131
+ signing_key:
186
132
  specification_version: 4
187
133
  summary: RSpec and Minitest matchers to prevent N+1 queries problem
188
134
  test_files: []