n_plus_one_control 0.2.1 → 0.5.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.
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.3
5
- before_install: gem install bundler -v 1.13.6
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in n_plus_one_control.gemspec
4
- gemspec
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]
@@ -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
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -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
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- describe NPlusOneControl do
6
- it "has a version number" do
7
- expect(NPlusOneControl::VERSION).not_to be nil
8
- end
9
- end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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