prosopite 0.1.5 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e45d47e93e239165f045462c1247f33b4dae5796e1c852fd7a2a6ade0977ef2a
4
- data.tar.gz: 25c2afb9c1340d3e16c3fcd2ba880becdfc674c54aaecba4d83566c6585998de
3
+ metadata.gz: 3df6a0299972911e48ea44330995864e2cd2191ec66c199296ba324a5fa8e639
4
+ data.tar.gz: 4f9ee347a8cf52fd742ee66586c7e4b601c8436a4bcabe1b1afdef5e09293f44
5
5
  SHA512:
6
- metadata.gz: 3eb734873fc1c396b5312dc70fe91a4c10a8adb4658bce2302559e8d61db22cc6e54429806e99e3014308b1e236b69a34f75e59bd3b789f36145843390776998
7
- data.tar.gz: 626d2a7ebaf7553647672f137696adfe2aa70fe67589b7380c2366862c85022ee560dfbe2665f7580c4cd930a95d7c94e1a59557d4674c563379fef94e197846
6
+ metadata.gz: 668bc7e58bcabd77231adc831054d56ca572e1e9857f87736d1fd06048c1eeaac9c0c0ab970c6ed6f8ce70670ae51277c3c4f56012d0ec61240c0e728c1f3795
7
+ data.tar.gz: '0341920f4f1beddc2b9c46553f9284724ecddfe50f30579a7cc9627c93fdf2d3c714bb57fd453dd7a4783a86951b9dd2cb0a79e721189bce3b89eda0c119c8bf'
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
 
7
7
  gem "rake", "~> 13.0"
8
+ gem 'pg_query'
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prosopite (0.1.5)
5
- pg_query (~> 1.3)
4
+ prosopite (0.2.1)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
@@ -44,6 +43,7 @@ DEPENDENCIES
44
43
  activerecord
45
44
  factory_bot
46
45
  minitest
46
+ pg_query
47
47
  prosopite!
48
48
  pry
49
49
  rake (~> 13.0)
data/LICENSE.txt CHANGED
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright [yyyy] [name of copyright owner]
190
+ Copyright 2021 Mpampis Kostas
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -97,6 +97,12 @@ Add this line to your application's Gemfile:
97
97
  gem 'prosopite'
98
98
  ```
99
99
 
100
+ If you're **not** using MySQL/MariaDB, you should also add:
101
+
102
+ ```ruby
103
+ gem 'pg_query'
104
+ ```
105
+
100
106
  And then execute:
101
107
 
102
108
  $ bundle install
@@ -120,12 +126,14 @@ Prosopite auto-detection can be enabled on all controllers:
120
126
 
121
127
  ```ruby
122
128
  class ApplicationController < ActionController::Base
123
- before_action do
124
- Prosopite.scan
125
- end
126
-
127
- after_action do
128
- Prosopite.finish
129
+ unless Rails.env.production?
130
+ before_action do
131
+ Prosopite.scan
132
+ end
133
+
134
+ after_action do
135
+ Prosopite.finish
136
+ end
129
137
  end
130
138
  end
131
139
  ```
@@ -168,12 +176,12 @@ end
168
176
 
169
177
  WARNING: scan/finish should run before/after **each** test and NOT before/after the whole suite.
170
178
 
171
- ## Whitelisting
179
+ ## Allow list
172
180
 
173
181
  Ignore notifications for call stacks containing one or more substrings:
174
182
 
175
183
  ```ruby
