prosopite 1.0.2 → 1.0.6

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: 8c01f842ae2f77f85afad80b9333d9e08532763b0a2f4da8b947fd224bd8ad5e
4
- data.tar.gz: b7355dba60a61af1936fabc438838cd6c5e68c2bf99ad4f89250f0fbb56da6f7
3
+ metadata.gz: 77276ce50263c0d9e2e4a9e0c525924ac5810367f036d783197222819f8fb312
4
+ data.tar.gz: b43868ebc87b40d97997db1c7a77fb3ee34cb032a76a0b290adb174c8d04c6d3
5
5
  SHA512:
6
- metadata.gz: d667f984caa2c358dd1268a515d4784bad98d86c39f42535cc97c7161c5f9131dbdf8dd57786aebc6fc20b55d1fcc660f4700260db08e47df3400797e4f7e3da
7
- data.tar.gz: 1b31e428871d38f29fb3f46f1c637cc822addbc5c826366da5b98ebe149bc1af63912f69a8a50cec277ed8e2c34c185bdc11e3f58d302a43fedd97c5b20622cc
6
+ metadata.gz: 5fc318b3becb5095f93244d8e23699c27e604c43ba7a3dc489ee3939d19b23e26a47eb4957d6355f28fdaa18fb6ecaa2caf27721a03fcadadefa73a5cbcfec7e
7
+ data.tar.gz: e5954aa6aec45c26722f807dcb2fde6aae4c1090d097cc7172a306cdde9639b734d226826fa1d6dcab59a9d22857dc7901978eb4297287ac49b81a63b23ae0e1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prosopite (1.0.1)
4
+ prosopite (1.0.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -127,11 +127,12 @@ Prosopite auto-detection can be enabled on all controllers:
127
127
  ```ruby
128
128
  class ApplicationController < ActionController::Base
129
129
  unless Rails.env.production?
130
- before_action do
130
+ around_action :n_plus_one_detection
131
+
132
+ def n_plus_one_detection
131
133
  Prosopite.scan
132
- end
133
-
134
- after_action do
134
+ yield
135
+ ensure
135
136
  Prosopite.finish
136
137
  end
137
138
  end
@@ -181,7 +182,13 @@ WARNING: scan/finish should run before/after **each** test and NOT before/after
181
182
  Ignore notifications for call stacks containing one or more substrings:
182
183
 
183
184
  ```ruby
184
- Prosopite.allow_list = ['substring_in_call_stack']
185
+ Prosopite.allow_stack_paths = ['substring_in_call_stack']
186
+ ```
187
+
188
+ Ignore notifications matching a specific SQL query:
189
+
190
+ ```ruby
191
+ Prosopite.ignore_queries = [/regex_match/, "SELECT * from EXACT_STRING_MATCH"]
185
192
  ```
186
193
 
187
194
  ## Scanning code outside controllers or tests
@@ -202,6 +209,23 @@ Prosopite.scan do
202
209
  end
203
210
  ```
204
211
 
212
+ ## Pausing and resuming scans
213
+
214
+ Scans can be paused:
215
+
216
+ ```ruby
217
+ Prosopite.scan
218
+ # <code to scan>
219
+ Prosopite.pause
220
+ # <code that has n+1s>
221
+ Prosopite.resume
222
+ # <code to scan>
223
+ Prosopite.finish
224
+ ```
225
+
226
+ An example of when you might use this is if you are [testing Active Jobs inline](https://guides.rubyonrails.org/testing.html#testing-jobs),
227
+ and don't want to run Prosopite on background job code, just foreground app code. In that case you could write an [Active Job callback](https://edgeguides.rubyonrails.org/active_job_basics.html#callbacks) that pauses the scan while the job is running.
228
+
205
229
  ## Contributing
206
230
 
207
231
  Bug reports and pull requests are welcome on GitHub at https://github.com/charkost/prosopite.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prosopite
4
- VERSION = "1.0.2"
4
+ VERSION = "1.0.6"
5
5
  end
data/lib/prosopite.rb CHANGED
@@ -1,12 +1,21 @@
1
1
 
2
2
  module Prosopite
3
+ DEFAULT_ALLOW_LIST = %w(active_record/associations/preloader active_record/validations/uniqueness)
4
+
3
5
  class NPlusOneQueriesError < StandardError; end
4
6
  class << self
5
7
  attr_writer :raise,
6
8
  :stderr_logger,
7
9
  :rails_logger,
8
10
  :prosopite_logger,
9
- :allow_list
11
+ :allow_stack_paths,
12
+ :ignore_queries
13
+
14
+ def allow_list=(value)
15
+ puts "Prosopite.allow_list= is deprecated. Use Prosopite.allow_stack_paths= instead."
16
+
17
+ self.allow_stack_paths = value
18
+ end
10
19
 
11
20
  def scan
12
21
  tc[:prosopite_scan] ||= false
@@ -18,7 +27,7 @@ module Prosopite
18
27
  tc[:prosopite_query_holder] = Hash.new { |h, k| h[k] = [] }
19
28
  tc[:prosopite_query_caller] = {}
20
29
 
21
- @allow_list ||= []
30
+ @allow_stack_paths ||= []
22
31
 
23
32
  tc[:prosopite_scan] = true
24
33
 
@@ -36,6 +45,14 @@ module Prosopite
36
45
  Thread.current
37
46
  end
38
47
 
48
+ def pause
49
+ tc[:prosopite_scan] = false
50
+ end
51
+
52
+ def resume
53
+ tc[:prosopite_scan] = true
54
+ end
55
+
39
56
  def scan?
40
57
  tc[:prosopite_scan]
41
58
  end
@@ -62,14 +79,15 @@ module Prosopite
62
79
  end
63
80
  end
64
81
 
82
+ next unless fingerprints.uniq.size == 1
83
+
65
84
  kaller = tc[:prosopite_query_caller][location_key]
85
+ allow_list = (@allow_stack_paths + DEFAULT_ALLOW_LIST)
86
+ is_allowed = kaller.any? { |f| allow_list.any? { |s| f.include?(s) } }
66
87
 
67
- if fingerprints.uniq.size == 1 && !kaller.any? { |f| @allow_list.any? { |s| f.include?(s) } }
88
+ unless is_allowed
68
89
  queries = tc[:prosopite_query_holder][location_key]
69
-
70
- unless kaller.any? { |f| f.include?('active_record/validations/uniqueness') }
71
- tc[:prosopite_notifications][queries] = kaller
72
- end
90
+ tc[:prosopite_notifications][queries] = kaller
73
91
  end
74
92
  end
75
93
  end
@@ -170,6 +188,11 @@ module Prosopite
170
188
  str.split("\n").map { |line| "\e[91m#{line}\e[0m" }.join("\n")
171
189
  end
172
190
 
191
+ def ignore_query?(sql)
192
+ @ignore_queries ||= []
193
+ @ignore_queries.any? { |q| q === sql }
194
+ end
195
+
173
196
  def subscribe
174
197
  @subscribed ||= false
175
198
  return if @subscribed
@@ -177,7 +200,7 @@ module Prosopite
177
200
  ActiveSupport::Notifications.subscribe 'sql.active_record' do |_, _, _, _, data|
178
201
  sql, name = data[:sql], data[:name]
179
202
 
180
- if scan? && name != "SCHEMA" && sql.include?('SELECT') && data[:cached].nil?
203
+ if scan? && name != "SCHEMA" && sql.include?('SELECT') && data[:cached].nil? && !ignore_query?(sql)
181
204
  location_key = Digest::SHA1.hexdigest(caller.join)
182
205
 
183
206
  tc[:prosopite_query_counter][location_key] += 1
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prosopite
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mpampis Kostas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-25 00:00:00.000000000 Z
11
+ date: 2021-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry