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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +20 -0
  3. data/README.md +65 -36
  4. data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
  5. data/app/controllers/rails_mini_profiler/application_controller.rb +15 -7
  6. data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +28 -13
  7. data/app/helpers/rails_mini_profiler/application_helper.rb +3 -1
  8. data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
  9. data/app/javascript/images/check.svg +3 -0
  10. data/app/javascript/images/chevron.svg +3 -0
  11. data/app/javascript/images/copy.svg +6 -0
  12. data/app/javascript/images/filter.svg +1 -0
  13. data/app/javascript/images/logo_variant.svg +2 -2
  14. data/app/javascript/images/search.svg +4 -5
  15. data/app/javascript/js/checklist_controller.js +48 -0
  16. data/app/javascript/js/clipboard_controller.js +53 -0
  17. data/app/javascript/js/enable_controller.js +23 -0
  18. data/app/javascript/js/filter_controller.js +48 -0
  19. data/app/javascript/js/search_controller.js +18 -0
  20. data/app/javascript/js/select_controller.js +52 -0
  21. data/app/javascript/packs/rails-mini-profiler.js +69 -53
  22. data/app/javascript/stylesheets/components/buttons.scss +59 -0
  23. data/app/javascript/stylesheets/components/dropdown.scss +103 -0
  24. data/app/javascript/stylesheets/components/input.scss +10 -0
  25. data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +7 -13
  26. data/app/javascript/stylesheets/components/page_header.scss +7 -0
  27. data/app/javascript/stylesheets/components/pagination.scss +55 -0
  28. data/app/javascript/stylesheets/components/placeholder.scss +36 -0
  29. data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
  30. data/app/javascript/stylesheets/components/trace.scss +93 -0
  31. data/app/javascript/stylesheets/flamegraph.scss +3 -2
  32. data/app/javascript/stylesheets/flashes.scss +3 -5
  33. data/app/javascript/stylesheets/profiled_requests.scss +12 -161
  34. data/app/javascript/stylesheets/rails-mini-profiler.scss +82 -66
  35. data/app/javascript/stylesheets/traces.scss +52 -56
  36. data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +10 -17
  37. data/app/presenters/rails_mini_profiler/trace_presenter.rb +9 -7
  38. data/app/search/rails_mini_profiler/base_search.rb +67 -0
  39. data/app/search/rails_mini_profiler/profiled_request_search.rb +33 -0
  40. data/app/search/rails_mini_profiler/trace_search.rb +27 -0
  41. data/app/views/models/_flamegraph.json.jb +3 -0
  42. data/app/views/models/_profiled_request.jb +3 -0
  43. data/app/views/models/_trace.jb +3 -0
  44. data/app/views/rails_mini_profiler/badge.html.erb +2 -2
  45. data/app/views/rails_mini_profiler/flamegraphs/show.json.jb +3 -0
  46. data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
  47. data/app/views/rails_mini_profiler/profiled_requests/index.json.jb +3 -0
  48. data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +19 -0
  49. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
  50. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
  51. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
  52. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
  53. data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +23 -6
  54. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list.erb +12 -0
  55. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_header.erb +87 -0
  56. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_placeholder.erb +12 -0
  57. data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +5 -18
  58. data/app/views/rails_mini_profiler/profiled_requests/show.json.jb +5 -0
  59. data/app/views/rails_mini_profiler/shared/_head.erb +1 -1
  60. data/lib/generators/rails_mini_profiler/install_generator.rb +7 -1
  61. data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +11 -4
  62. data/lib/rails_mini_profiler/badge.rb +4 -2
  63. data/lib/rails_mini_profiler/{storage.rb → configuration/storage.rb} +0 -0
  64. data/lib/rails_mini_profiler/configuration/user_interface.rb +70 -0
  65. data/lib/rails_mini_profiler/configuration.rb +7 -7
  66. data/lib/rails_mini_profiler/engine.rb +12 -8
  67. data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
  68. data/lib/rails_mini_profiler/guard.rb +19 -1
  69. data/lib/rails_mini_profiler/middleware.rb +6 -4
  70. data/lib/rails_mini_profiler/redirect.rb +1 -1
  71. data/lib/rails_mini_profiler/request_context.rb +6 -1
  72. data/lib/rails_mini_profiler/request_wrapper.rb +44 -8
  73. data/lib/rails_mini_profiler/tracing/controller_tracer.rb +15 -0
  74. data/lib/rails_mini_profiler/tracing/null_trace.rb +7 -0
  75. data/lib/rails_mini_profiler/tracing/sequel_tracer.rb +37 -0
  76. data/lib/rails_mini_profiler/tracing/sequel_tracker.rb +37 -0
  77. data/lib/rails_mini_profiler/tracing/subscriptions.rb +34 -0
  78. data/lib/rails_mini_profiler/{models → tracing}/trace.rb +10 -2
  79. data/lib/rails_mini_profiler/tracing/trace_factory.rb +37 -0
  80. data/lib/rails_mini_profiler/tracing/tracer.rb +31 -0
  81. data/lib/rails_mini_profiler/tracing/view_tracer.rb +12 -0
  82. data/lib/rails_mini_profiler/tracing.rb +11 -0
  83. data/lib/rails_mini_profiler/user.rb +1 -1
  84. data/lib/rails_mini_profiler/version.rb +1 -4
  85. data/lib/rails_mini_profiler.rb +5 -7
  86. data/vendor/assets/images/check.svg +3 -0
  87. data/vendor/assets/images/chevron.svg +3 -0
  88. data/vendor/assets/images/copy.svg +6 -0
  89. data/vendor/assets/images/filter.svg +1 -0
  90. data/vendor/assets/images/logo_variant.svg +2 -2
  91. data/vendor/assets/images/search.svg +4 -5
  92. data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
  93. data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
  94. metadata +95 -11
  95. data/Rakefile +0 -18
  96. data/lib/rails_mini_profiler/tracers.rb +0 -85
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0dfb74da2ecab593753e57b0b23c131ffb7857d5c22ce167185a8ba29aa013b
4
- data.tar.gz: cc0d40ee213af658df6475062054814be1dcbec8471e894dd7fc13c8633dd734
3
+ metadata.gz: bb1212a4d90c46143193b2173e9e136aa6757a74a6591137cba33169235d175d
4
+ data.tar.gz: f799849a5d978c2c22d39b3fa1f4be6c02541f75b7a2312d98a510a1d69a195b
5
5
  SHA512:
