n_one 1.0.0 → 1.0.3

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
2
  SHA256:
3
- metadata.gz: b2b2ba1b5d986f35ed074dac942c0448f1ae64217403a5946398da54ad93e54f
4
- data.tar.gz: 14cbea5e0fd6b84932f5d8b74957f8d99e97b0572eb062af824c9cf41745be0d
3
+ metadata.gz: 1d2ff9afa1af0f84f3291494c21e47d28c94d67c61abea2b9a10bba5482c1ed8
4
+ data.tar.gz: 2e50029cb362fbcd9f773d7dd5e4d86120a9cef425b0a167b13d659441de57fa
5
5
  SHA512:
6
- metadata.gz: ea4606afb1b0ef8cc5417745f76b7260b628e77c645896e6ba919096340692cf6602a5d7645133fe5dbf79021d0664a738b6e870bbbea72728b6cb514bac638e
7
- data.tar.gz: '08567625417d3881bc257bde3da51db8e500eb06a2ffdc2dd32d2800026d8d00df6a9ffb81cc6638d66bb96bde769f015c231a31a0444e7e8deb9b84c4efda04'
6
+ metadata.gz: 2b5585f38769f8e3a5228111d01a0b894217197f8597cc608e70b0d3d6fa35cf2047f79e3eb71c86a4a2a3a300eea4655a59e090868d4fe51008b9e175294d29
7
+ data.tar.gz: 78718a6479979500f2e6466bbbb25d60a122c0b5ea3199d86b39a212e1b80ba1ee423778b2df14ccc8eb36e270b76e48306a9bd01f7e86b0147c141681ba872e
@@ -4,7 +4,7 @@ jobs:
4
4
  test:
5
5
  strategy:
6
6
  matrix:
7
- ruby: [2.5, '3.0', head]
7
+ ruby: [2.6, '3.0']
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
10
  - uses: actions/checkout@v2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- n_one (1.0.0)
4
+ n_one (1.0.3)
5
5
  pg_query
6
6
 
7
7
  GEM
@@ -23,6 +23,8 @@ GEM
23
23
  concurrent-ruby (1.1.8)
24
24
  factory_bot (6.1.0)
25
25
  activesupport (>= 5.0.0)
26
+ google-protobuf (3.19.4)
27
+ google-protobuf (3.19.4-x86_64-linux)
26
28
  i18n (1.8.9)
27
29
  concurrent-ruby (~> 1.0)
28
30
  method_source (1.0.0)
@@ -30,7 +32,8 @@ GEM
30
32
  parallel (1.20.1)
31
33
  parser (3.0.1.0)
32
34
  ast (~> 2.4.1)
33
- pg_query (1.3.0)
35
+ pg_query (2.1.3)
36
+ google-protobuf (>= 3.19.2)
34
37
  pry (0.14.0)
35
38
  coderay (~> 1.1)
36
39
  method_source (~> 1.0)
data/README.md CHANGED
@@ -74,11 +74,52 @@ end
74
74
  Ignore notifications for call stacks containing one or more substrings:
75
75
 
76
76
  ```ruby
77
- NOne.scan!(whitelist: 'myapp/lib/known_n_plus_ones/') do
78
- example.run
77
+ NOne.scan!(whitelist: ['myapp/lib/known_n_plus_ones/']) do
78
+ example.run
79
+ end
80
+ ```
81
+
82
+ ## Ignore names
83
+
84
+ Ignore queries with names:
85
+
86
+ ```ruby
87
+ NOne.scan!(ignore_names: ['SCHEMA']) do
88
+ example.run
89
+ end
90
+ ```
91
+
92
+ It will skip schema queries(e.g. for column names of a given table)
93
+
94
+ ## Stack trace sanitizing
95
+
96
+ Sanitize the call stack trace that is used to calculate the query fingerprint:
97
+
98
+ ```ruby
99
+ sanitizer = lambda do |stacktrace|
100
+ stacktrace.reject { |s| s.include?('/active_record/relation/delegation.rb') }
101
+ end
102
+
103
+ NOne.scan!(stacktrace_sanitizer: sanitizer) do
104
+ example.run
105
+ end
106
+ ```
107
+
108
+ Consider the following example:
109
+
110
+ ```ruby
111
+ class Foo < ActiveRecord::Base
112
+ def self.bar
113
+ first(5)
79
114
  end
115
+ end
116
+
117
+ 2.times { Foo.all.bar }
80
118
  ```
81
119
 
