arlequin 0.1.0 → 0.1.2

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: 64dab272387e862b66546a8de88779cc36ffbdfaa0a6082125b8404509fec181
4
- data.tar.gz: 0c1d6fe9c4af985484476e81645ce54a1a1391ed19765276dd9d1915bc12b514
3
+ metadata.gz: 3083b16c52cd3b8f777180ee21b6f838768bb8da81bd5079312fad9896ba14a7
4
+ data.tar.gz: c6daedc9d726407f3a26aabae5aa38865d2470cb1e24cf9f1fffd47d4106d761
5
5
  SHA512:
6
- metadata.gz: 6dab0e3d89077eccf1a50591045e75720bade0b638ff4ab5c2e892c7297b6b12c314cdb25047094b50ac4a45895e18ebb6e2a742409dc290cb7d8620fa911995
7
- data.tar.gz: 194ba82a1169e3abec5d4114bd1fae8e3c51b169103f8ae3c2ae8fea8cb4bedc596e0681fbb75840fa6b94f0ea34e1e8acf431eb71e6ddf5efb9097fca91f503
6
+ metadata.gz: 8ae1b8e8d9d99928a76d562bbdfef3b2effe853f60fb2cfe2943a098532d919cb99a9c7f4b79396d09d88f4361794cd9656e62850c0465f370b8fc7dc6b1cfd3
7
+ data.tar.gz: a361f0f8055ed4e5f97580ede4ba096c6d6edb3638fe3c51e6078e54e833345a6f78f9d8524b118a78f860224b326e41d9a45d36e643db1917ea76c18c214f3f
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /doc/
2
+ /tmp/
3
+ /log/
4
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ rubocop-rails-omakase: rubocop.yml
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rubocop-rails-omakase", require: false, group: [ :development ]
data/Gemfile.lock ADDED
@@ -0,0 +1,232 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ arlequin (0.1.1)
5
+ rails (>= 5.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ actioncable (7.1.3.2)
11
+ actionpack (= 7.1.3.2)
12
+ activesupport (= 7.1.3.2)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ zeitwerk (~> 2.6)
16
+ actionmailbox (7.1.3.2)
17
+ actionpack (= 7.1.3.2)
18
+ activejob (= 7.1.3.2)
19
+ activerecord (= 7.1.3.2)
20
+ activestorage (= 7.1.3.2)
21
+ activesupport (= 7.1.3.2)
22
+ mail (>= 2.7.1)
23
+ net-imap
24
+ net-pop
25
+ net-smtp
26
+ actionmailer (7.1.3.2)
27
+ actionpack (= 7.1.3.2)
28
+ actionview (= 7.1.3.2)
29
+ activejob (= 7.1.3.2)
30
+ activesupport (= 7.1.3.2)
31
+ mail (~> 2.5, >= 2.5.4)
32
+ net-imap
33
+ net-pop
34
+ net-smtp
35
+ rails-dom-testing (~> 2.2)
36
+ actionpack (7.1.3.2)
37
+ actionview (= 7.1.3.2)
38
+ activesupport (= 7.1.3.2)
39
+ nokogiri (>= 1.8.5)
40
+ racc
41
+ rack (>= 2.2.4)
42
+ rack-session (>= 1.0.1)
43
+ rack-test (>= 0.6.3)
44
+ rails-dom-testing (~> 2.2)
45
+ rails-html-sanitizer (~> 1.6)
46
+ actiontext (7.1.3.2)
47
+ actionpack (= 7.1.3.2)
48
+ activerecord (= 7.1.3.2)
49
+ activestorage (= 7.1.3.2)
50
+ activesupport (= 7.1.3.2)
51
+ globalid (>= 0.6.0)
52
+ nokogiri (>= 1.8.5)
53
+ actionview (7.1.3.2)
54
+ activesupport (= 7.1.3.2)
55
+ builder (~> 3.1)
56
+ erubi (~> 1.11)
57
+ rails-dom-testing (~> 2.2)
58
+ rails-html-sanitizer (~> 1.6)
59
+ activejob (7.1.3.2)
60
+ activesupport (= 7.1.3.2)
61
+ globalid (>= 0.3.6)
62
+ activemodel (7.1.3.2)
63
+ activesupport (= 7.1.3.2)
64
+ activerecord (7.1.3.2)
65
+ activemodel (= 7.1.3.2)
66
+ activesupport (= 7.1.3.2)
67
+ timeout (>= 0.4.0)
68
+ activestorage (7.1.3.2)
69
+ actionpack (= 7.1.3.2)
70
+ activejob (= 7.1.3.2)
71
+ activerecord (= 7.1.3.2)
72
+ activesupport (= 7.1.3.2)
73
+ marcel (~> 1.0)
74
+ activesupport (7.1.3.2)
75
+ base64
76
+ bigdecimal
77
+ concurrent-ruby (~> 1.0, >= 1.0.2)
78
+ connection_pool (>= 2.2.5)
79
+ drb
80
+ i18n (>= 1.6, < 2)
81
+ minitest (>= 5.1)
82
+ mutex_m
83
+ tzinfo (~> 2.0)
84
+ ast (2.4.2)
85
+ base64 (0.2.0)
86
+ bigdecimal (3.1.8)
87
+ builder (3.3.0)
88
+ concurrent-ruby (1.3.3)
89
+ connection_pool (2.4.1)
90
+ crass (1.0.6)
91
+ date (3.3.4)
92
+ drb (2.2.1)
93
+ erubi (1.13.0)
94
+ globalid (1.2.1)
95
+ activesupport (>= 6.1)
96
+ i18n (1.14.5)
97
+ concurrent-ruby (~> 1.0)
98
+ io-console (0.7.2)
99
+ irb (1.14.0)
100
+ rdoc (>= 4.0.0)
101
+ reline (>= 0.4.2)
102
+ json (2.7.2)
103
+ language_server-protocol (3.17.0.3)
104
+ loofah (2.22.0)
105
+ crass (~> 1.0.2)
106
+ nokogiri (>= 1.12.0)
107
+ mail (2.8.1)
108
+ mini_mime (>= 0.1.1)
109
+ net-imap
110
+ net-pop
111
+ net-smtp
112
+ marcel (1.0.4)
113
+ mini_mime (1.1.5)
114
+ minitest (5.24.1)
115
+ mutex_m (0.2.0)
116
+ net-imap (0.4.14)
117
+ date
118
+ net-protocol
119
+ net-pop (0.1.2)
120
+ net-protocol
121
+ net-protocol (0.2.2)
122
+ timeout
123
+ net-smtp (0.5.0)
124
+ net-protocol
125
+ nio4r (2.7.3)
126
+ nokogiri (1.16.6-arm64-darwin)
127
+ racc (~> 1.4)
128
+ parallel (1.25.1)
129
+ parser (3.3.4.0)
130
+ ast (~> 2.4.1)
131
+ racc
132
+ psych (5.1.2)
133
+ stringio
134
+ racc (1.8.0)
135
+ rack (3.1.7)
136
+ rack-session (2.0.0)
137
+ rack (>= 3.0.0)
138
+ rack-test (2.1.0)
139
+ rack (>= 1.3)
140
+ rackup (2.1.0)
141
+ rack (>= 3)
142
+ webrick (~> 1.8)
143
+ rails (7.1.3.2)
144
+ actioncable (= 7.1.3.2)
145
+ actionmailbox (= 7.1.3.2)
146
+ actionmailer (= 7.1.3.2)
147
+ actionpack (= 7.1.3.2)
148
+ actiontext (= 7.1.3.2)
149
+ actionview (= 7.1.3.2)
150
+ activejob (= 7.1.3.2)
151
+ activemodel (= 7.1.3.2)
152
+ activerecord (= 7.1.3.2)
153
+ activestorage (= 7.1.3.2)
154
+ activesupport (= 7.1.3.2)
155
+ bundler (>= 1.15.0)
156
+ railties (= 7.1.3.2)
157
+ rails-dom-testing (2.2.0)
158
+ activesupport (>= 5.0.0)
159
+ minitest
160
+ nokogiri (>= 1.6)
161
+ rails-html-sanitizer (1.6.0)
162
+ loofah (~> 2.21)
163
+ nokogiri (~> 1.14)
164
+ railties (7.1.3.2)
165
+ actionpack (= 7.1.3.2)
166
+ activesupport (= 7.1.3.2)
167
+ irb
168
+ rackup (>= 1.0.0)
169
+ rake (>= 12.2)
170
+ thor (~> 1.0, >= 1.2.2)
171
+ zeitwerk (~> 2.6)
172
+ rainbow (3.1.1)
173
+ rake (13.2.1)
174
+ rdoc (6.7.0)
175
+ psych (>= 4.0.0)
176
+ regexp_parser (2.9.2)
177
+ reline (0.5.9)
178
+ io-console (~> 0.5)
179
+ rexml (3.3.2)
180
+ strscan
181
+ rubocop (1.65.0)
182
+ json (~> 2.3)
183
+ language_server-protocol (>= 3.17.0)
184
+ parallel (~> 1.10)
185
+ parser (>= 3.3.0.2)
186
+ rainbow (>= 2.2.2, < 4.0)
187
+ regexp_parser (>= 2.4, < 3.0)
188
+ rexml (>= 3.2.5, < 4.0)
189
+ rubocop-ast (>= 1.31.1, < 2.0)
190
+ ruby-progressbar (~> 1.7)
191
+ unicode-display_width (>= 2.4.0, < 3.0)
192
+ rubocop-ast (1.31.3)
193
+ parser (>= 3.3.1.0)
194
+ rubocop-minitest (0.35.1)
195
+ rubocop (>= 1.61, < 2.0)
196
+ rubocop-ast (>= 1.31.1, < 2.0)
197
+ rubocop-performance (1.21.1)
198
+ rubocop (>= 1.48.1, < 2.0)
199
+ rubocop-ast (>= 1.31.1, < 2.0)
200
+ rubocop-rails (2.25.1)
201
+ activesupport (>= 4.2.0)
202
+ rack (>= 1.1)
203
+ rubocop (>= 1.33.0, < 2.0)
204
+ rubocop-ast (>= 1.31.1, < 2.0)
205
+ rubocop-rails-omakase (1.0.0)
206
+ rubocop
207
+ rubocop-minitest
208
+ rubocop-performance
209
+ rubocop-rails
210
+ ruby-progressbar (1.13.0)
211
+ stringio (3.1.1)
212
+ strscan (3.1.0)
213
+ thor (1.3.1)
214
+ timeout (0.4.1)
215
+ tzinfo (2.0.6)
216
+ concurrent-ruby (~> 1.0)
217
+ unicode-display_width (2.5.0)
218
+ webrick (1.8.1)
219
+ websocket-driver (0.7.6)
220
+ websocket-extensions (>= 0.1.0)
221
+ websocket-extensions (0.1.5)
222
+ zeitwerk (2.6.16)
223
+
224
+ PLATFORMS
225
+ arm64-darwin-22
226
+
227
+ DEPENDENCIES
228
+ arlequin!
229
+ rubocop-rails-omakase
230
+
231
+ BUNDLED WITH
232
+ 2.4.19
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Arlequin
2
+
3
+ **Arlequin** is a Ruby gem that helps detect and warn about N+1 SQL queries in Rails applications. It includes middleware that integrates seamlessly with your Rails application to improve performance by identifying inefficient database queries.
4
+
5
+ ## Features
6
+
7
+ - **N+1 Query Detection**: Automatically detects and warns about N+1 query problems.
8
+ - **Integration**: Easy integration with Rails applications through a Railtie.
9
+ - **Performance Insights**: Provides detailed insights into query performance issues.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'arlequin', group: [ :development ]
17
+ ```
18
+
19
+ Then execute:
20
+
21
+ ```bash
22
+ $ bundle install
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic Setup
28
+
29
+ To start using Arlequin, simply add the following to your Rails application’s configuration file (config/application.rb):
30
+
31
+ ```ruby
32
+ require 'arlequin'
33
+ ```
34
+
35
+ ### Middleware
36
+ Arlequin integrates with Rails as middleware. It will automatically start monitoring SQL queries for N+1 problems.
37
+ No additional setup is required beyond including it in your Gemfile and configuration.
38
+
39
+ ## Example
40
+
41
+ Suppose you have a Rails application with a Post model that has many Comments. If you inadvertently write code like:
42
+
43
+ ```ruby
44
+ @posts = Post.all
45
+ @posts.each do |post|
46
+ puts post.comments.count
47
+ end
48
+ ```
49
+
50
+ Arlequin will detect this N+1 query issue and warn you about it in the UI.
51
+
52
+ ## Development
53
+
54
+ To contribute to Arlequin, clone the repository and run the test suite:
55
+
56
+ ```bash
57
+ git clone https://github.com/dominicgoulet/arlequin.git
58
+ cd arlequin
59
+ bundle install
60
+ rake test
61
+ ```
62
+
63
+ Make sure to write tests for any new features or bug fixes you add.
64
+ Follow the existing coding style and conventions used in the project.
65
+
66
+ ## Contributing
67
+
68
+ We welcome contributions! Please submit pull requests and issues through GitHub.
69
+ Ensure your code adheres to the project’s style and includes appropriate tests.
70
+
71
+ ## License
72
+
73
+ Arlequin is released under the MIT License.
74
+
75
+ ## Contact
76
+
77
+ For any questions or feedback, feel free to reach out to us at [dominic@dominicgoulet.com].
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest/test_task"
4
+
5
+ Minitest::TestTask.create(:test) do |t|
6
+ t.libs << "test"
7
+ t.libs << "lib"
8
+ t.warning = false
9
+ t.test_globs = [ "test/**/*_test.rb" ]
10
+ end
11
+
12
+ task default: :test
data/arlequin.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "arlequin"
5
+ s.version = "0.1.2"
6
+ s.summary = "Arlequin performance logger gem"
7
+ s.description = "Performance Logger Gem"
8
+
9
+ s.license = "MIT"
10
+
11
+ s.author = "Dominic Goulet"
12
+ s.email = "dominic@dominicgoulet.com"
13
+ s.homepage = "https://github.com/dominicgoulet/arlequin"
14
+
15
+ # Specify which files should be added to the gem when it is released.
16
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
17
+ s.files = Dir.chdir(File.expand_path(__dir__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|translation)/}) }
19
+ end
20
+
21
+ s.metadata = {
22
+ "source_code_uri" => s.homepage
23
+ }
24
+
25
+ s.add_dependency "rails", ">= 5.0"
26
+ end
@@ -1,40 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arlequin
4
+ ##
5
+ # Middleware to detect and warn about N+1 queries in a Rails application.
4
6
  class Middleware
