rails_mini_profiler 0.3.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +20 -0
- data/README.md +65 -36
- data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
- data/app/controllers/rails_mini_profiler/application_controller.rb +15 -7
- data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +28 -13
- data/app/helpers/rails_mini_profiler/application_helper.rb +3 -1
- data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
- data/app/javascript/images/check.svg +3 -0
- data/app/javascript/images/chevron.svg +3 -0
- data/app/javascript/images/copy.svg +6 -0
- data/app/javascript/images/filter.svg +1 -0
- data/app/javascript/images/logo_variant.svg +2 -2
- data/app/javascript/images/search.svg +4 -5
- data/app/javascript/js/checklist_controller.js +48 -0
- data/app/javascript/js/clipboard_controller.js +53 -0
- data/app/javascript/js/enable_controller.js +23 -0
- data/app/javascript/js/filter_controller.js +48 -0
- data/app/javascript/js/search_controller.js +18 -0
- data/app/javascript/js/select_controller.js +52 -0
- data/app/javascript/packs/rails-mini-profiler.js +69 -53
- data/app/javascript/stylesheets/components/buttons.scss +59 -0
- data/app/javascript/stylesheets/components/dropdown.scss +103 -0
- data/app/javascript/stylesheets/components/input.scss +10 -0
- data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +7 -13
- data/app/javascript/stylesheets/components/page_header.scss +7 -0
- data/app/javascript/stylesheets/components/pagination.scss +55 -0
- data/app/javascript/stylesheets/components/placeholder.scss +36 -0
- data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
- data/app/javascript/stylesheets/components/trace.scss +93 -0
- data/app/javascript/stylesheets/flamegraph.scss +3 -2
- data/app/javascript/stylesheets/flashes.scss +3 -5
- data/app/javascript/stylesheets/profiled_requests.scss +12 -161
- data/app/javascript/stylesheets/rails-mini-profiler.scss +82 -66
- data/app/javascript/stylesheets/traces.scss +52 -56
- data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +10 -17
- data/app/presenters/rails_mini_profiler/trace_presenter.rb +9 -7
- data/app/search/rails_mini_profiler/base_search.rb +67 -0
- data/app/search/rails_mini_profiler/profiled_request_search.rb +33 -0
- data/app/search/rails_mini_profiler/trace_search.rb +27 -0
- data/app/views/models/_flamegraph.json.jb +3 -0
- data/app/views/models/_profiled_request.jb +3 -0
- data/app/views/models/_trace.jb +3 -0
- data/app/views/rails_mini_profiler/badge.html.erb +2 -2
- data/app/views/rails_mini_profiler/flamegraphs/show.json.jb +3 -0
- data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
- data/app/views/rails_mini_profiler/profiled_requests/index.json.jb +3 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +19 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
- data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +23 -6
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_header.erb +87 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +5 -18
- data/app/views/rails_mini_profiler/profiled_requests/show.json.jb +5 -0
- data/app/views/rails_mini_profiler/shared/_head.erb +1 -1
- data/lib/generators/rails_mini_profiler/install_generator.rb +7 -1
- data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +11 -4
- data/lib/rails_mini_profiler/badge.rb +4 -2
- data/lib/rails_mini_profiler/{storage.rb → configuration/storage.rb} +0 -0
- data/lib/rails_mini_profiler/configuration/user_interface.rb +70 -0
- data/lib/rails_mini_profiler/configuration.rb +7 -7
- data/lib/rails_mini_profiler/engine.rb +12 -8
- data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
- data/lib/rails_mini_profiler/guard.rb +19 -1
- data/lib/rails_mini_profiler/middleware.rb +6 -4
- data/lib/rails_mini_profiler/redirect.rb +1 -1
- data/lib/rails_mini_profiler/request_context.rb +6 -1
- data/lib/rails_mini_profiler/request_wrapper.rb +44 -8
- data/lib/rails_mini_profiler/tracing/controller_tracer.rb +15 -0
- data/lib/rails_mini_profiler/tracing/null_trace.rb +7 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracer.rb +37 -0
- data/lib/rails_mini_profiler/tracing/sequel_tracker.rb +37 -0
- data/lib/rails_mini_profiler/tracing/subscriptions.rb +34 -0
- data/lib/rails_mini_profiler/{models → tracing}/trace.rb +10 -2
- data/lib/rails_mini_profiler/tracing/trace_factory.rb +37 -0
- data/lib/rails_mini_profiler/tracing/tracer.rb +31 -0
- data/lib/rails_mini_profiler/tracing/view_tracer.rb +12 -0
- data/lib/rails_mini_profiler/tracing.rb +11 -0
- data/lib/rails_mini_profiler/user.rb +1 -1
- data/lib/rails_mini_profiler/version.rb +1 -4
- data/lib/rails_mini_profiler.rb +5 -7
- data/vendor/assets/images/check.svg +3 -0
- data/vendor/assets/images/chevron.svg +3 -0
- data/vendor/assets/images/copy.svg +6 -0
- data/vendor/assets/images/filter.svg +1 -0
- data/vendor/assets/images/logo_variant.svg +2 -2
- data/vendor/assets/images/search.svg +4 -5
- data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
- metadata +95 -11
- data/Rakefile +0 -18
- data/lib/rails_mini_profiler/tracers.rb +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb1212a4d90c46143193b2173e9e136aa6757a74a6591137cba33169235d175d
|
4
|
+
data.tar.gz: f799849a5d978c2c22d39b3fa1f4be6c02541f75b7a2312d98a510a1d69a195b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae143e874778e4af9560e5b6521003c5d4933ec86ffe5900a56c57b93791a3d0faa3ac18294cecccac12048dc35c38a70aa7a53ffa91b12d2025a89c0acc0384
|
7
|
+
data.tar.gz: f2a8ca075571593f1cce9d1b59910ea5aac8f95a06c277298be9e0e93be5dcd1b013fbaefbef22bf173f6e2db40fed29068cfda44d422aa372cdde30818ec9cf
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 hschne
|
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
CHANGED
@@ -63,8 +63,6 @@ route:
|
|
63
63
|
```ruby
|
64
64
|
# routes.rb
|
65
65
|
Rails.application.routes.draw do
|
66
|
-
...
|
67
|
-
|
68
66
|
mount RailsMiniProfiler::Engine => '/rails_mini_profiler'
|
69
67
|
end
|
70
68
|
```
|
@@ -76,18 +74,18 @@ top right that is injected into your pages.
|
|
76
74
|
|
77
75
|
![overview](docs/images/overview.png)
|
78
76
|
|
79
|
-
Requests to your application will be profiled automatically. You can view all stored requests by navigating to `yourapp/rails_mini_profiler/profiled_requests`.
|
77
|
+
Requests to your application will be profiled automatically. You can view and search all stored requests by navigating to `yourapp/rails_mini_profiler/profiled_requests`.
|
80
78
|
|
81
79
|
### Request Details
|
82
80
|
|
83
81
|
<p align="center">
|
84
82
|
<img alt="Light" src="docs/images/trace.png" width="45%">
|
85
83
|
|
86
|
-
<img alt="Dark" src="docs/images/
|
84
|
+
<img alt="Dark" src="docs/images/trace-details.png" width="45%">
|
87
85
|
</p>
|
88
86
|
|
89
87
|
This view shows you how your requests spend their time. How much of it is spent in the DB, how much in rendering views?
|
90
|
-
|
88
|
+
May can filter and clicking on individual traces to gain deeper insights and see detailed information.
|
91
89
|
|
92
90
|
### Flamegraphs
|
93
91
|
|
@@ -106,31 +104,23 @@ Flamegraphs are rendered using [Speedscope](https://github.com/jlfwong/speedscop
|
|
106
104
|
|
107
105
|
Rails Mini Profiler provides a wide array of configuration options. You can find details below. For an example configuration check `initializers/rails_mini_profiler.rb` (or [the template file](https://github.com/hschne/rails-mini-profiler/blob/main/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb)).
|
108
106
|
|
109
|
-
|
|
110
|
-
|
107
|
+
| Name | Default | Description |
|
108
|
+
|--------------------------|------------------------------|-------------------------------------------------------------------------------------------------|
|
111
109
|
| `enabled` | `true` (dev)/ `false` (prod) | Whether or not RMP is enabled |
|
112
|
-
| `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
|
113
|
-
| `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
|
114
110
|
| `flamegraph_enabled` | `true` | Should flamegraphs be recorded automatically? |
|
115
111
|
| `flamegraph_sample_rate` | `0.5` | The flamegraph sample rate. How many snapshots per millisecond are created. |
|
116
112
|
| `skip_paths` | `[]` | An array of request paths that should not be profiled. Regex allowed. |
|
117
|
-
| `storage` | `Storage`
|
118
|
-
| `
|
119
|
-
|
120
|
-
### Request Configuration
|
121
|
-
|
122
|
-
You may override the configuration by sending request parameters. The following parameters are available:
|
113
|
+
| `storage` | `Storage.new` | Storage configuration. See [Storage](#Storage). |
|
114
|
+
| `ui` | `UserInterface.new` | UI configuration. See [UI](#UI). |
|
115
|
+
| `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Authorization](#Authorization) |
|
123
116
|
|
124
|
-
| Option | Description |
|
125
|
-
| ---------------- | ------------------------------------------------------------------------------------------- |
|
126
|
-
| `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediatly. |
|
127
117
|
|
128
118
|
### Storage
|
129
119
|
|
130
120
|
Rails Mini Profiler stores profiling information in your database per default. You can configure various details of how
|
131
121
|
traces and requests are stored.
|
132
122
|
|
133
|
-
|
|
123
|
+
| Name | Default | Description |
|
134
124
|
| ------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- |
|
135
125
|
| `database` | `nil` | Set a custom database to be used for storing profiler information. Uses `connect_to` for profiler records |
|
136
126
|
| `profiled_requests_table` | `rmp_profiled_requests` | The table to be used to store profiled requests. |
|
@@ -141,7 +131,7 @@ Rails Mini Profiler does not offer an automatic way to clean up old profiling in
|
|
141
131
|
|
142
132
|
```ruby
|
143
133
|
# Clockwork
|
144
|
-
every(1.month, 'purge rails mini profiler' do
|
134
|
+
every(1.month, 'purge rails mini profiler') do
|
145
135
|
ProfiledRequestCleanupJob.perform_later
|
146
136
|
end
|
147
137
|
|
@@ -155,17 +145,37 @@ class ProfiledRequestCleanupJob < ApplicationJob
|
|
155
145
|
end
|
156
146
|
```
|
157
147
|
|
158
|
-
###
|
148
|
+
### UI
|
149
|
+
|
150
|
+
Rails Mini Profiler allows you to configure various UI features.
|
151
|
+
|
152
|
+
| Name | Default | Description |
|
153
|
+
|---------------------|---------------------------|-------------------------------------------------------------------------------------------------|
|
154
|
+
| `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
|
155
|
+
| `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
|
156
|
+
| `base_controller` | `ApplicationController` | Which controller UI controllers should inherit from. |
|
157
|
+
| `page_size` | `25` | The page size for lists shown in the UI. |
|
158
|
+
| `webpacker_enabled` | `true` | Use Webpacker if available? Disable to fall back to the asset pipeline. |
|
159
|
+
|
160
|
+
### Request Configuration
|
161
|
+
|
162
|
+
You may override static configuration on a per-request by attaching request parameters. For example, `https://myapp.com/api/mydata?rmp_flamegraph=false`
|
163
|
+
|
164
|
+
| Name | Description |
|
165
|
+
| ---------------- | ------------------------------------------------------------------------------------------- |
|
166
|
+
| `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediately. |
|
159
167
|
|
160
|
-
|
168
|
+
### Authorization
|
161
169
|
|
162
|
-
|
170
|
+
Profiling information is segregated by user ID. That means users cannot see each other's profiled requests. Per default, individual users are identified by their IP adress.
|
171
|
+
|
172
|
+
You may change this by setting a custom user provider:
|
163
173
|
|
164
174
|
```ruby
|
165
175
|
config.user_provider = proc { |env| Rack::Request.new(env).ip }
|
166
176
|
```
|
167
177
|
|
168
|
-
You may also explicitly set the user
|
178
|
+
You may also explicitly set the user by modifying your application controller (or the controller you have configured as UI base controller):
|
169
179
|
|
170
180
|
```ruby
|
171
181
|
class ApplicationController < ActionController::Base
|
@@ -175,15 +185,18 @@ class ApplicationController < ActionController::Base
|
|
175
185
|
end
|
176
186
|
```
|
177
187
|
|
178
|
-
|
188
|
+
`ApplicationController` is used as the default base class for UI controllers. To change it, you may use `configuration.ui.base_controller`.
|
179
189
|
|
180
190
|
### Profiling in Production
|
181
191
|
|
182
192
|
Rails Mini Profiler is not intended for performance reporting. There are other tools for that ( [Skylight](https://www.skylight.io/),
|
183
|
-
[New Relic](https://newrelic.com/), [DataDog](https://www.datadoghq.com/)...).
|
193
|
+
[New Relic](https://newrelic.com/), [DataDog](https://www.datadoghq.com/)...). But you can still use RMP in production to profile specific requests.
|
194
|
+
|
195
|
+
Per default, *no requests will be profiled* in production, and the Rails Mini Profiler UI will be inaccessible.
|
184
196
|
|
185
|
-
|
186
|
-
|
197
|
+
#### Enabling Profiling
|
198
|
+
|
199
|
+
Since profiling impacts performance, it is recommended that you limit which requests are being profiled:
|
187
200
|
|
188
201
|
```ruby
|
189
202
|
RailsMiniProfiler.configure do |config|
|
@@ -191,7 +204,17 @@ RailsMiniProfiler.configure do |config|
|
|
191
204
|
end
|
192
205
|
```
|
193
206
|
|
194
|
-
|
207
|
+
#### Authorizing Users
|
208
|
+
|
209
|
+
You must explicitly authorize profiling for users, as well as authenticate them to the UI:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
class ApplicationController < ActionController::Base
|
213
|
+
before_action do
|
214
|
+
RailsMiniProfiler::User.authorize(current_user.id) # Requests by this user will now be profiled, and they get access to the UI
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
195
218
|
|
196
219
|
## Why Rails Mini Profiler?
|
197
220
|
|
@@ -201,13 +224,13 @@ Improving the performance of any application is a 3-step process. You have to an
|
|
201
224
|
2. Why is it slow?
|
202
225
|
3. Did my solution fix the slowness?
|
203
226
|
|
204
|
-
I'm a huge fan of [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), and
|
227
|
+
I'm a huge fan of [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), and APM tools such as [Skylight](https://www.skylight.io/) or [Scout APM](https://scoutapm.com). Each of these tools has its own areas where it succeeds.
|
205
228
|
|
206
|
-
APM tools are excellent for profiling your app in production - aka. they show you what is slow - and offer _some_ hints as to what causes the slowdown. `rack-mini-profiler` can do some sampling in production, but excels at providing detailed insight into _why_ something is slow
|
229
|
+
APM tools are excellent for profiling your app in production - aka. they show you what is slow - and offer _some_ hints as to what causes the slowdown. `rack-mini-profiler` can do some sampling in production, but excels at providing detailed insight into _why_ something is slow by providing Flamegraphs and detailed query information.
|
207
230
|
|
208
231
|
Rails Mini Profiler improves upon `rack-mini-profiler` in the latter regard. It is a developer tool, rather than a monitoring tool, and sets a big focus on developer experience. Simply put, it aims to be the best tool available to help you figure out _why_ specific requests are slow.
|
209
232
|
|
210
|
-
As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e.g. Sinatra) or production sampling, but provides a much better user experience and better
|
233
|
+
As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e.g. Sinatra) or production sampling, but provides a much better user experience and better support for API-only applications.
|
211
234
|
|
212
235
|
## Troubleshooting
|
213
236
|
|
@@ -215,7 +238,13 @@ As such, compared to `rack-mini-profiler`, it does not support non-Rails apps (e
|
|
215
238
|
|
216
239
|
Rails Mini Profiler is in early development. As such, breaking changes may still be introduced on a regular basis. While Rails Mini Profiler is in pre-release we do not offer upgrade migrations.
|
217
240
|
|
218
|
-
If an upgrade to Rails Mini Profiler breaks your application, we recommend that you
|
241
|
+
If an upgrade to Rails Mini Profiler breaks your application, we recommend that you clean house and start over. Re-run the initializer and overwrite existing files:
|
242
|
+
|
243
|
+
```bash
|
244
|
+
rails rails_mini_profiler:install
|
245
|
+
```
|
246
|
+
|
247
|
+
If only the DB schema is out of date, drop the offending tables and re-run migrations for the latest version:
|
219
248
|
|
220
249
|
```
|
221
250
|
rails rails_mini_profiler:install:migrations
|
@@ -226,13 +255,13 @@ rails db:migrate
|
|
226
255
|
|
227
256
|
Rails Mini Profiler supports API-only apps, but you have to make some small adjustments to use it. At the top of `application.rb` add [Sprockets](https://github.com/rails/sprockets-rails):
|
228
257
|
|
229
|
-
```
|
258
|
+
```ruby
|
230
259
|
require "sprockets/railtie"
|
231
260
|
```
|
232
261
|
|
233
262
|
Then, modify `application.rb`:
|
234
263
|
|
235
|
-
```
|
264
|
+
```ruby
|
236
265
|
module ApiOnly
|
237
266
|
class Application < Rails::Application
|
238
267
|
|
@@ -242,7 +271,7 @@ module ApiOnly
|
|
242
271
|
end
|
243
272
|
```
|
244
273
|
|
245
|
-
**Note
|
274
|
+
**Note**: Sprockets and flash are currently required for some of Rails Mini Profiler's UI features. These modifications may no longer be needed in the future.
|
246
275
|
|
247
276
|
### No Flamegraphs are being recored?
|
248
277
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
class DatabaseAdapter
|
5
|
+
class << self
|
6
|
+
def cast_to_text(column)
|
7
|
+
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
8
|
+
# Cast json field to text to have access to the LIKE operator
|
9
|
+
"#{column}::text"
|
10
|
+
else
|
11
|
+
column
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RailsMiniProfiler
|
4
|
-
class ApplicationController <
|
5
|
-
rescue_from ActiveRecord::RecordNotFound, with:
|
4
|
+
class ApplicationController < RailsMiniProfiler.configuration.ui.base_controller
|
5
|
+
rescue_from ActiveRecord::RecordNotFound, with: ->(error) { handle(error, 404) }
|
6
6
|
|
7
|
-
before_action :
|
7
|
+
before_action :check_rmp_user
|
8
8
|
|
9
9
|
protected
|
10
10
|
|
@@ -17,12 +17,20 @@ module RailsMiniProfiler
|
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def handle(error, status = 500)
|
21
|
+
respond_to do |format|
|
22
|
+
format.html { redirect_back(fallback_location: profiled_requests_path, alert: error.to_s) }
|
23
|
+
format.json { render status: status, json: { message: error.to_s } }
|
24
|
+
end
|
22
25
|
end
|
23
26
|
|
24
|
-
def
|
25
|
-
|
27
|
+
def check_rmp_user
|
28
|
+
user = User.get(request.env).present?
|
29
|
+
redirect_back(fallback_location: fallback_location) unless user
|
30
|
+
end
|
31
|
+
|
32
|
+
def fallback_location
|
33
|
+
defined?(main_app.root_path) ? main_app.root_path : '/'
|
26
34
|
end
|
27
35
|
end
|
28
36
|
end
|
@@ -4,22 +4,28 @@ require_dependency 'rails_mini_profiler/application_controller'
|
|
4
4
|
|
5
5
|
module RailsMiniProfiler
|
6
6
|
class ProfiledRequestsController < ApplicationController
|
7
|
+
include Pagy::Backend
|
8
|
+
|
7
9
|
before_action :set_profiled_request, only: %i[show destroy]
|
8
10
|
|
9
11
|
def index
|
10
|
-
@profiled_requests = ProfiledRequest
|
11
|
-
|
12
|
-
|
13
|
-
@profiled_requests = @profiled_requests.where('request_path LIKE ?', "%#{params[:path]}%") if params[:path]
|
12
|
+
@profiled_requests = ProfiledRequest.where(user_id: user_id).order(id: :desc)
|
13
|
+
search = ProfiledRequestSearch.new(index_params, scope: @profiled_requests)
|
14
|
+
@pagy, @profiled_requests = pagy(search.results, items: configuration.ui.page_size)
|
14
15
|
@profiled_requests = @profiled_requests.map { |request| present(request) }
|
15
16
|
end
|
16
17
|
|
17
18
|
def show
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
search = TraceSearch.new(show_params, scope: @profiled_request.traces)
|
20
|
+
context = {
|
21
|
+
start: @profiled_request.start,
|
22
|
+
finish: @profiled_request.finish,
|
23
|
+
total_duration: @profiled_request.duration,
|
24
|
+
total_allocations: @profiled_request.allocations
|
25
|
+
}
|
26
|
+
@traces = search.results
|
21
27
|
.order(:start)
|
22
|
-
.map { |trace| present(trace,
|
28
|
+
.map { |trace| present(trace, context: context) }
|
23
29
|
@profiled_request = present(@profiled_request)
|
24
30
|
end
|
25
31
|
|
@@ -30,16 +36,25 @@ module RailsMiniProfiler
|
|
30
36
|
|
31
37
|
def destroy_all
|
32
38
|
ProfiledRequest.transaction do
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
profiled_requests = ProfiledRequest.where(user_id: user_id)
|
40
|
+
profiled_requests = ProfiledRequestSearch.new(index_params, scope: profiled_requests).results
|
41
|
+
Flamegraph.joins(:profiled_request).merge(profiled_requests).delete_all
|
42
|
+
Trace.joins(:profiled_request).merge(profiled_requests).delete_all
|
43
|
+
profiled_requests.delete_all
|
37
44
|
end
|
38
|
-
redirect_to profiled_requests_url, notice: 'Profiled
|
45
|
+
redirect_to profiled_requests_url, notice: 'Profiled requests cleared', status: :see_other
|
39
46
|
end
|
40
47
|
|
41
48
|
private
|
42
49
|
|
50
|
+
def show_params
|
51
|
+
params.permit(:id, :payload, :duration, :allocations, name: [])
|
52
|
+
end
|
53
|
+
|
54
|
+
def index_params
|
55
|
+
params.permit(:path, :duration, id: [], method: [], media_type: [], status: [])
|
56
|
+
end
|
57
|
+
|
43
58
|
def user_id
|
44
59
|
@user_id ||= User.get(request.env)
|
45
60
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module RailsMiniProfiler
|
4
4
|
module ApplicationHelper
|
5
|
+
include Pagy::Frontend
|
6
|
+
|
5
7
|
def present(model, presenter_class = nil, **kwargs)
|
6
8
|
klass = presenter_class || "#{model.class}Presenter".constantize
|
7
9
|
presenter = klass.new(model, self, **kwargs)
|
@@ -10,7 +12,7 @@ module RailsMiniProfiler
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def inline_svg(path, options = {})
|
13
|
-
if defined?(Webpacker::Engine)
|
15
|
+
if defined?(Webpacker::Engine) && RailsMiniProfiler.configuration.ui.webpacker_enabled
|
14
16
|
path = "media/images/#{path}"
|
15
17
|
inline_svg_pack_tag(path, options)
|
16
18
|
else
|
@@ -12,5 +12,15 @@ module RailsMiniProfiler
|
|
12
12
|
def formatted_allocations(allocations)
|
13
13
|
number_to_human(allocations, units: { unit: '', thousand: 'k', million: 'M', billion: 'B', trillion: 'T' })
|
14
14
|
end
|
15
|
+
|
16
|
+
def trace_display_name(name)
|
17
|
+
{
|
18
|
+
'sql.active_record': 'ActiveRecord Query',
|
19
|
+
'instantiation.active_record': 'ActiveRecord Instantiation',
|
20
|
+
'render_template.action_view': 'Render View',
|
21
|
+
'render_partial.action_view': 'Render Partial',
|
22
|
+
'process_action.action_controller': 'Controller'
|
23
|
+
}[name.to_sym]
|
24
|
+
end
|
15
25
|
end
|
16
26
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
2
|
+
viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3" xml:space="preserve">
|
3
|
+
<path fill="currentColor"
|
4
|
+
d="M314.25 85.4h-227c-21.3 0-38.6 17.3-38.6 38.6v325.7c0 21.3 17.3 38.6 38.6 38.6h227c21.3 0 38.6-17.3 38.6-38.6V124c-.1-21.3-17.4-38.6-38.6-38.6zm11.5 364.2c0 6.4-5.2 11.6-11.6 11.6h-227c-6.4 0-11.6-5.2-11.6-11.6V124c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.6z"/>
|
5
|
+
<path fill="currentColor"
|
6
|
+
d="M401.05 0h-227c-21.3 0-38.6 17.3-38.6 38.6 0 7.5 6 13.5 13.5 13.5s13.5-6 13.5-13.5c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.7c0 6.4-5.2 11.6-11.6 11.6-7.5 0-13.5 6-13.5 13.5s6 13.5 13.5 13.5c21.3 0 38.6-17.3 38.6-38.6V38.6c0-21.3-17.3-38.6-38.6-38.6z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
-
<style
|
1
|
+
<svg id="logo-variant" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
+
<style>#logo-variant path{fill:none;stroke-width:64;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4}</style>
|
3
3
|
<defs>
|
4
4
|
<linearGradient id="lightShadow">
|
5
5
|
<stop offset="0" style="stop-color:#c22121;stop-opacity:1"/>
|
@@ -1,10 +1,9 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
3
|
-
<
|
4
|
-
|
5
|
-
<g id="Search" transform="translate(2.000000, 2.000000)" fill="#000000" fill-rule="nonzero">
|
3
|
+
<g id="Iconly/Bulk/Search" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
|
4
|
+
<g id="Search" transform="translate(2.000000, 2.000000)" fill="currentColor" fill-rule="nonzero">
|
6
5
|
<ellipse id="Ellipse_746" cx="8.59921927" cy="8.65324385" rx="8.59921927" ry="8.65324385"></ellipse>
|
7
|
-
<path d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
6
|
+
<path fill="currentColor" d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
8
7
|
</g>
|
9
8
|
</g>
|
10
|
-
</svg>
|
9
|
+
</svg>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["count"];
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.setCount();
|
8
|
+
}
|
9
|
+
|
10
|
+
checkAll() {
|
11
|
+
this.setAllCheckboxes(true);
|
12
|
+
this.setCount();
|
13
|
+
}
|
14
|
+
|
15
|
+
checkNone() {
|
16
|
+
this.setAllCheckboxes(false);
|
17
|
+
this.setCount();
|
18
|
+
}
|
19
|
+
|
20
|
+
onChecked() {
|
21
|
+
this.setCount();
|
22
|
+
}
|
23
|
+
|
24
|
+
setAllCheckboxes(checked) {
|
25
|
+
this.checkboxes.forEach((el) => {
|
26
|
+
const checkbox = el;
|
27
|
+
|
28
|
+
if (!checkbox.disabled) {
|
29
|
+
checkbox.checked = checked;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
setCount() {
|
35
|
+
if (this.hasCountTarget) {
|
36
|
+
const count = this.selectedCheckboxes.length;
|
37
|
+
this.countTarget.innerHTML = `${count} selected`;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
get selectedCheckboxes() {
|
42
|
+
return this.checkboxes.filter((c) => c.checked);
|
43
|
+
}
|
44
|
+
|
45
|
+
get checkboxes() {
|
46
|
+
return new Array(...this.element.querySelectorAll("input[type=checkbox]"));
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["button", "source"];
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
if (!this.hasButtonTarget) return;
|
8
|
+
|
9
|
+
this.originalText = this.buttonTarget.innerText;
|
10
|
+
this.successDuration = 2000;
|
11
|
+
}
|
12
|
+
|
13
|
+
copy(event) {
|
14
|
+
event.preventDefault();
|
15
|
+
|
16
|
+
let text = this.sourceTarget.innerText;
|
17
|
+
const filter = this.data.get("filter");
|
18
|
+
if (filter) {
|
19
|
+
text = new RegExp(filter).exec(text)[0];
|
20
|
+
}
|
21
|
+
const temporaryInput = document.createElement("textarea");
|
22
|
+
temporaryInput.value = text;
|
23
|
+
document.body.appendChild(temporaryInput);
|
24
|
+
temporaryInput.select();
|
25
|
+
document.execCommand("copy");
|
26
|
+
document.body.removeChild(temporaryInput);
|
27
|
+
|
28
|
+
this.copied();
|
29
|
+
}
|
30
|
+
|
31
|
+
copied() {
|
32
|
+
if (!this.hasButtonTarget) return;
|
33
|
+
|
34
|
+
if (this.timeout) {
|
35
|
+
clearTimeout(this.timeout);
|
36
|
+
}
|
37
|
+
|
38
|
+
const copiedClass = this.data.get("copiedClass");
|
39
|
+
if (copiedClass) {
|
40
|
+
this.buttonTarget.classList.add(copiedClass);
|
41
|
+
}
|
42
|
+
const copiedMessage = this.data.get("copiedMessage");
|
43
|
+
const content = this.buttonTarget.innerHTML;
|
44
|
+
if (copiedMessage) {
|
45
|
+
this.buttonTarget.innerHTML = copiedMessage;
|
46
|
+
}
|
47
|
+
|
48
|
+
this.timeout = setTimeout(() => {
|
49
|
+
this.buttonTarget.classList.remove(copiedClass);
|
50
|
+
this.buttonTarget.innerHTML = content;
|
51
|
+
}, this.successDuration);
|
52
|
+
}
|
53
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["enable"];
|
5
|
+
|
6
|
+
enable() {
|
7
|
+
this.enableTarget.disabled = false;
|
8
|
+
}
|
9
|
+
|
10
|
+
disable() {
|
11
|
+
this.enableTarget.disabled = true;
|
12
|
+
}
|
13
|
+
|
14
|
+
change(event) {
|
15
|
+
if (event.type.match(/rmp:select:.*/)) {
|
16
|
+
if (event.detail.count > 0) {
|
17
|
+
this.enable();
|
18
|
+
} else {
|
19
|
+
this.disable();
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["filter"];
|
5
|
+
|
6
|
+
apply() {
|
7
|
+
location.href = `${window.location.pathname}?${this.params}`;
|
8
|
+
}
|
9
|
+
|
10
|
+
reset() {
|
11
|
+
location.href = `${window.location.pathname}`;
|
12
|
+
}
|
13
|
+
|
14
|
+
post() {
|
15
|
+
const token = document.head.querySelector(
|
16
|
+
'meta[name="csrf-token"]'
|
17
|
+
).content;
|
18
|
+
const path = `${window.location.pathname}/destroy_all?${this.params}`;
|
19
|
+
fetch(path, {
|
20
|
+
method: "DELETE",
|
21
|
+
redirect: "follow",
|
22
|
+
headers: {
|
23
|
+
"Content-Type": "application/json",
|
24
|
+
credentials: "same-origin",
|
25
|
+
},
|
26
|
+
body: JSON.stringify({ authenticity_token: token }),
|
27
|
+
}).then((response) => {
|
28
|
+
if (response.redirected) {
|
29
|
+
window.location.href = response.url;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
get params() {
|
35
|
+
return this.activeFilterTargets()
|
36
|
+
.map((t) => `${t.name}=${t.value}`)
|
37
|
+
.join("&");
|
38
|
+
}
|
39
|
+
|
40
|
+
activeFilterTargets() {
|
41
|
+
return this.filterTargets.filter(function (target) {
|
42
|
+
if (target.type === "checkbox" || target.type === "radio")
|
43
|
+
return target.checked;
|
44
|
+
|
45
|
+
return target.value.length > 0;
|
46
|
+
});
|
47
|
+
}
|
48
|
+
}
|