query_owl 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +165 -0
- data/Rakefile +20 -0
- data/app/controllers/query_owl/application_controller.rb +4 -0
- data/app/jobs/query_owl/application_job.rb +4 -0
- data/app/mailers/query_owl/application_mailer.rb +6 -0
- data/app/models/query_owl/application_record.rb +5 -0
- data/config/routes.rb +2 -0
- data/lib/query_owl/configuration.rb +23 -0
- data/lib/query_owl/detector.rb +52 -0
- data/lib/query_owl/engine.rb +19 -0
- data/lib/query_owl/logger.rb +21 -0
- data/lib/query_owl/middleware.rb +18 -0
- data/lib/query_owl/query_tracker.rb +44 -0
- data/lib/query_owl/version.rb +3 -0
- data/lib/query_owl.rb +23 -0
- data/lib/tasks/query_owl_tasks.rake +4 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 45b067e5caa42b01df6cb84cce7541b77ee9bbee1f9f6d919ed554fb2386fd0e
|
|
4
|
+
data.tar.gz: 688d0b4f41edcf13e99bad7dbe6a4bdfe8ad40d458f908a06c2bc87b8ccbdbea
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 960d4332a40bd8531ccdda7b06d8e6a947b048c3c5f758fba374e62759668bbe43ae335e7d5dc4fc394785e2ea5fa6e13e1f7f8ca9c4e6f035f52b380815c982
|
|
7
|
+
data.tar.gz: 1764fbd57f9359a30866c2665a5698ede5bd4764fab90f10afa7984451086cc1d51fd8bf933828bfdff14a87631c753824b8cff7cd225a1474ca41b9efd5a0f3
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Chuck Smith
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# QueryOwl
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eclectic-coding/query_owl/actions/workflows/main.yml)
|
|
4
|
+
[](https://rubygems.org/gems/query_owl)
|
|
5
|
+
[](https://rubygems.org/gems/query_owl)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
[](https://codecov.io/gh/eclectic-coding/query_owl)
|
|
8
|
+
|
|
9
|
+
A leaner alternative to Bullet. QueryOwl detects N+1 queries and slow queries in development, logging structured warnings to your Rails logger — without the noise.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Features](#features)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Configuration](#configuration)
|
|
16
|
+
- [Log Output](#log-output)
|
|
17
|
+
- [Manual Testing in the Dummy App](#manual-testing-in-the-dummy-app)
|
|
18
|
+
- [Roadmap](#roadmap)
|
|
19
|
+
- [Contributing](#contributing)
|
|
20
|
+
- [License](#license)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **N+1 detection** — flags when the same SQL pattern fires 2+ times in a single request
|
|
27
|
+
- **Slow query detection** — flags queries exceeding a configurable threshold (default: 100ms)
|
|
28
|
+
- **Structured log output** — JSON-style warnings via `Rails.logger` with SQL, duration, count, and filtered backtrace
|
|
29
|
+
- **Zero overhead in production** — auto-enabled in development only
|
|
30
|
+
|
|
31
|
+
[↑ Back to top](#table-of-contents)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Add to your `Gemfile`:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
gem "query_owl"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then run:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
bundle install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
[↑ Back to top](#table-of-contents)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Create an initializer:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# config/initializers/query_owl.rb
|
|
59
|
+
QueryOwl.configure do |config|
|
|
60
|
+
config.enabled = Rails.env.development?
|
|
61
|
+
config.slow_query_threshold_ms = 100 # flag queries slower than this
|
|
62
|
+
config.n_plus_one_threshold = 2 # flag after this many repeated patterns
|
|
63
|
+
config.log_level = :warn # :warn | :info | :debug
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
[↑ Back to top](#table-of-contents)
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Log Output
|
|
72
|
+
|
|
73
|
+
When a problem is detected, QueryOwl writes a structured line to `Rails.logger`:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
[QueryOwl] {"type":"n_plus_one","sql":"SELECT * FROM posts WHERE user_id = ?","count":10,"backtrace":["app/controllers/posts_controller.rb:12"]}
|
|
77
|
+
[QueryOwl] {"type":"slow_query","sql":"SELECT * FROM reports WHERE ...","duration_ms":340}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
[↑ Back to top](#table-of-contents)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Manual Testing in the Dummy App
|
|
85
|
+
|
|
86
|
+
The gem ships with a minimal Rails app in `spec/dummy/` for manual verification.
|
|
87
|
+
|
|
88
|
+
**Start a console:**
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
cd spec/dummy
|
|
92
|
+
RAILS_ENV=development bin/rails console
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Trigger N+1 detection:**
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
QueryOwl.config.enabled = true
|
|
99
|
+
QueryOwl::QueryTracker.start!
|
|
100
|
+
Widget.all.each { |w| Widget.find(w.id) }
|
|
101
|
+
queries = QueryOwl::QueryTracker.stop!
|
|
102
|
+
events = QueryOwl::Detector.detect_n_plus_one(queries)
|
|
103
|
+
QueryOwl::Logger.log_events(events)
|
|
104
|
+
# => [QueryOwl] {"type":"n_plus_one","sql":"SELECT ...","count":3,...}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Trigger slow query detection:**
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
QueryOwl.config.slow_query_threshold_ms = 0 # flag everything
|
|
111
|
+
QueryOwl::QueryTracker.start!
|
|
112
|
+
Widget.all.to_a
|
|
113
|
+
queries = QueryOwl::QueryTracker.stop!
|
|
114
|
+
events = QueryOwl::Detector.detect_slow_queries(queries)
|
|
115
|
+
QueryOwl::Logger.log_events(events)
|
|
116
|
+
# => [QueryOwl] {"type":"slow_query","sql":"SELECT ...","duration_ms":...}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Full pipeline** (as it runs on every real HTTP request):
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
QueryOwl.config.slow_query_threshold_ms = 0
|
|
123
|
+
QueryOwl::QueryTracker.start!
|
|
124
|
+
Widget.all.each { |w| Widget.find(w.id) }
|
|
125
|
+
queries = QueryOwl::QueryTracker.stop!
|
|
126
|
+
events = QueryOwl::Detector.detect_n_plus_one(queries) +
|
|
127
|
+
QueryOwl::Detector.detect_slow_queries(queries)
|
|
128
|
+
QueryOwl::Logger.log_events(events)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Seed the dummy database first** (if needed):
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
cd spec/dummy
|
|
135
|
+
RAILS_ENV=development bin/rails db:migrate
|
|
136
|
+
RAILS_ENV=development bin/rails runner "3.times { |i| Widget.create!(name: \"Widget #{i}\") }"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
[↑ Back to top](#table-of-contents)
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Roadmap
|
|
144
|
+
|
|
145
|
+
See [ROADMAP.md](ROADMAP.md) for planned releases, including unused eager load detection (0.2.0) and a `/rails/slow_queries` dashboard endpoint (0.3.0).
|
|
146
|
+
|
|
147
|
+
[↑ Back to top](#table-of-contents)
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Contributing
|
|
152
|
+
|
|
153
|
+
1. Fork the repo and create a `feat/<name>` branch
|
|
154
|
+
2. Write specs for your change
|
|
155
|
+
3. Run `bundle exec rake` (lint + audit + tests) before opening a PR
|
|
156
|
+
|
|
157
|
+
[↑ Back to top](#table-of-contents)
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT — see [MIT-LICENSE](MIT-LICENSE).
|
|
164
|
+
|
|
165
|
+
[↑ Back to top](#table-of-contents)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
|
|
3
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
|
4
|
+
load "rails/tasks/engine.rake"
|
|
5
|
+
|
|
6
|
+
require "bundler/gem_tasks"
|
|
7
|
+
require 'rubocop/rake_task'
|
|
8
|
+
require 'bundler/audit/task'
|
|
9
|
+
require 'rspec/core/rake_task'
|
|
10
|
+
|
|
11
|
+
RuboCop::RakeTask.new(:lint)
|
|
12
|
+
Bundler::Audit::Task.new
|
|
13
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
14
|
+
|
|
15
|
+
task default: [:lint, :'bundle:audit:update', 'bundle:audit:check', :spec]
|
|
16
|
+
|
|
17
|
+
desc "Run performance benchmarks"
|
|
18
|
+
task :benchmark do
|
|
19
|
+
ruby "benchmarks/benchmark.rb"
|
|
20
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module QueryOwl
|
|
2
|
+
class Configuration
|
|
3
|
+
VALID_LOG_LEVELS = %i[debug info warn].freeze
|
|
4
|
+
|
|
5
|
+
attr_reader :log_level
|
|
6
|
+
attr_accessor :enabled, :slow_query_threshold_ms, :n_plus_one_threshold
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@enabled = Rails.env.development?
|
|
10
|
+
@slow_query_threshold_ms = 100
|
|
11
|
+
@n_plus_one_threshold = 2
|
|
12
|
+
@log_level = :warn
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def log_level=(level)
|
|
16
|
+
unless VALID_LOG_LEVELS.include?(level)
|
|
17
|
+
raise ArgumentError, "log_level must be one of #{VALID_LOG_LEVELS.inspect}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@log_level = level
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module QueryOwl
|
|
2
|
+
module Detector
|
|
3
|
+
# Matches numeric literals, single-quoted strings, and IN-list contents.
|
|
4
|
+
NORMALIZE_PATTERNS = [
|
|
5
|
+
[/'[^']*'/, "?"],
|
|
6
|
+
[/\b\d+\b/, "?"],
|
|
7
|
+
[/\s+/, " "]
|
|
8
|
+
].freeze
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def detect_n_plus_one(queries)
|
|
12
|
+
threshold = QueryOwl.config.n_plus_one_threshold
|
|
13
|
+
|
|
14
|
+
queries
|
|
15
|
+
.reject { |q| q[:cached] }
|
|
16
|
+
.group_by { |q| normalize(q[:sql]) }
|
|
17
|
+
.filter_map do |normalized_sql, group|
|
|
18
|
+
next if group.length < threshold
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
type: :n_plus_one,
|
|
22
|
+
sql: normalized_sql,
|
|
23
|
+
count: group.length,
|
|
24
|
+
backtrace: group.first[:backtrace]
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def detect_slow_queries(queries)
|
|
30
|
+
threshold = QueryOwl.config.slow_query_threshold_ms
|
|
31
|
+
|
|
32
|
+
queries.filter_map do |q|
|
|
33
|
+
next if q[:cached]
|
|
34
|
+
next if q[:duration_ms] < threshold
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
type: :slow_query,
|
|
38
|
+
sql: normalize(q[:sql]),
|
|
39
|
+
duration_ms: q[:duration_ms],
|
|
40
|
+
backtrace: q[:backtrace]
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def normalize(sql)
|
|
46
|
+
NORMALIZE_PATTERNS
|
|
47
|
+
.reduce(sql.to_s) { |s, (pattern, replacement)| s.gsub(pattern, replacement) }
|
|
48
|
+
.strip
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module QueryOwl
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace QueryOwl
|
|
4
|
+
config.generators.api_only = true
|
|
5
|
+
|
|
6
|
+
initializer "query_owl.subscribe" do
|
|
7
|
+
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
|
8
|
+
next unless QueryOwl.config.enabled
|
|
9
|
+
|
|
10
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
11
|
+
QueryTracker.record(event)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer "query_owl.request_tracking" do |app|
|
|
16
|
+
app.middleware.use(Middleware)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module QueryOwl
|
|
4
|
+
module Logger
|
|
5
|
+
PREFIX = "[QueryOwl]"
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def log_events(events)
|
|
9
|
+
return if events.empty?
|
|
10
|
+
|
|
11
|
+
events.each { |event| write(event) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def write(event)
|
|
17
|
+
Rails.logger.public_send(QueryOwl.config.log_level, "#{PREFIX} #{event.to_json}")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module QueryOwl
|
|
2
|
+
class Middleware
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
return @app.call(env) unless QueryOwl.config.enabled
|
|
9
|
+
|
|
10
|
+
QueryTracker.start!
|
|
11
|
+
@app.call(env)
|
|
12
|
+
ensure
|
|
13
|
+
queries = QueryTracker.stop!
|
|
14
|
+
events = Detector.detect_n_plus_one(queries) + Detector.detect_slow_queries(queries)
|
|
15
|
+
Logger.log_events(events)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module QueryOwl
|
|
2
|
+
module QueryTracker
|
|
3
|
+
IGNORED_PATTERNS = /^(SCHEMA|EXPLAIN|BEGIN|COMMIT|ROLLBACK|SAVEPOINT|RELEASE)/i
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
def start!
|
|
7
|
+
Thread.current[:query_owl_queries] = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def record(event)
|
|
11
|
+
return unless tracking?
|
|
12
|
+
return if event.payload[:name] == "SCHEMA"
|
|
13
|
+
return if event.payload[:sql].to_s.match?(IGNORED_PATTERNS)
|
|
14
|
+
|
|
15
|
+
queries << {
|
|
16
|
+
sql: event.payload[:sql],
|
|
17
|
+
duration_ms: event.duration.round(2),
|
|
18
|
+
cached: event.payload[:cached],
|
|
19
|
+
backtrace: filtered_backtrace
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def queries
|
|
24
|
+
Thread.current[:query_owl_queries] ||= []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def stop!
|
|
28
|
+
collected = queries.dup
|
|
29
|
+
Thread.current[:query_owl_queries] = nil
|
|
30
|
+
collected
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def tracking?
|
|
34
|
+
!Thread.current[:query_owl_queries].nil?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def filtered_backtrace
|
|
40
|
+
caller.grep_v(%r{/gems/|/rubygems/|/ruby/gems/|lib/query_owl/}).first(5)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/query_owl.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "query_owl/version"
|
|
2
|
+
require "query_owl/configuration"
|
|
3
|
+
require "query_owl/query_tracker"
|
|
4
|
+
require "query_owl/detector"
|
|
5
|
+
require "query_owl/logger"
|
|
6
|
+
require "query_owl/middleware"
|
|
7
|
+
require "query_owl/engine"
|
|
8
|
+
|
|
9
|
+
module QueryOwl
|
|
10
|
+
class << self
|
|
11
|
+
def configure
|
|
12
|
+
yield config
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def config
|
|
16
|
+
@config ||= Configuration.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reset_config!
|
|
20
|
+
@config = Configuration.new
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: query_owl
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Chuck Smith
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.1'
|
|
26
|
+
description: A leaner alternative to Bullet. Detects N+1 queries and slow queries
|
|
27
|
+
in development, logging structured warnings to your Rails logger without the noise.
|
|
28
|
+
email:
|
|
29
|
+
- eclectic-coding@users.noreply.github.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- MIT-LICENSE
|
|
35
|
+
- README.md
|
|
36
|
+
- Rakefile
|
|
37
|
+
- app/controllers/query_owl/application_controller.rb
|
|
38
|
+
- app/jobs/query_owl/application_job.rb
|
|
39
|
+
- app/mailers/query_owl/application_mailer.rb
|
|
40
|
+
- app/models/query_owl/application_record.rb
|
|
41
|
+
- config/routes.rb
|
|
42
|
+
- lib/query_owl.rb
|
|
43
|
+
- lib/query_owl/configuration.rb
|
|
44
|
+
- lib/query_owl/detector.rb
|
|
45
|
+
- lib/query_owl/engine.rb
|
|
46
|
+
- lib/query_owl/logger.rb
|
|
47
|
+
- lib/query_owl/middleware.rb
|
|
48
|
+
- lib/query_owl/query_tracker.rb
|
|
49
|
+
- lib/query_owl/version.rb
|
|
50
|
+
- lib/tasks/query_owl_tasks.rake
|
|
51
|
+
homepage: https://github.com/eclectic-coding/query_owl
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata:
|
|
55
|
+
homepage_uri: https://github.com/eclectic-coding/query_owl
|
|
56
|
+
source_code_uri: https://github.com/eclectic-coding/query_owl
|
|
57
|
+
changelog_uri: https://github.com/eclectic-coding/query_owl/blob/main/CHANGELOG.md
|
|
58
|
+
rdoc_options: []
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '3.3'
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 3.6.9
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: Structured N+1 and slow query warnings for Rails development.
|
|
75
|
+
test_files: []
|