7
+ ##
8
+ # Initializes the middleware with the given app.
9
+ #
10
+ # @param app [Object] The Rack application this middleware wraps.
5
11
  def initialize(app)
6
12
  @app = app
7
13
  end
8
14
 
15
+ ##
16
+ # Processes the incoming request, detects N+1 queries, and injects a warning
17
+ # into the HTML response if necessary.
18
+ #
19
+ # @param env [Hash] The Rack environment.
20
+ # @return [Array] The status, headers, and response of the Rack application.
9
21
  def call(env)
10
- # Set up a query counter
11
22
  @queries = Hash.new { |hash, key| hash[key] = 0 }
12
-
13
23
  ActiveSupport::Notifications.subscribe("sql.active_record", &method(:on_sql))
14
24
 
15
25
  status, headers, response = @app.call(env)
16
26
 
17
- # Detect and log N+1 queries
18
- log_n_plus_one_warnings
27
+ if n_plus_one_detected?
28
+ html_fragment = generate_html_warning
29
+ response = inject_html_fragment(response, html_fragment)
30
+ end
31
+
32
+ ActiveSupport::Notifications.unsubscribe("sql.active_record")
19
33
 
20
34
  [ status, headers, response ]
21
35
  end
22
36
 
23
37
  private
24
38
 
