n_plus_one_control 0.1.3 → 0.4.1
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/CHANGELOG.md +14 -0
- data/LICENSE.txt +1 -1
- data/README.md +102 -2
- data/lib/n_plus_one_control.rb +48 -3
- data/lib/n_plus_one_control/executor.rb +51 -19
- data/lib/n_plus_one_control/minitest.rb +23 -4
- data/lib/n_plus_one_control/rspec.rb +4 -1
- data/lib/n_plus_one_control/rspec/context.rb +12 -11
- data/lib/n_plus_one_control/rspec/dsl.rb +24 -6
- data/lib/n_plus_one_control/rspec/matcher.rb +12 -3
- data/lib/n_plus_one_control/version.rb +1 -1
- metadata +19 -34
- data/.gitignore +0 -11
- data/.rspec +0 -2
- data/.rubocop.yml +0 -71
- data/.travis.yml +0 -5
- data/Gemfile +0 -4
- data/Rakefile +0 -13
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/n_plus_one_control.gemspec +0 -47
- data/spec/n_plus_one_control/executor_spec.rb +0 -65
- data/spec/n_plus_one_control/rspec_spec.rb +0 -84
- data/spec/n_plus_one_control_spec.rb +0 -9
- data/spec/spec_helper.rb +0 -30
- data/spec/support/post.rb +0 -19
- data/spec/support/user.rb +0 -17
- data/tests/minitest_test.rb +0 -63
- data/tests/test_helper.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b20b8f4269aac76f3da642b271b93ddee2adf508539bba6852e02225695de155
|
4
|
+
data.tar.gz: e739f342b4d46cc451229f3a065cb0e2b83ee28c301d99045d0f094e74bc137b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74805b4ea497ff96bb882557ed8a14e04d32cf5ea5c62e9c65167647c880275aec9d0f8a405a298554e9cfecbb20049d99f630b977475a75122d847c7b27e1a9
|
7
|
+
data.tar.gz: 19063ec1fb2a84edcfd0e38075a3858e239a438803ffb40e03916bc8eb5d49dda9e8b03a30a60788e4e00f9c5484811740448fe3241e64658a78ccf884619321
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
## master (unreleased)
|
2
|
+
|
3
|
+
## 0.4.1 (2020-09-04)
|
4
|
+
|
5
|
+
- Enhance failure message by showing differences in table hits. ([@palkan][])
|
6
|
+
|
7
|
+
## 0.4.0 (2020-07-20)
|
8
|
+
|
9
|
+
- Make scale factor available in tests via `#current_scale` method. ([@Earendil95][])
|
10
|
+
|
11
|
+
- Start keeping a changelog. ([@palkan][])
|
12
|
+
|
13
|
+
[@Earendil95]: https://github.com/Earendil95
|
14
|
+
[@palkan]: https://github.com/palkan
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -92,6 +92,26 @@ expect { ... }.to perform_constant_number_of_queries.matching(/INSERT/)
|
|
92
92
|
expect { ... }.to perform_constant_number_of_queries.with_scale_factors(10, 100)
|
93
93
|
```
|
94
94
|
|
95
|
+
#### Using scale factor in spec
|
96
|
+
|
97
|
+
Let's suppose your action accepts parameter, which can make impact on the number of returned records:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
get :index, params: { per_page: 10 }
|
101
|
+
```
|
102
|
+
|
103
|
+
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:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
context "N+1", :n_plus_one do
|
107
|
+
before { create_list :post, 3 }
|
108
|
+
|
109
|
+
specify do
|
110
|
+
expect { get :index, params: { per_page: current_scale } }.to perform_constant_number_of_queries
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
95
115
|
### Minitest
|
96
116
|
|
97
117
|
First, add NPlusOneControl to your `test_helper.rb`:
|
@@ -147,6 +167,66 @@ def test_no_n_plus_one_error
|
|
147
167
|
end
|
148
168
|
```
|
149
169
|
|
170
|
+
As in RSpec, you can use `current_scale` factor instead of `populate` block:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
def test_no_n_plus_one_error
|
174
|
+
assert_perform_constant_number_of_queries do
|
175
|
+
get :index, params: { per_page: current_scale }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
### With caching
|
181
|
+
|
182
|
+
If you use caching you can face the problem when first request performs more DB queries than others. The solution is:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# RSpec
|
186
|
+
|
187
|
+
context "N + 1", :n_plus_one do
|
188
|
+
populate { |n| create_list :post, n }
|
189
|
+
|
190
|
+
warmup { get :index } # cache something must be cached
|
191
|
+
|
192
|
+
specify do
|
193
|
+
expect { get :index }.to perform_constant_number_of_queries
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Minitest
|
198
|
+
|
199
|
+
def populate(n)
|
200
|
+
create_list(:post, n)
|
201
|
+
end
|
202
|
+
|
203
|
+
def warmup
|
204
|
+
get :index
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_no_n_plus_one_error
|
208
|
+
assert_perform_constant_number_of_queries do
|
209
|
+
get :index
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# or with params
|
214
|
+
|
215
|
+
def test_no_n_plus_one
|
216
|
+
populate = ->(n) { create_list(:post, n) }
|
217
|
+
warmup = -> { get :index }
|
218
|
+
|
219
|
+
assert_perform_constant_number_of_queries population: populate, warmup: warmup do
|
220
|
+
get :index
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
If your `warmup` and testing procs are identical, you can use:
|
226
|
+
```ruby
|
227
|
+
expext { get :index }.to perform_constant_number_of_queries.with_warming_up # RSpec only
|
228
|
+
```
|
229
|
+
|
150
230
|
### Configuration
|
151
231
|
|
152
232
|
There are some global configuration parameters (and their corresponding defaults):
|
@@ -160,6 +240,14 @@ NPlusOneControl.default_scale_factors = [2, 3]
|
|
160
240
|
# You can activate verbosity through env variable NPLUSONE_VERBOSE=1
|
161
241
|
NPlusOneControl.verbose = false
|
162
242
|
|
243
|
+
# Print table hits difference, for example:
|
244
|
+
#
|
245
|
+
# Unmatched query numbers by tables:
|
246
|
+
# users (SELECT): 2 != 3
|
247
|
+
# events (INSERT): 1 != 2
|
248
|
+
#
|
249
|
+
self.show_table_stats = true
|
250
|
+
|
163
251
|
# Ignore matching queries
|
164
252
|
NPlusOneControl.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
|
165
253
|
|
@@ -167,6 +255,19 @@ NPlusOneControl.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
|
|
167
255
|
# We track ActiveRecord event by default,
|
168
256
|
# but can also track rom-rb events ('sql.rom') as well.
|
169
257
|
NPlusOneControl.event = 'sql.active_record'
|
258
|
+
|
259
|
+
# configure transactional behavour for populate method
|
260
|
+
# in case of use multiple database connections
|
261
|
+
NPlusOneControl::Executor.tap do |executor|
|
262
|
+
connections = ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
|
263
|
+
|
264
|
+
executor.transaction_begin = -> do
|
265
|
+
connections.each { |connection| connection.begin_transaction(joinable: false) }
|
266
|
+
end
|
267
|
+
executor.transaction_rollback = -> do
|
268
|
+
connections.each(&:rollback_transaction)
|
269
|
+
end
|
270
|
+
end
|
170
271
|
```
|
171
272
|
|
172
273
|
## How does it work?
|
@@ -180,7 +281,7 @@ It may be useful to provide more matchers/assertions, for example:
|
|
180
281
|
```ruby
|
181
282
|
|
182
283
|
# Actually, that means that it is N+1))
|
183
|
-
assert_linear_number_of_queries { ... }
|
284
|
+
assert_linear_number_of_queries { ... }
|
184
285
|
|
185
286
|
# But we can tune it with `coef` and handle such cases as selecting in batches
|
186
287
|
assert_linear_number_of_queries(coef: 0.1) do
|
@@ -211,4 +312,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan
|
|
211
312
|
## License
|
212
313
|
|
213
314
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
214
|
-
|
data/lib/n_plus_one_control.rb
CHANGED
@@ -5,17 +5,59 @@ require "n_plus_one_control/executor"
|
|
5
5
|
|
6
6
|
# RSpec and Minitest matchers to prevent N+1 queries problem.
|
7
7
|
module NPlusOneControl
|
8
|
+
# Used to extract a table name from a query
|
9
|
+
EXTRACT_TABLE_RXP = /(insert into|update|delete from|from) ['"](\S+)['"]/i.freeze
|
10
|
+
|
11
|
+
# Used to convert a query part extracted by the regexp above to the corresponding
|
12
|
+
# human-friendly type
|
13
|
+
QUERY_PART_TO_TYPE = {
|
14
|
+
"insert into" => "INSERT",
|
15
|
+
"update" => "UPDATE",
|
16
|
+
"delete from" => "DELETE",
|
17
|
+
"from" => "SELECT"
|
18
|
+
}.freeze
|
19
|
+
|
8
20
|
class << self
|
9
|
-
attr_accessor :default_scale_factors, :verbose, :ignore, :event
|
21
|
+
attr_accessor :default_scale_factors, :verbose, :show_table_stats, :ignore, :event
|
10
22
|
|
11
|
-
def failure_message(queries)
|
23
|
+
def failure_message(queries) # rubocop:disable Metrics/MethodLength
|
12
24
|
msg = ["Expected to make the same number of queries, but got:\n"]
|
13
25
|
queries.each do |(scale, data)|
|
14
26
|
msg << " #{data.size} for N=#{scale}\n"
|
15
|
-
msg << data.map { |sql| " #{sql}\n" }.join.to_s if verbose
|
16
27
|
end
|
28
|
+
|
29
|
+
msg.concat(table_usage_stats(queries.map(&:last))) if show_table_stats
|
30
|
+
|
31
|
+
if verbose
|
32
|
+
queries.each do |(scale, data)|
|
33
|
+
msg << " Queries for N=#{scale}\n"
|
34
|
+
msg << data.map { |sql| " #{sql}\n" }.join.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
17
38
|
msg.join
|
18
39
|
end
|
40
|
+
|
41
|
+
def table_usage_stats(runs) # rubocop:disable Metrics/MethodLength
|
42
|
+
msg = ["\nUnmatched query numbers by tables:\n"]
|
43
|
+
|
44
|
+
before, after = runs.map do |queries|
|
45
|
+
queries.group_by do |query|
|
46
|
+
matches = query.match(EXTRACT_TABLE_RXP)
|
47
|
+
next unless matches
|
48
|
+
|
49
|
+
" #{matches[2]} (#{QUERY_PART_TO_TYPE[matches[1].downcase]})"
|
50
|
+
end.transform_values(&:count)
|
51
|
+
end
|
52
|
+
|
53
|
+
before.keys.each do |k|
|
54
|
+
next if before[k] == after[k]
|
55
|
+
|
56
|
+
msg << "#{k}: #{before[k]} != #{after[k]}\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
msg
|
60
|
+
end
|
19
61
|
end
|
20
62
|
|
21
63
|
# Scale factors to use.
|
@@ -25,6 +67,9 @@ module NPlusOneControl
|
|
25
67
|
# Print performed queries if true
|
26
68
|
self.verbose = ENV['NPLUSONE_VERBOSE'] == '1'
|
27
69
|
|
70
|
+
# Print table hits difference
|
71
|
+
self.show_table_stats = true
|
72
|
+
|
28
73
|
# Ignore matching queries
|
29
74
|
self.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
|
30
75
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module NPlusOneControl
|
4
4
|
# Runs code for every scale factor
|
5
5
|
# and returns collected queries.
|
6
|
-
|
6
|
+
class Executor
|
7
7
|
# Subscribes to ActiveSupport notifications and collect matching queries.
|
8
8
|
class Collector
|
9
9
|
def initialize(pattern)
|
@@ -21,35 +21,67 @@ module NPlusOneControl
|
|
21
21
|
|
22
22
|
def callback(_name, _start, _finish, _message_id, values)
|
23
23
|
return if %w[CACHE SCHEMA].include? values[:name]
|
24
|
+
|
24
25
|
@queries << values[:sql] if @pattern.nil? || (values[:sql] =~ @pattern)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
class << self
|
29
|
-
|
30
|
-
|
30
|
+
attr_accessor :transaction_begin
|
31
|
+
attr_accessor :transaction_rollback
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :current_scale
|
35
|
+
|
36
|
+
self.transaction_begin = -> do
|
37
|
+
ActiveRecord::Base.connection.begin_transaction(joinable: false)
|
38
|
+
end
|
39
|
+
|
40
|
+
self.transaction_rollback = -> do
|
41
|
+
ActiveRecord::Base.connection.rollback_transaction
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(population: nil, scale_factors: nil, matching: nil)
|
45
|
+
@population = population
|
46
|
+
@scale_factors = scale_factors
|
47
|
+
@matching = matching
|
48
|
+
end
|
31
49
|
|
32
|
-
|
33
|
-
|
50
|
+
# rubocop:disable Metrics/MethodLength
|
51
|
+
def call
|
52
|
+
raise ArgumentError, "Block is required!" unless block_given?
|
34
53
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
54
|
+
results = []
|
55
|
+
collector = Collector.new(matching)
|
56
|
+
|
57
|
+
(scale_factors || NPlusOneControl.default_scale_factors).each do |scale|
|
58
|
+
@current_scale = scale
|
59
|
+
with_transaction do
|
60
|
+
population&.call(scale)
|
61
|
+
results << [scale, collector.call { yield }]
|
40
62
|
end
|
41
|
-
results
|
42
63
|
end
|
64
|
+
results
|
65
|
+
end
|
66
|
+
# rubocop:enable Metrics/MethodLength
|
43
67
|
|
44
|
-
|
68
|
+
private
|
45
69
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
70
|
+
def with_transaction
|
71
|
+
transaction_begin.call
|
72
|
+
yield
|
73
|
+
ensure
|
74
|
+
transaction_rollback.call
|
75
|
+
end
|
76
|
+
|
77
|
+
def transaction_begin
|
78
|
+
self.class.transaction_begin
|
79
|
+
end
|
80
|
+
|
81
|
+
def transaction_rollback
|
82
|
+
self.class.transaction_rollback
|
53
83
|
end
|
84
|
+
|
85
|
+
attr_reader :population, :scale_factors, :matching
|
54
86
|
end
|
55
87
|
end
|
@@ -8,21 +8,40 @@ module NPlusOneControl
|
|
8
8
|
def assert_perform_constant_number_of_queries(
|
9
9
|
populate: nil,
|
10
10
|
matching: nil,
|
11
|
-
scale_factors: nil
|
11
|
+
scale_factors: nil,
|
12
|
+
warmup: nil
|
12
13
|
)
|
13
14
|
|
14
15
|
raise ArgumentError, "Block is required" unless block_given?
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
warming_up warmup
|
18
|
+
|
19
|
+
@executor = NPlusOneControl::Executor.new(
|
20
|
+
population: populate || population_method,
|
18
21
|
matching: matching || /^SELECT/i,
|
19
22
|
scale_factors: scale_factors || NPlusOneControl.default_scale_factors
|
20
|
-
)
|
23
|
+
)
|
24
|
+
|
25
|
+
queries = @executor.call { yield }
|
21
26
|
|
22
27
|
counts = queries.map(&:last).map(&:size)
|
23
28
|
|
24
29
|
assert counts.max == counts.min, NPlusOneControl.failure_message(queries)
|
25
30
|
end
|
31
|
+
|
32
|
+
def current_scale
|
33
|
+
@executor&.current_scale
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def warming_up(warmup)
|
39
|
+
(warmup || methods.include?(:warmup) ? method(:warmup) : nil)&.call
|
40
|
+
end
|
41
|
+
|
42
|
+
def population_method
|
43
|
+
methods.include?(:populate) ? method(:populate) : nil
|
44
|
+
end
|
26
45
|
end
|
27
46
|
end
|
28
47
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
gem "rspec-core", ">= 3.5"
|
4
|
+
|
3
5
|
require "n_plus_one_control"
|
4
6
|
require "n_plus_one_control/rspec/dsl"
|
5
7
|
require "n_plus_one_control/rspec/matcher"
|
@@ -11,5 +13,6 @@ module NPlusOneControl
|
|
11
13
|
end
|
12
14
|
|
13
15
|
::RSpec.configure do |config|
|
14
|
-
config.extend NPlusOneControl::RSpec::DSL, n_plus_one: true
|
16
|
+
config.extend NPlusOneControl::RSpec::DSL::ClassMethods, n_plus_one: true
|
17
|
+
config.include NPlusOneControl::RSpec::DSL, n_plus_one: true
|
15
18
|
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
RSpec.shared_context "n_plus_one_control" do
|
4
4
|
# Helper to access populate block from within example/matcher
|
5
5
|
let(:n_plus_one_populate) do |ex|
|
6
|
-
if ex.example_group.populate.nil?
|
7
|
-
raise(
|
8
|
-
<<-MSG
|
9
|
-
Populate block is missing!
|
6
|
+
return if ex.example_group.populate.nil?
|
10
7
|
|
11
|
-
Please provide populate callback, e.g.:
|
12
|
-
|
13
|
-
populate { |n| n.times { create_some_stuff } }
|
14
|
-
MSG
|
15
|
-
)
|
16
|
-
end
|
17
8
|
->(n) { ex.instance_exec(n, &ex.example_group.populate) }
|
18
9
|
end
|
10
|
+
|
11
|
+
let(:n_plus_one_warmup) do |ex|
|
12
|
+
return if ex.example_group.warmup.nil?
|
13
|
+
|
14
|
+
-> { ex.instance_exec(&ex.example_group.warmup) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.include_context "n_plus_one_control", n_plus_one: true
|
19
20
|
end
|
@@ -2,14 +2,32 @@
|
|
2
2
|
|
3
3
|
module NPlusOneControl
|
4
4
|
module RSpec
|
5
|
-
#
|
5
|
+
# Includes scale method into RSpec Example
|
6
6
|
module DSL
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
# Extends RSpec ExampleGroup with populate & warmup methods
|
8
|
+
module ClassMethods
|
9
|
+
# Setup warmup block, wich will run before matching
|
10
|
+
# for example, if using cache, then later queries
|
11
|
+
# will perform less DB queries than first
|
12
|
+
def warmup
|
13
|
+
return @warmup unless block_given?
|
11
14
|
|
12
|
-
|
15
|
+
@warmup = Proc.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Setup populate callback, which is used
|
19
|
+
# to prepare data for each run.
|
20
|
+
def populate
|
21
|
+
return @populate unless block_given?
|
22
|
+
|
23
|
+
@populate = Proc.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_accessor :executor
|
28
|
+
|
29
|
+
def current_scale
|
30
|
+
executor&.current_scale
|
13
31
|
end
|
14
32
|
end
|
15
33
|
end
|
@@ -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,17 +23,21 @@
|
|
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
|
25
32
|
|
26
|
-
@
|
33
|
+
@matcher_execution_context.executor = NPlusOneControl::Executor.new(
|
27
34
|
population: populate,
|
28
35
|
matching: pattern,
|
29
|
-
scale_factors: @factors
|
30
|
-
&actual
|
36
|
+
scale_factors: @factors
|
31
37
|
)
|
32
38
|
|
39
|
+
@queries = @matcher_execution_context.executor.call(&actual)
|
40
|
+
|
33
41
|
counts = @queries.map(&:last).map(&:size)
|
34
42
|
|
35
43
|
counts.max == counts.min
|
@@ -41,3 +49,4 @@
|
|
41
49
|
|
42
50
|
failure_message { |_actual| NPlusOneControl.failure_message(@queries) }
|
43
51
|
end
|
52
|
+
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: n_plus_one_control
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.4.1
|
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: 2020-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.10'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.10'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 0.61.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 0.61.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: activerecord
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,16 +112,16 @@ dependencies:
|
|
112
112
|
name: sqlite3
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 1.3.6
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: 1.3.6
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: pry-byebug
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,26 +139,16 @@ dependencies:
|
|
139
139
|
description: "\n RSpec and Minitest matchers to prevent N+1 queries problem.\n\n
|
140
140
|
\ Evaluates code under consideration several times with different scale factors\n
|
141
141
|
\ to make sure that the number of DB queries behaves as expected (i.e. O(1) instead
|
142
|
-
of O(N)).\n
|
143
|
-
\ populate { |n| create_list(:post, n) }\n\n specify do\n expect
|
144
|
-
{ get :index }.to perform_constant_number_of_queries\n end\n end\n ```\n
|
145
|
-
\ "
|
142
|
+
of O(N)).\n "
|
146
143
|
email:
|
147
144
|
- dementiev.vm@gmail.com
|
148
145
|
executables: []
|
149
146
|
extensions: []
|
150
147
|
extra_rdoc_files: []
|
151
148
|
files:
|
152
|
-
-
|
153
|
-
- ".rspec"
|
154
|
-
- ".rubocop.yml"
|
155
|
-
- ".travis.yml"
|
156
|
-
- Gemfile
|
149
|
+
- CHANGELOG.md
|
157
150
|
- LICENSE.txt
|
158
151
|
- README.md
|
159
|
-
- Rakefile
|
160
|
-
- bin/console
|
161
|
-
- bin/setup
|
162
152
|
- lib/n_plus_one_control.rb
|
163
153
|
- lib/n_plus_one_control/executor.rb
|
164
154
|
- lib/n_plus_one_control/minitest.rb
|
@@ -167,19 +157,15 @@ files:
|
|
167
157
|
- lib/n_plus_one_control/rspec/dsl.rb
|
168
158
|
- lib/n_plus_one_control/rspec/matcher.rb
|
169
159
|
- lib/n_plus_one_control/version.rb
|
170
|
-
- n_plus_one_control.gemspec
|
171
|
-
- spec/n_plus_one_control/executor_spec.rb
|
172
|
-
- spec/n_plus_one_control/rspec_spec.rb
|
173
|
-
- spec/n_plus_one_control_spec.rb
|
174
|
-
- spec/spec_helper.rb
|
175
|
-
- spec/support/post.rb
|
176
|
-
- spec/support/user.rb
|
177
|
-
- tests/minitest_test.rb
|
178
|
-
- tests/test_helper.rb
|
179
160
|
homepage: http://github.com/palkan/n_plus_one_control
|
180
161
|
licenses:
|
181
162
|
- MIT
|
182
|
-
metadata:
|
163
|
+
metadata:
|
164
|
+
bug_tracker_uri: http://github.com/palkan/n_plus_one_control/issues
|
165
|
+
changelog_uri: https://github.com/palkan/n_plus_one_control/blob/master/CHANGELOG.md
|
166
|
+
documentation_uri: http://github.com/palkan/n_plus_one_control
|
167
|
+
homepage_uri: http://github.com/palkan/n_plus_one_control
|
168
|
+
source_code_uri: http://github.com/palkan/n_plus_one_control
|
183
169
|
post_install_message:
|
184
170
|
rdoc_options: []
|
185
171
|
require_paths:
|
@@ -195,8 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
181
|
- !ruby/object:Gem::Version
|
196
182
|
version: '0'
|
197
183
|
requirements: []
|
198
|
-
|
199
|
-
rubygems_version: 2.6.13
|
184
|
+
rubygems_version: 3.0.6
|
200
185
|
signing_key:
|
201
186
|
specification_version: 4
|
202
187
|
summary: RSpec and Minitest matchers to prevent N+1 queries problem
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
AllCops:
|
2
|
-
Include:
|
3
|
-
- 'lib/**/*.rb'
|
4
|
-
- 'lib/**/*.rake'
|
5
|
-
- 'spec/**/*.rb'
|
6
|
-
Exclude:
|
7
|
-
- 'bin/**/*'
|
8
|
-
- 'spec/dummy/**/*'
|
9
|
-
- 'tmp/**/*'
|
10
|
-
- 'Rakefile'
|
11
|
-
- 'Gemfile'
|
12
|
-
- '*.gemspec'
|
13
|
-
DisplayCopNames: true
|
14
|
-
StyleGuideCopsOnly: false
|
15
|
-
TargetRubyVersion: 2.4
|
16
|
-
|
17
|
-
Rails:
|
18
|
-
Enabled: false
|
19
|
-
|
20
|
-
Style/AccessorMethodName:
|
21
|
-
Enabled: false
|
22
|
-
|
23
|
-
Style/TrivialAccessors:
|
24
|
-
Enabled: false
|
25
|
-
|
26
|
-
Style/Documentation:
|
27
|
-
Exclude:
|
28
|
-
- 'spec/**/*.rb'
|
29
|
-
- 'tests/**/*.rb'
|
30
|
-
|
31
|
-
Style/StringLiterals:
|
32
|
-
Enabled: false
|
33
|
-
|
34
|
-
Style/RegexpLiteral:
|
35
|
-
Enabled: false
|
36
|
-
|
37
|
-
Style/SpaceInsideStringInterpolation:
|
38
|
-
EnforcedStyle: no_space
|
39
|
-
|
40
|
-
Style/ClassAndModuleChildren:
|
41
|
-
Enabled: false
|
42
|
-
|
43
|
-
Style/BlockDelimiters:
|
44
|
-
Exclude:
|
45
|
-
- 'spec/**/*.rb'
|
46
|
-
|
47
|
-
Lint/AmbiguousRegexpLiteral:
|
48
|
-
Enabled: false
|
49
|
-
|
50
|
-
|
51
|
-
Metrics/MethodLength:
|
52
|
-
Exclude:
|
53
|
-
- 'spec/**/*.rb'
|
54
|
-
|
55
|
-
Metrics/AbcSize:
|
56
|
-
Max: 20
|
57
|
-
|
58
|
-
Metrics/LineLength:
|
59
|
-
Max: 100
|
60
|
-
Exclude:
|
61
|
-
- 'spec/**/*.rb'
|
62
|
-
|
63
|
-
Metrics/BlockLength:
|
64
|
-
Exclude:
|
65
|
-
- 'spec/**/*.rb'
|
66
|
-
|
67
|
-
Rails/Date:
|
68
|
-
Enabled: false
|
69
|
-
|
70
|
-
Rails/TimeZone:
|
71
|
-
Enabled: false
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
require "rspec/core/rake_task"
|
3
|
-
require "rubocop/rake_task"
|
4
|
-
require "rake/testtask"
|
5
|
-
|
6
|
-
Rake::TestTask.new do |t|
|
7
|
-
t.test_files = FileList['tests/**/*_test.rb']
|
8
|
-
end
|
9
|
-
|
10
|
-
RuboCop::RakeTask.new
|
11
|
-
RSpec::Core::RakeTask.new(:spec)
|
12
|
-
|
13
|
-
task :default => [:spec, :test, :rubocop]
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "n_plus_one_control"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|
data/bin/setup
DELETED
data/n_plus_one_control.gemspec
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'n_plus_one_control/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "n_plus_one_control"
|
8
|
-
spec.version = NPlusOneControl::VERSION
|
9
|
-
spec.authors = ["palkan"]
|
10
|
-
spec.email = ["dementiev.vm@gmail.com"]
|
11
|
-
|
12
|
-
spec.summary = "RSpec and Minitest matchers to prevent N+1 queries problem"
|
13
|
-
spec.required_ruby_version = '>= 2.0.0'
|
14
|
-
spec.description = %{
|
15
|
-
RSpec and Minitest matchers to prevent N+1 queries problem.
|
16
|
-
|
17
|
-
Evaluates code under consideration several times with different scale factors
|
18
|
-
to make sure that the number of DB queries behaves as expected (i.e. O(1) instead of O(N)).
|
19
|
-
|
20
|
-
Example:
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
context "N+1", :n_plus_one do
|
24
|
-
populate { |n| create_list(:post, n) }
|
25
|
-
|
26
|
-
specify do
|
27
|
-
expect { get :index }.to perform_constant_number_of_queries
|
28
|
-
end
|
29
|
-
end
|
30
|
-
```
|
31
|
-
}
|
32
|
-
spec.homepage = "http://github.com/palkan/n_plus_one_control"
|
33
|
-
spec.license = "MIT"
|
34
|
-
|
35
|
-
spec.files = `git ls-files`.split($/)
|
36
|
-
spec.require_paths = ["lib"]
|
37
|
-
|
38
|
-
spec.add_development_dependency "bundler", "~> 1.10"
|
39
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
-
spec.add_development_dependency "rspec", "~> 3.5"
|
41
|
-
spec.add_development_dependency "minitest", "~> 5.9"
|
42
|
-
spec.add_development_dependency "factory_girl", "~> 4.8.0"
|
43
|
-
spec.add_development_dependency "rubocop", "~> 0.49"
|
44
|
-
spec.add_development_dependency "activerecord", "~> 5.1"
|
45
|
-
spec.add_development_dependency "sqlite3"
|
46
|
-
spec.add_development_dependency "pry-byebug"
|
47
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
describe NPlusOneControl::Executor do
|
6
|
-
let(:populate) do
|
7
|
-
->(n) { create_list(:post, n) }
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:observable) do
|
11
|
-
-> { Post.find_each(&:user) }
|
12
|
-
end
|
13
|
-
|
14
|
-
it "raises when block is missing" do
|
15
|
-
expect { described_class.call(population: populate) }
|
16
|
-
.to raise_error(ArgumentError, "Block is required!")
|
17
|
-
end
|
18
|
-
|
19
|
-
it "raises when populate is missing" do
|
20
|
-
expect { described_class.call(&observable) }
|
21
|
-
.to raise_error(ArgumentError, /population/)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "returns correct counts for default scales" do
|
25
|
-
result = described_class.call(
|
26
|
-
population: populate,
|
27
|
-
&observable
|
28
|
-
)
|
29
|
-
|
30
|
-
expect(result.size).to eq 2
|
31
|
-
expect(result.first[0]).to eq 2
|
32
|
-
expect(result.first[1].size).to eq 3
|
33
|
-
expect(result.last[0]).to eq 3
|
34
|
-
expect(result.last[1].size).to eq 4
|
35
|
-
end
|
36
|
-
|
37
|
-
it "returns correct counts for custom scales" do
|
38
|
-
result = described_class.call(
|
39
|
-
population: populate,
|
40
|
-
scale_factors: [5, 10, 100],
|
41
|
-
&observable
|
42
|
-
)
|
43
|
-
|
44
|
-
expect(result.size).to eq 3
|
45
|
-
expect(result.first[0]).to eq 5
|
46
|
-
expect(result.first[1].size).to eq 6
|
47
|
-
expect(result.second[0]).to eq 10
|
48
|
-
expect(result.second[1].size).to eq 11
|
49
|
-
expect(result.last[0]).to eq 100
|
50
|
-
expect(result.last[1].size).to eq 101
|
51
|
-
end
|
52
|
-
|
53
|
-
it "returns correct counts with custom match" do
|
54
|
-
result = described_class.call(
|
55
|
-
population: populate,
|
56
|
-
matching: /users/,
|
57
|
-
&observable
|
58
|
-
)
|
59
|
-
|
60
|
-
expect(result.first[0]).to eq 2
|
61
|
-
expect(result.first[1].size).to eq 2
|
62
|
-
expect(result.last[0]).to eq 3
|
63
|
-
expect(result.last[1].size).to eq 3
|
64
|
-
end
|
65
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
describe NPlusOneControl::RSpec do
|
6
|
-
context "when no N+1", :n_plus_one do
|
7
|
-
populate { |n| create_list(:post, n) }
|
8
|
-
|
9
|
-
specify do
|
10
|
-
expect { Post.preload(:user).find_each { |p| p.user.name } }
|
11
|
-
.to perform_constant_number_of_queries
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context "when has N+1", :n_plus_one do
|
16
|
-
populate { |n| create_list(:post, n) }
|
17
|
-
|
18
|
-
specify do
|
19
|
-
expect do
|
20
|
-
expect { Post.find_each { |p| p.user.name } }
|
21
|
-
.to perform_constant_number_of_queries
|
22
|
-
end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when context is missing" do
|
27
|
-
specify do
|
28
|
-
expect do
|
29
|
-
expect { subject }.to perform_constant_number_of_queries
|
30
|
-
end.to raise_error(/missing tag/i)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
context "when populate is missing", :n_plus_one do
|
35
|
-
specify do
|
36
|
-
expect do
|
37
|
-
expect { subject }.to perform_constant_number_of_queries
|
38
|
-
end.to raise_error(/please provide populate/i)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "when negated" do
|
43
|
-
specify do
|
44
|
-
expect do
|
45
|
-
expect { subject }.not_to perform_constant_number_of_queries
|
46
|
-
end.to raise_error(/support negation/i)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context "when verbose", :n_plus_one do
|
51
|
-
populate { |n| create_list(:post, n) }
|
52
|
-
|
53
|
-
around(:each) do |ex|
|
54
|
-
NPlusOneControl.verbose = true
|
55
|
-
ex.run
|
56
|
-
NPlusOneControl.verbose = false
|
57
|
-
end
|
58
|
-
|
59
|
-
specify do
|
60
|
-
expect do
|
61
|
-
expect { Post.find_each { |p| p.user.name } }
|
62
|
-
.to perform_constant_number_of_queries
|
63
|
-
end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /select .+ from/i)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "with scale_factors", :n_plus_one do
|
68
|
-
populate { |n| create_list(:post, n) }
|
69
|
-
|
70
|
-
specify do
|
71
|
-
expect { Post.find_each { |p| p.user.name } }
|
72
|
-
.to perform_constant_number_of_queries.with_scale_factors(1, 1)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
context "with matching", :n_plus_one do
|
77
|
-
populate { |n| create_list(:post, n) }
|
78
|
-
|
79
|
-
specify do
|
80
|
-
expect { Post.find_each { |p| p.user.name } }
|
81
|
-
.to perform_constant_number_of_queries.matching(/posts/)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
4
|
-
require "n_plus_one_control/rspec"
|
5
|
-
require "benchmark"
|
6
|
-
require "active_record"
|
7
|
-
require "factory_girl"
|
8
|
-
require "pry-byebug"
|
9
|
-
|
10
|
-
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
11
|
-
|
12
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
13
|
-
|
14
|
-
RSpec.configure do |config|
|
15
|
-
config.mock_with :rspec
|
16
|
-
|
17
|
-
config.order = :random
|
18
|
-
config.filter_run focus: true
|
19
|
-
config.run_all_when_everything_filtered = true
|
20
|
-
|
21
|
-
config.include FactoryGirl::Syntax::Methods
|
22
|
-
|
23
|
-
config.before(:each) do
|
24
|
-
ActiveRecord::Base.connection.begin_transaction(joinable: false)
|
25
|
-
end
|
26
|
-
|
27
|
-
config.after(:each) do
|
28
|
-
ActiveRecord::Base.connection.rollback_transaction
|
29
|
-
end
|
30
|
-
end
|
data/spec/support/post.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
ActiveRecord::Schema.define do
|
4
|
-
create_table :posts do |t|
|
5
|
-
t.string :title
|
6
|
-
t.integer :user_id
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class Post < ActiveRecord::Base
|
11
|
-
belongs_to :user
|
12
|
-
end
|
13
|
-
|
14
|
-
FactoryGirl.define do
|
15
|
-
factory :post do
|
16
|
-
title "Title"
|
17
|
-
user
|
18
|
-
end
|
19
|
-
end
|
data/spec/support/user.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
ActiveRecord::Schema.define do
|
4
|
-
create_table :users do |t|
|
5
|
-
t.string :name
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class User < ActiveRecord::Base
|
10
|
-
has_many :posts
|
11
|
-
end
|
12
|
-
|
13
|
-
FactoryGirl.define do
|
14
|
-
factory :user do
|
15
|
-
name "John"
|
16
|
-
end
|
17
|
-
end
|
data/tests/minitest_test.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "test_helper"
|
4
|
-
|
5
|
-
class TestMinitest < Minitest::Test
|
6
|
-
def test_no_n_plus_one_error
|
7
|
-
populate = ->(n) { create_list(:post, n) }
|
8
|
-
|
9
|
-
assert_perform_constant_number_of_queries(populate: populate) do
|
10
|
-
Post.preload(:user).find_each { |p| p.user.name }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_with_n_plus_one_error
|
15
|
-
populate = ->(n) { create_list(:post, n) }
|
16
|
-
|
17
|
-
e = assert_raises Minitest::Assertion do
|
18
|
-
assert_perform_constant_number_of_queries(populate: populate) do
|
19
|
-
Post.find_each { |p| p.user.name }
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
assert_match "Expected to make the same number of queries", e.message
|
24
|
-
assert_match "3 for N=2", e.message
|
25
|
-
assert_match "4 for N=3", e.message
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_no_n_plus_one_error_with_scale_factors
|
29
|
-
populate = ->(n) { create_list(:post, n) }
|
30
|
-
|
31
|
-
assert_perform_constant_number_of_queries(
|
32
|
-
populate: populate,
|
33
|
-
scale_factors: [1, 1]
|
34
|
-
) do
|
35
|
-
Post.find_each { |p| p.user.name }
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_no_n_plus_one_error_with_matching
|
40
|
-
populate = ->(n) { create_list(:post, n) }
|
41
|
-
|
42
|
-
assert_perform_constant_number_of_queries(
|
43
|
-
populate: populate,
|
44
|
-
matching: /posts/
|
45
|
-
) do
|
46
|
-
Post.find_each { |p| p.user.name }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def populate(n)
|
51
|
-
create_list(:post, n)
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_fallback_to_populate_method
|
55
|
-
e = assert_raises Minitest::Assertion do
|
56
|
-
assert_perform_constant_number_of_queries do
|
57
|
-
Post.find_each { |p| p.user.name }
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
assert_match "Expected to make the same number of queries", e.message
|
62
|
-
end
|
63
|
-
end
|
data/tests/test_helper.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "minitest/autorun"
|
4
|
-
require "minitest/pride"
|
5
|
-
|
6
|
-
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
7
|
-
Thread.abort_on_exception = true
|
8
|
-
|
9
|
-
require "n_plus_one_control/minitest"
|
10
|
-
require "benchmark"
|
11
|
-
require "active_record"
|
12
|
-
require "factory_girl"
|
13
|
-
require "pry-byebug"
|
14
|
-
|
15
|
-
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
16
|
-
|
17
|
-
Dir["#{File.dirname(__FILE__)}/../spec/support/**/*.rb"].each { |f| require f }
|
18
|
-
|
19
|
-
module TransactionalTests
|
20
|
-
def setup
|
21
|
-
ActiveRecord::Base.connection.begin_transaction(joinable: false)
|
22
|
-
super
|
23
|
-
end
|
24
|
-
|
25
|
-
def teardown
|
26
|
-
super
|
27
|
-
ActiveRecord::Base.connection.rollback_transaction
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
Minitest::Test.prepend TransactionalTests
|
32
|
-
Minitest::Test.include FactoryGirl::Syntax::Methods
|