6
- metadata.gz: f21f840089c68cd189c75bd00428d3f9f23a9b84a9c3e48de1fe2236d798ece02e7869a0bc4b265a6bb26018c347cc73d0e089d64d96bcf163ecf5bcda157f7e
7
- data.tar.gz: 8185fec8a94783a448f7340e395e3278d3eecc4b801acfd3b321a1ff2255c5d902037ecff71001b78e74371f94eabb76efed986d15a9decc307feec0a45779f2
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
  &nbsp; &nbsp; &nbsp; &nbsp;
86
- <img alt="Dark" src="docs/images/sequel.png" width="45%">
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
- By clicking on individual traces you can find out detailed information.
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
- | Option | Default | Description |
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` | Storage configuration. See [Storage](#Storage) |
118
- | `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Users](#Users) |
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
- | Configuration | Default | Description |
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
- ### Users
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
- Profiling information is segregated by user ID. That means users cannot see each other's profiled requests.
168
+ ### Authorization
161
169
 
162
- Per default, individual users are identified by their IP address. You may change this by setting a custom user provider:
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 from the application itself:
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
- Note that you **must** set the current user when running Rails Mini Profiler in production. No profiles will be saved otherwise.
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
- However, you can still use it in production to profile specific requests. Since profiling impacts performance, it is recommended
186
- that you limit which requests are being profiled:
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
- Only requests by explicitly set users will be stored. To configure how individual users are identified see [Users](#Users)
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 AMP tools such as [Skylight](https://www.skylight.io/) or [Scout APM](https://scoutapm.com), and each of these tools has its place.
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, using Flamegraphs and detailed query information.
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 supports API-only applications.
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 drop the offending tables and re-run migrations for the latest version:
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: 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.
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 < ActionController::Base
5
- rescue_from ActiveRecord::RecordNotFound, with: :not_found
4
+ class ApplicationController < RailsMiniProfiler.configuration.ui.base_controller
5
+ rescue_from ActiveRecord::RecordNotFound, with: ->(error) { handle(error, 404) }
6
6
 
7
- before_action :check_current_user
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 not_found(error)
21
- redirect_back(fallback_location: profiled_requests_path, alert: error.to_s)
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 check_current_user
25
- redirect_back(fallback_location: root_path) unless User.get(request.env).present?
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
- .includes(:flamegraph)
12
- .where(user_id: user_id).order(id: :desc)
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
- @traces = @profiled_request.traces
19
- @traces = @traces.where("#{payload_column} LIKE ?", "%#{params[:search]}%") if params[:search]
20
- @traces = @traces
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, profiled_request: @profiled_request) }
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
- requests_table_name = RailsMiniProfiler.storage_configuration.profiled_requests_table.to_sym
34
- Flamegraph.joins(:profiled_request).where(requests_table_name => { user_id: user_id }).delete_all
35
- Trace.joins(:profiled_request).where(requests_table_name => { user_id: user_id }).delete_all
36
- ProfiledRequest.where(requests_table_name => { user_id: user_id }).delete_all
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 Requests cleared'
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,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 407.437 407.437">
2
+ <path fill="currentColor" d="M386.258 91.567l-182.54 181.945L21.179 91.567 0 112.815 203.718 315.87l203.719-203.055z"/>
3
+ </svg>
@@ -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>path{fill:none;stroke-width:64;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4}</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
- <title>Iconly/Bulk/Search</title>
4
- <g id="Iconly/Bulk/Search" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
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
+ }