39
+ ##
40
+ # Callback for ActiveSupport notifications on SQL queries.
41
+ #
42
+ # @param _name [String] The event name.
43
+ # @param _start [Time] The start time of the event.
44
+ # @param _finish [Time] The finish time of the event.
45
+ # @param _id [String] The event ID.
46
+ # @param payload [Hash] The event payload containing SQL information.
25
47
  def on_sql(_name, _start, _finish, _id, payload)
26
- return if payload[:name] == "SCHEMA" # Skip schema queries
48
+ return if payload[:name] == "SCHEMA"
27
49
 
28
50
  query = payload[:sql]
29
51
  @queries[query] += 1
30
52
  end
31
53
 
32
- def log_n_plus_one_warnings
54
+ ##
55
+ # Generates warnings for detected N+1 queries.
56
+ #
57
+ # @return [Array<String>] The list of N+1 query warnings.
58
+ def n_plus_one_warnings
59
+ warnings = []
60
+
33
61
  @queries.each do |query, count|
34
62
  if count > 1
35
- Rails.logger.warn("N+1 query detected: #{query}, executed #{count} times")
63
+ warnings << "N+1 query detected: #{query}, executed #{count} times"
36
64
  end
37
65
  end
66
+
67
+ warnings
68
+ end
69
+
70
+ ##
71
+ # Checks if any N+1 queries have been detected.
72
+ #
73
+ # @return [Boolean] True if N+1 queries are detected, otherwise false.
74
+ def n_plus_one_detected?
75
+ @queries.values.any? { |count| count > 1 }
76
+ end
77
+
78
+ ##
79
+ # Generates an HTML warning for detected N+1 queries.
80
+ #
81
+ # @return [String] The HTML warning to be injected into the response.
82
+ def generate_html_warning
83
+ "<div style='z-index: 9999; position: fixed; bottom: 0; left: 0; width: 100%; background-color: blue; color: white; text-align: center; padding: 5px;'>" +
84
+ "N+1 query detected! Check your queries. " +
85
+ n_plus_one_warnings.join("<br>") +
86
+ "</div>"
87
+ end
88
+
89
+ ##
90
+ # Injects an HTML fragment into the response body.
91
+ #
92
+ # @param response [Array<String>] The original response body.
93
+ # @param fragment [String] The HTML fragment to inject.
94
+ # @return [Array<String>] The modified response body.
95
+ def inject_html_fragment(response, fragment)
96
+ body = + ""
97
+
98
+ response.each do |part|
99
+ body << part
100
+ end
101
+
102
+ body.sub!("</body>", "#{fragment}</body>")
103
+
104
+ [ body ]
38
105
  end