176
- Prosopite.whitelist = ['substring_in_call_stack']
184
+ Prosopite.allow_list = ['substring_in_call_stack']
177
185
  ```
178
186
 
179
187
  ## Scanning code outside controllers or tests
data/lib/prosopite.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'pg_query'
2
1
 
3
2
  module Prosopite
4
3
  class NPlusOneQueriesError < StandardError; end
@@ -7,42 +6,46 @@ module Prosopite
7
6
  :stderr_logger,
8
7
  :rails_logger,
9
8
  :prosopite_logger,
10
- :whitelist
9
+ :allow_list
11
10
 
12
11
  def scan
13
- @scan ||= false
12
+ tc[:prosopite_scan] ||= false
14
13
  return if scan?
15
14
 
16
15
  subscribe
17
16
 
18
- @query_counter = Hash.new(0)
19
- @query_holder = Hash.new { |h, k| h[k] = [] }
20
- @query_caller = {}
17
+ tc[:prosopite_query_counter] = Hash.new(0)
18
+ tc[:prosopite_query_holder] = Hash.new { |h, k| h[k] = [] }
19
+ tc[:prosopite_query_caller] = {}
21
20
 
22
- @whitelist ||= []
21
+ @allow_list ||= []
23
22
 
24
- @scan = true
23
+ tc[:prosopite_scan] = true
24
+ end
25
+
26
+ def tc
27
+ Thread.current
25
28
  end
26
29
 
27
30
  def scan?
28
- @scan
31
+ tc[:prosopite_scan]
29
32
  end
30
33
 
31
34
  def finish
32
35
  return unless scan?
33
36
 
34
- @scan = false
37
+ tc[:prosopite_scan] = false
35
38
 
36
39
  create_notifications
37
- send_notifications if @notifications.present?
40
+ send_notifications if tc[:prosopite_notifications].present?
38
41
  end
39
42
 
40
43
  def create_notifications
41
- @notifications = {}
44
+ tc[:prosopite_notifications] = {}
42
45
 
43
- @query_counter.each do |location_key, count|
46
+ tc[:prosopite_query_counter].each do |location_key, count|
44
47
  if count > 1
45
- fingerprints = @query_holder[location_key].map do |q|
48
+ fingerprints = tc[:prosopite_query_holder][location_key].map do |q|
46
49
  begin
47
50
  fingerprint(q)
48
51
  rescue
@@ -50,13 +53,13 @@ module Prosopite
50
53
  end
51
54
  end
52
55
 
53
- kaller = @query_caller[location_key]
56
+ kaller = tc[:prosopite_query_caller][location_key]
54
57
 
55
- if fingerprints.uniq.size == 1 && !kaller.any? { |f| @whitelist.any? { |s| f.include?(s) } }
56
- queries = @query_holder[location_key]
58
+ if fingerprints.uniq.size == 1 && !kaller.any? { |f| @allow_list.any? { |s| f.include?(s) } }
59
+ queries = tc[:prosopite_query_holder][location_key]
57
60
 
58
61
  unless kaller.any? { |f| f.include?('active_record/validations/uniqueness') }
59
- @notifications[queries] = kaller
62
+ tc[:prosopite_notifications][queries] = kaller
60
63
  end
61
64
  end
62
65
  end
@@ -67,6 +70,12 @@ module Prosopite
67
70
  if ActiveRecord::Base.connection.adapter_name.downcase.include?('mysql')
68
71
  mysql_fingerprint(query)
69
72
  else
73
+ begin
74
+ require 'pg_query'
75
+ rescue LoadError => e
76
+ msg = "Could not load the 'pg_query' gem. Add `gem 'pg_query'` to your Gemfile"
77
+ raise LoadError, msg, e.backtrace
78
+ end
70
79
  PgQuery.fingerprint(query)
71
80
  end
72
81
  end
@@ -126,7 +135,7 @@ module Prosopite
126
135
 
127
136
  notifications_str = ''
128
137
 
129
- @notifications.each do |queries, kaller|
138
+ tc[:prosopite_notifications].each do |queries, kaller|
130
139
  notifications_str << "N+1 queries detected:\n"
131
140
  queries.each { |q| notifications_str << " #{q}\n" }
132
141
  notifications_str << "Call stack:\n"
@@ -136,8 +145,8 @@ module Prosopite
136
145
  notifications_str << "\n"
137
146
  end
138
147
 
139
- Rails.logger.warn(notifications_str) if @rails_logger
140
- $stderr.puts(notifications_str) if @stderr_logger
148
+ Rails.logger.warn(red(notifications_str)) if @rails_logger
149
+ $stderr.puts(red(notifications_str)) if @stderr_logger
141
150
 
142
151
  if @prosopite_logger
143
152
  File.open(File.join(Rails.root, 'log', 'prosopite.log'), 'a') do |f|
@@ -148,6 +157,10 @@ module Prosopite
148
157
  raise NPlusOneQueriesError.new(notifications_str) if @raise
149
158
  end
150
159
 
160
+ def red(str)
161
+ str.split("\n").map { |line| "\e[91m#{line}\e[0m" }.join("\n")
162
+ end
163
+
151
164
  def subscribe
152
165
  @subscribed ||= false
153
166
  return if @subscribed
@@ -158,11 +171,11 @@ module Prosopite
158
171
  if scan? && sql.include?('SELECT') && data[:cached].nil?
159
172
  location_key = Digest::SHA1.hexdigest(caller.join)
160
173
 
161
- @query_counter[location_key] += 1
162
- @query_holder[location_key] << sql
174
+ tc[:prosopite_query_counter][location_key] += 1
175
+ tc[:prosopite_query_holder][location_key] << sql
163
176
 
164
- if @query_counter[location_key] > 1
165
- @query_caller[location_key] = caller.dup
177
+ if tc[:prosopite_query_counter][location_key] > 1
178
+ tc[:prosopite_query_caller][location_key] = caller.dup
166
179
  end
167
180
  end
168
181
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prosopite
4
- VERSION = "0.1.5"
4
+ VERSION = "1.0.0"
5
5
  end
data/prosopite.gemspec CHANGED
@@ -24,8 +24,6 @@ Gem::Specification.new do |spec|
24
24
  end
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_runtime_dependency 'pg_query', '~> 1.3'
28
-
29
27
  spec.add_development_dependency "pry"
30
28
  spec.add_development_dependency "minitest"
31
29
  spec.add_development_dependency "factory_bot"
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prosopite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 1.0.0
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-02-27 00:00:00.000000000 Z
11
+ date: 2021-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: pg_query
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.3'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.3'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: pry
29
15
  requirement: !ruby/object:Gem::Requirement