n_1_finder 0.0.4 → 0.0.5
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 +4 -4
- data/Readme.md +22 -21
- data/lib/n_1_finder.rb +28 -44
- data/lib/n_1_finder/adapters/base_adapter.rb +32 -35
- data/lib/n_1_finder/adapters/factory.rb +16 -0
- data/lib/n_1_finder/errors/base.rb +7 -0
- data/lib/n_1_finder/errors/invalid_logger.rb +4 -0
- data/lib/n_1_finder/errors/invalid_orm.rb +4 -0
- data/lib/n_1_finder/version.rb +1 -1
- data/spec/n_1_finder/adapters/factory_spec.rb +36 -0
- data/spec/n_1_finder_spec.rb +3 -3
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 961ceb11825e90ca10011fb5d9be594976e31d30
|
4
|
+
data.tar.gz: 2223479f079ca9e0c1e1384243e76ad249babf64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a15d14a85b1293769a7f11b77ff2fd0009b15e43502c8b1bb3ca3393d4f8682bfe71eceab4a5064e7470879498d835b6c4d8a3da7d73556a06089f03d98e0930
|
7
|
+
data.tar.gz: d5814f4732a51d49a1fde41f3a2d5ef7b9125910aae695ea686a959d6b02e0d531a0d0bc572fa20e11998bbd2de6f552e6830a122a61ea7d4ef2fba1174cc2ce
|
data/Readme.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
[](https://badge.fury.io/rb/n_1_finder)
|
3
3
|
[](https://travis-ci.org/aglushkov/n1_finder)
|
4
4
|
[](http://inch-ci.org/github/aglushkov/n1_finder)
|
5
|
+
[](https://codeclimate.com/github/aglushkov/n1_finder)
|
5
6
|
|
6
7
|
This gem helps to find N+1 queries.
|
7
8
|
|
@@ -12,27 +13,6 @@
|
|
12
13
|
## Installation
|
13
14
|
`gem 'n_1_finder', group: :development`
|
14
15
|
|
15
|
-
## Configuration
|
16
|
-
### Logger
|
17
|
-
Default is `Logger.new(STDOUT)`
|
18
|
-
|
19
|
-
Logger can be any instance of `Logger` class.
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
N1Finder.logger = Logger.new('log/n1.log')
|
23
|
-
```
|
24
|
-
|
25
|
-
### ORM
|
26
|
-
Default is `:active_record` if you have activerecord gem installed.
|
27
|
-
|
28
|
-
Default is `:sequel` if you have sequel gem installed.
|
29
|
-
|
30
|
-
Allowed values are `:active_record` and `:sequel`
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
N1Finder.orm = :active_record
|
34
|
-
```
|
35
|
-
|
36
16
|
## Using
|
37
17
|
Include middleware to your Rack app:
|
38
18
|
```ruby
|
@@ -68,6 +48,27 @@ N+1 QUERY DETECTED:
|
|
68
48
|
SELECT "comments".* FROM "comments" WHERE "comments"."user_id" = $1, user_id = 947
|
69
49
|
```
|
70
50
|
|
51
|
+
## Configuration
|
52
|
+
### Logger
|
53
|
+
Default is `Logger.new(STDOUT)`
|
54
|
+
|
55
|
+
Logger can be any instance of `Logger` class.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
N1Finder.logger = Logger.new('log/n1.log')
|
59
|
+
```
|
60
|
+
|
61
|
+
### ORM
|
62
|
+
Default is `:active_record` if you have activerecord gem installed.
|
63
|
+
|
64
|
+
Default is `:sequel` if you have sequel gem installed.
|
65
|
+
|
66
|
+
Allowed values are `:active_record` and `:sequel`
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
N1Finder.orm = :active_record
|
70
|
+
```
|
71
|
+
|
71
72
|
## Running tests
|
72
73
|
- Copy `spec/.env.example` file to `spec/.env` and set database credentials.
|
73
74
|
```bash
|
data/lib/n_1_finder.rb
CHANGED
@@ -1,32 +1,7 @@
|
|
1
|
-
require 'n_1_finder/logger'
|
2
|
-
require 'n_1_finder/middleware'
|
3
|
-
require 'n_1_finder/query'
|
4
|
-
require 'n_1_finder/n_1_query'
|
5
|
-
require 'n_1_finder/storage'
|
6
|
-
require 'n_1_finder/adapters/base_adapter'
|
7
|
-
require 'n_1_finder/adapters/active_record_adapter'
|
8
|
-
require 'n_1_finder/adapters/sequel_adapter'
|
9
|
-
require 'n_1_finder/adapters/null_adapter'
|
10
|
-
|
11
1
|
# Main class
|
12
2
|
class N1Finder
|
13
|
-
# Base error
|
14
|
-
# @api private
|
15
|
-
class Invalid < StandardError; end
|
16
|
-
|
17
|
-
# Raised when specifying invalid ORM
|
18
|
-
# @api private
|
19
|
-
class InvalidORM < Invalid; end
|
20
|
-
|
21
|
-
# Raised when specifying invalid logger
|
22
|
-
# @api private
|
23
|
-
class InvalidLogger < Invalid; end
|
24
|
-
|
25
3
|
# Supported ORM adapters
|
26
|
-
ORM_ADAPTERS =
|
27
|
-
active_record: N1Finder::Adapters::ActiveRecordAdapter,
|
28
|
-
sequel: N1Finder::Adapters::SequelAdapter
|
29
|
-
}.freeze
|
4
|
+
ORM_ADAPTERS = [:active_record, :sequel].freeze
|
30
5
|
|
31
6
|
class << self
|
32
7
|
# Supported ORM adapters
|
@@ -37,10 +12,10 @@ class N1Finder
|
|
37
12
|
#
|
38
13
|
# @return [void] result of block call
|
39
14
|
def find
|
40
|
-
storage =
|
15
|
+
storage = Storage.new
|
41
16
|
result = catch_queries(storage) { yield }
|
42
|
-
n1_queries =
|
43
|
-
|
17
|
+
n1_queries = N1Query.generate_by(storage.queries)
|
18
|
+
Logger.new.log(n1_queries)
|
44
19
|
|
45
20
|
result
|
46
21
|
end
|
@@ -59,11 +34,11 @@ class N1Finder
|
|
59
34
|
# @param [Logger] custom_logger
|
60
35
|
# Must be instance of `Logger`
|
61
36
|
#
|
62
|
-
# @raise [N1Finder::InvalidLogger] If custom_logger is not an instance of `Logger`.
|
37
|
+
# @raise [N1Finder::Errors::InvalidLogger] If custom_logger is not an instance of `Logger`.
|
63
38
|
#
|
64
39
|
# @return [Logger]
|
65
40
|
def logger=(custom_logger)
|
66
|
-
raise
|
41
|
+
raise Errors::InvalidLogger unless custom_logger.is_a?(::Logger)
|
67
42
|
|
68
43
|
@logger = custom_logger
|
69
44
|
end
|
@@ -75,12 +50,11 @@ class N1Finder
|
|
75
50
|
#
|
76
51
|
# @return [Symbol, nil]
|
77
52
|
def orm
|
78
|
-
@orm ||=
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
53
|
+
@orm ||= if defined?(ActiveRecord)
|
54
|
+
:active_record
|
55
|
+
elsif defined?(Sequel)
|
56
|
+
:sequel
|
57
|
+
end
|
84
58
|
end
|
85
59
|
|
86
60
|
# Configure ORM
|
@@ -88,11 +62,11 @@ class N1Finder
|
|
88
62
|
# @param [Symbol] custom_orm
|
89
63
|
# Must be `:active_record` or `:sequel`
|
90
64
|
#
|
91
|
-
# @raise [N1Finder::InvalidORM] If custom_orm is not in allowed list.
|
65
|
+
# @raise [N1Finder::Errors::InvalidORM] If custom_orm is not in allowed list.
|
92
66
|
#
|
93
67
|
# @return [Symbol]
|
94
68
|
def orm=(custom_orm)
|
95
|
-
raise
|
69
|
+
raise Errors::InvalidORM unless ORM_ADAPTERS.include?(custom_orm)
|
96
70
|
|
97
71
|
@orm = custom_orm
|
98
72
|
end
|
@@ -100,12 +74,22 @@ class N1Finder
|
|
100
74
|
private
|
101
75
|
|
102
76
|
def catch_queries(storage)
|
103
|
-
adapter =
|
77
|
+
adapter = Adapters::Factory.get(orm, storage)
|
104
78
|
adapter.exec(&Proc.new)
|
105
79
|
end
|
106
|
-
|
107
|
-
def adapter_class
|
108
|
-
ORM_ADAPTERS[orm] || N1Finder::Adapters::NullAdapter
|
109
|
-
end
|
110
80
|
end
|
111
81
|
end
|
82
|
+
|
83
|
+
require 'n_1_finder/logger'
|
84
|
+
require 'n_1_finder/middleware'
|
85
|
+
require 'n_1_finder/query'
|
86
|
+
require 'n_1_finder/n_1_query'
|
87
|
+
require 'n_1_finder/storage'
|
88
|
+
require 'n_1_finder/adapters/factory'
|
89
|
+
require 'n_1_finder/adapters/base_adapter'
|
90
|
+
require 'n_1_finder/adapters/active_record_adapter'
|
91
|
+
require 'n_1_finder/adapters/sequel_adapter'
|
92
|
+
require 'n_1_finder/adapters/null_adapter'
|
93
|
+
require 'n_1_finder/errors/base'
|
94
|
+
require 'n_1_finder/errors/invalid_orm'
|
95
|
+
require 'n_1_finder/errors/invalid_logger'
|
@@ -1,46 +1,43 @@
|
|
1
1
|
##
|
2
|
-
#
|
2
|
+
# Adds common functionality to other adapters
|
3
3
|
#
|
4
|
-
|
5
|
-
#
|
6
|
-
|
7
|
-
# An alias that we create for MAIN_METHOD function
|
8
|
-
MAIN_METHOD_ALIAS = :main_method_alias
|
4
|
+
class N1Finder::Adapters::BaseAdapter
|
5
|
+
# An alias that we create for MAIN_METHOD function
|
6
|
+
MAIN_METHOD_ALIAS = :main_method_alias
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
# @!attribute [r] storage
|
9
|
+
# Storage that stores queries
|
10
|
+
attr_reader :storage
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def initialize(storage)
|
13
|
+
@storage = storage
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
16
|
+
# Replaces original query execution function (defined in MAIN_METHOD)
|
17
|
+
# with our function that collects all queries and calls original function.
|
18
|
+
# After passed block yileds, replaces our function with origianal and
|
19
|
+
# removes our function.
|
20
|
+
#
|
21
|
+
# @yield passed block
|
22
|
+
#
|
23
|
+
# @return passed block execution result
|
24
|
+
def exec
|
25
|
+
set_trap
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
remove_trap
|
29
|
+
end
|
32
30
|
|
33
|
-
|
31
|
+
private
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
def remove_trap
|
34
|
+
main_query_method = self.class::MAIN_METHOD
|
35
|
+
main_method_alias = MAIN_METHOD_ALIAS
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
37
|
+
database_class.class_eval do
|
38
|
+
remove_method(main_query_method)
|
39
|
+
alias_method main_query_method, main_method_alias
|
40
|
+
remove_method(main_method_alias)
|
44
41
|
end
|
45
42
|
end
|
46
43
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Combine ORM adapters
|
2
|
+
module N1Finder::Adapters
|
3
|
+
# Adapters factory
|
4
|
+
class Factory
|
5
|
+
# Constructs new instance of adapter
|
6
|
+
def self.get(key, storage)
|
7
|
+
adapter_class = case key
|
8
|
+
when :active_record then N1Finder::Adapters::ActiveRecordAdapter
|
9
|
+
when :sequel then N1Finder::Adapters::SequelAdapter
|
10
|
+
else N1Finder::Adapters::NullAdapter
|
11
|
+
end
|
12
|
+
|
13
|
+
adapter_class.new(storage)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/n_1_finder/version.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec.describe N1Finder::Adapters::Factory do
|
2
|
+
describe '.get' do
|
3
|
+
let(:storage) { 'STORAGE' }
|
4
|
+
subject { described_class.get(key, storage) }
|
5
|
+
|
6
|
+
context 'getting active_record' do
|
7
|
+
let(:key) { :active_record }
|
8
|
+
it 'returns active_record adapter' do
|
9
|
+
expect(subject).to be_a(N1Finder::Adapters::ActiveRecordAdapter)
|
10
|
+
end
|
11
|
+
it 'sets storage' do
|
12
|
+
expect(subject.storage).to eq storage
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'getting sequel' do
|
17
|
+
let(:key) { :sequel }
|
18
|
+
it 'returns sequel adapter' do
|
19
|
+
expect(subject).to be_a(N1Finder::Adapters::SequelAdapter)
|
20
|
+
end
|
21
|
+
it 'sets storage' do
|
22
|
+
expect(subject.storage).to eq storage
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'getting other adapter' do
|
27
|
+
let(:key) { :other }
|
28
|
+
it 'returns sequel adapter' do
|
29
|
+
expect(subject).to be_a(N1Finder::Adapters::NullAdapter)
|
30
|
+
end
|
31
|
+
it 'sets storage' do
|
32
|
+
expect(subject.storage).to eq storage
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/spec/n_1_finder_spec.rb
CHANGED
@@ -42,7 +42,7 @@ RSpec.describe N1Finder do
|
|
42
42
|
context 'when logger is not a Logger' do
|
43
43
|
let(:logger) { nil }
|
44
44
|
it 'raises error' do
|
45
|
-
expect { subject }.to raise_error described_class::InvalidLogger
|
45
|
+
expect { subject }.to raise_error described_class::Errors::InvalidLogger
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -50,7 +50,7 @@ RSpec.describe N1Finder do
|
|
50
50
|
describe '#orm=' do
|
51
51
|
subject { described_class.orm = orm }
|
52
52
|
context 'when provided supported orm' do
|
53
|
-
let(:orm) { described_class::ORM_ADAPTERS.
|
53
|
+
let(:orm) { described_class::ORM_ADAPTERS.sample }
|
54
54
|
it 'sets orm' do
|
55
55
|
subject
|
56
56
|
expect(described_class.orm).to eq orm
|
@@ -60,7 +60,7 @@ RSpec.describe N1Finder do
|
|
60
60
|
context 'when orm is not supported' do
|
61
61
|
let(:orm) { :mongo }
|
62
62
|
it 'raises error' do
|
63
|
-
expect { subject }.to raise_error
|
63
|
+
expect { subject }.to raise_error described_class::Errors::InvalidORM
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: n_1_finder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Glushkov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Finds N+1 queries
|
14
14
|
email: aglushkov@shakuro.com
|
@@ -20,8 +20,12 @@ files:
|
|
20
20
|
- lib/n_1_finder.rb
|
21
21
|
- lib/n_1_finder/adapters/active_record_adapter.rb
|
22
22
|
- lib/n_1_finder/adapters/base_adapter.rb
|
23
|
+
- lib/n_1_finder/adapters/factory.rb
|
23
24
|
- lib/n_1_finder/adapters/null_adapter.rb
|
24
25
|
- lib/n_1_finder/adapters/sequel_adapter.rb
|
26
|
+
- lib/n_1_finder/errors/base.rb
|
27
|
+
- lib/n_1_finder/errors/invalid_logger.rb
|
28
|
+
- lib/n_1_finder/errors/invalid_orm.rb
|
25
29
|
- lib/n_1_finder/logger.rb
|
26
30
|
- lib/n_1_finder/middleware.rb
|
27
31
|
- lib/n_1_finder/n_1_query.rb
|
@@ -30,6 +34,7 @@ files:
|
|
30
34
|
- lib/n_1_finder/version.rb
|
31
35
|
- spec/.env.example
|
32
36
|
- spec/n_1_finder/adapters/active_record_adapter_spec.rb
|
37
|
+
- spec/n_1_finder/adapters/factory_spec.rb
|
33
38
|
- spec/n_1_finder/adapters/null_adapter_spec.rb
|
34
39
|
- spec/n_1_finder/adapters/sequel_adapter_spec.rb
|
35
40
|
- spec/n_1_finder/features/active_record_mysql_spec.rb
|