39
106
  end
40
107
  end
data/lib/arlequin.rb CHANGED
@@ -1,7 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Arlequin
3
+ require "active_support/all"
4
+ require "arlequin/middleware"
5
+
6
+ module Arlequin
7
+ # The `Railtie` class integrates the Arlequin middleware into Rails applications.
8
+ #
9
+ # This Railtie automatically adds the Arlequin middleware to the Rails middleware stack,
10
+ # but it is intended to be used only in the development environment. It helps in monitoring
11
+ # and providing warnings about N+1 SQL queries during development.
12
+ #
13
+ # ## Configuration
14
+ #
15
+ # To use Arlequin in your Rails application, you should include it in the development group
16
+ # of your Gemfile and require it in the application configuration.
17
+ #
18
+ # Example Gemfile configuration:
19
+ #
20
+ # group :development do
21
+ # gem 'arlequin'
22
+ # end
23
+ #
24
+ # To ensure the middleware is used in development, require the Railtie in your application:
25
+ #
26
+ # # In `config/application.rb`
27
+ # require "arlequin/railtie"
28
+ #
29
+ # module YourApp
30
+ # class Application < Rails::Application
31
+ # # other configurations
32
+ # # Ensure that the Railtie is required
33
+ # end
34
+ # end
35
+ #
36
+ # This setup will automatically add the Arlequin middleware to the middleware stack
37
+ # when running in development mode. The middleware will then be used to detect and
38
+ # provide warnings for N+1 SQL queries.
39
+ #
40
+ # ## Usage
41
+ #
42
+ # Once configured, the Arlequin middleware will monitor SQL queries and inject a warning
43
+ # into the response HTML if N+1 queries are detected.
44
+ #
45
+ # The Railtie is part of the Arlequin gem and is designed to integrate seamlessly into
46
+ # Rails applications for development purposes, facilitating easier identification and
47
+ # resolution of N+1 query issues.
4
48
  class Railtie < Rails::Railtie