120
+ The subsequent `Foo.all.bar` call here will not be recognized as an N+1 query since it will have a different call stack trace (see the reason [here](https://github.com/rails/rails/blob/9a400d808bdbebd5ea50cebc79bde591d2669017/activerecord/lib/active_record/relation/delegation.rb#L82-L85)).
121
+ This can be fixed with the `stacktrace_sanitizer` option as described above.
122
+
82
123
  ## Contributing
83
124
 
84
125
  Bug reports and pull requests are welcome on GitHub at https://github.com/prikha/n_one.
data/lib/n_one/runner.rb CHANGED
@@ -2,8 +2,17 @@
2
2
 
3
3
  module NOne
4
4
  class Runner # :nodoc:
5
- def initialize(whitelist: [])
5
+ # Instantiation
6
+ #
7
+ # @param whitelist [<String>] frame substrings to be ignored
8
+ # @param ignore_names [<String>] query names to be ignored
9
+ # @param stacktrace_sanitizer [Proc<Array<String>>] if given, used to filter the stack trace
10
+ # that is used to calculate the location key
11
+ #
12
+ def initialize(whitelist: [], ignore_names: [], stacktrace_sanitizer: nil)
6
13
  @whitelist = ['active_record/validations/uniqueness'] + whitelist
14
+ @ignore_names = ignore_names
15
+ @stacktrace_sanitizer = stacktrace_sanitizer
7
16
  end
8
17
 
9
18
  def scan(&block)
@@ -20,7 +29,7 @@ module NOne
20
29
 
21
30
  private
22
31
 
23
- attr_reader :store, :whitelist
32
+ attr_reader :store, :whitelist, :ignore_names
24
33
 
25
34
  def init_store
26
35
  @store = {}
@@ -45,17 +54,20 @@ module NOne
45
54
  end.compact
46
55
  end
47
56
 
48
- def record_sql(&block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
57
+ def record_sql(&block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
49
58
  subscriber = ActiveSupport::Notifications.subscribe 'sql.active_record' do |_, _, _, _, data|
50
59
  sql = data[:sql]
51
60
  cached = data[:cached]
52
61
 
53
62
  next if !sql.include?('SELECT') || cached
63
+ next if ignore_names.include?(data[:name])
54
64
 
55
65
  sql_fingerprint = Query.fingerprint(sql)
56
66
  next unless sql_fingerprint
57
67
 
58
- location_key = Digest::SHA1.hexdigest(caller.join)
68
+ stacktrace = caller
69
+ stacktrace = @stacktrace_sanitizer.call(stacktrace) if @stacktrace_sanitizer
70
+ location_key = Digest::SHA1.hexdigest(stacktrace.join)
59
71
 
60
72
  store["#{sql_fingerprint}_#{location_key}"] ||= {
61
73
  count: 0,
@@ -66,7 +78,7 @@ module NOne
66
78
  store["#{sql_fingerprint}_#{location_key}"][:count] += 1
67
79
  store["#{sql_fingerprint}_#{location_key}"][:sql] << sql
68
80
  store["#{sql_fingerprint}_#{location_key}"][:sql].uniq!
69
- store["#{sql_fingerprint}_#{location_key}"][:caller] ||= caller.dup
81
+ store["#{sql_fingerprint}_#{location_key}"][:caller] ||= stacktrace
70
82
  end
71
83
 
72
84
  block.call
data/lib/n_one/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NOne
4
- VERSION = '1.0.0'
4
+ VERSION = '1.0.3'
5
5
  end
data/lib/n_one.rb CHANGED
@@ -30,11 +30,11 @@ module NOne
30
30
 
31
31
  module_function
32
32
 
33
- def scan(whitelist: [], &block)
34
- Runner.new(whitelist: whitelist).scan(&block)
33
+ def scan(**args, &block)
34
+ Runner.new(**args).scan(&block)
35
35
  end
36
36
 
37
- def scan!(whitelist: [], &block)
38
- Runner.new(whitelist: whitelist).scan!(&block)
37
+ def scan!(**args, &block)
38
+ Runner.new(**args).scan!(&block)
39
39
  end
40
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: n_one
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Prikhodko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-23 00:00:00.000000000 Z
11
+ date: 2022-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg_query
@@ -66,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  requirements: []
69
- rubygems_version: 3.1.2
69
+ rubygems_version: 3.0.3.1
70
70
  signing_key:
71
71
  specification_version: 4
72
72
  summary: N+1 auto-detection for ActiveRecord and PostgreSQL