49
+ # Initializes the Railtie and configures the Rails application to use the Arlequin middleware.
50
+ #
51
+ # This method is called during the initialization process of the Rails application.
52
+ # It adds the Arlequin middleware to the middleware stack in development environments,
53
+ # allowing it to intercept requests and monitor SQL queries.
5
54
  initializer "arlequin.configure_rails_initialization" do |app|
6
55
  app.middleware.use Arlequin::Middleware
7
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arlequin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominic Goulet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-10 00:00:00.000000000 Z
11
+ date: 2024-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -25,17 +25,25 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5.0'
27
27
  description: Performance Logger Gem
28
- email: dominic.goulet@gmail.com
28
+ email: dominic@dominicgoulet.com
29
29
  executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - ".gitignore"
34
+ - ".rubocop.yml"
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - README.md
38
+ - Rakefile
39
+ - arlequin.gemspec
33
40
  - lib/arlequin.rb
34
41
  - lib/arlequin/middleware.rb
35
- homepage: https://rubygems.org/gems/arlequin
42
+ homepage: https://github.com/dominicgoulet/arlequin
36
43
  licenses:
37
44
  - MIT
38
- metadata: {}
45
+ metadata:
46
+ source_code_uri: https://github.com/dominicgoulet/arlequin
39
47
  post_install_message:
40
48
  rdoc_options: []
41
49
  require_paths: