rails_mini_profiler 0.4.0 → 0.7.1

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +48 -34
  3. data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
  4. data/app/controllers/rails_mini_profiler/application_controller.rb +9 -15
  5. data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +44 -14
  6. data/app/helpers/rails_mini_profiler/application_helper.rb +1 -1
  7. data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
  8. data/app/javascript/images/check.svg +3 -0
  9. data/app/javascript/images/chevron.svg +3 -0
  10. data/app/javascript/images/copy.svg +6 -0
  11. data/app/javascript/images/filter.svg +1 -0
  12. data/app/javascript/images/logo_variant.svg +2 -2
  13. data/app/javascript/images/search.svg +4 -5
  14. data/app/javascript/js/checklist_controller.js +48 -0
  15. data/app/javascript/js/clipboard_controller.js +53 -0
  16. data/app/javascript/js/enable_controller.js +23 -0
  17. data/app/javascript/js/filter_controller.js +48 -0
  18. data/app/javascript/js/search_controller.js +18 -0
  19. data/app/javascript/js/select_controller.js +52 -0
  20. data/app/javascript/packs/rails-mini-profiler.js +69 -53
  21. data/app/javascript/stylesheets/components/buttons.scss +59 -0
  22. data/app/javascript/stylesheets/components/dropdown.scss +103 -0
  23. data/app/javascript/stylesheets/components/input.scss +10 -0
  24. data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +7 -13
  25. data/app/javascript/stylesheets/components/page_header.scss +7 -0
  26. data/app/javascript/stylesheets/components/pagination.scss +14 -13
  27. data/app/javascript/stylesheets/components/placeholder.scss +36 -0
  28. data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
  29. data/app/javascript/stylesheets/components/trace.scss +93 -0
  30. data/app/javascript/stylesheets/flamegraph.scss +3 -2
  31. data/app/javascript/stylesheets/flashes.scss +3 -5
  32. data/app/javascript/stylesheets/profiled_requests.scss +12 -161
  33. data/app/javascript/stylesheets/rails-mini-profiler.scss +81 -66
  34. data/app/javascript/stylesheets/traces.scss +52 -56
  35. data/app/models/rails_mini_profiler/profiled_request.rb +1 -1
  36. data/app/models/rails_mini_profiler/trace.rb +0 -15
  37. data/app/presenters/rails_mini_profiler/controller_trace_presenter.rb +10 -2
  38. data/app/presenters/rails_mini_profiler/instantiation_trace_presenter.rb +15 -3
  39. data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +10 -17
  40. data/app/presenters/rails_mini_profiler/render_partial_trace_presenter.rb +6 -2
  41. data/app/presenters/rails_mini_profiler/render_template_trace_presenter.rb +6 -2
  42. data/app/presenters/rails_mini_profiler/sequel_trace_presenter.rb +16 -4
  43. data/app/presenters/rails_mini_profiler/trace_presenter.rb +10 -8
  44. data/app/search/rails_mini_profiler/base_search.rb +67 -0
  45. data/app/search/rails_mini_profiler/profiled_request_search.rb +33 -0
  46. data/app/search/rails_mini_profiler/trace_search.rb +27 -0
  47. data/app/views/rails_mini_profiler/badge.html.erb +2 -2
  48. data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
  49. data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +19 -0
  50. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
  51. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
  52. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
  53. data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
  54. data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +24 -7
  55. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list.erb +12 -0
  56. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_header.erb +87 -0
  57. data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_placeholder.erb +12 -0
  58. data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +5 -18
  59. data/app/views/rails_mini_profiler/shared/_head.erb +1 -1
  60. data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +2 -0
  61. data/lib/rails_mini_profiler/badge.rb +21 -12
  62. data/lib/rails_mini_profiler/configuration/user_interface.rb +31 -1
  63. data/lib/rails_mini_profiler/configuration.rb +7 -0
  64. data/lib/rails_mini_profiler/engine.rb +12 -8
  65. data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
  66. data/lib/rails_mini_profiler/guard.rb +19 -1
  67. data/lib/rails_mini_profiler/middleware.rb +12 -10
  68. data/lib/rails_mini_profiler/request_context.rb +22 -18
  69. data/lib/rails_mini_profiler/request_wrapper.rb +12 -55
  70. data/lib/rails_mini_profiler/response_wrapper.rb +21 -17
  71. data/lib/rails_mini_profiler/tracers/controller_tracer.rb +29 -0
  72. data/lib/rails_mini_profiler/tracers/instantiation_tracer.rb +17 -0
  73. data/lib/rails_mini_profiler/tracers/null_trace.rb +7 -0
  74. data/lib/rails_mini_profiler/tracers/registry.rb +76 -0
  75. data/lib/rails_mini_profiler/tracers/rmp_tracer.rb +17 -0
  76. data/lib/rails_mini_profiler/tracers/sequel_tracer.rb +51 -0
  77. data/lib/rails_mini_profiler/tracers/sequel_tracker.rb +40 -0
  78. data/lib/rails_mini_profiler/tracers/subscriptions.rb +28 -0
  79. data/lib/rails_mini_profiler/{models → tracers}/trace.rb +11 -3
  80. data/lib/rails_mini_profiler/tracers/trace_factory.rb +27 -0
  81. data/lib/rails_mini_profiler/tracers/tracer.rb +45 -0
  82. data/lib/rails_mini_profiler/tracers/view_tracer.rb +29 -0
  83. data/lib/rails_mini_profiler/tracers.rb +12 -83
  84. data/lib/rails_mini_profiler/user.rb +1 -1
  85. data/lib/rails_mini_profiler/version.rb +1 -1
  86. data/lib/rails_mini_profiler.rb +4 -8
  87. data/vendor/assets/images/check.svg +3 -0
  88. data/vendor/assets/images/chevron.svg +3 -0
  89. data/vendor/assets/images/copy.svg +6 -0
  90. data/vendor/assets/images/filter.svg +1 -0
  91. data/vendor/assets/images/logo_variant.svg +2 -2
  92. data/vendor/assets/images/search.svg +4 -5
  93. data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
  94. data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
  95. metadata +58 -13
  96. data/app/models/rails_mini_profiler/controller_trace.rb +0 -37
  97. data/app/models/rails_mini_profiler/instantiation_trace.rb +0 -37
  98. data/app/models/rails_mini_profiler/render_partial_trace.rb +0 -37
  99. data/app/models/rails_mini_profiler/render_template_trace.rb +0 -37
  100. data/app/models/rails_mini_profiler/rmp_trace.rb +0 -35
  101. data/app/models/rails_mini_profiler/sequel_trace.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33db210bbcb4b8ee82f80aa2f88023a478ec4852e9bbcc60cf9f70baf2a0145f
4
- data.tar.gz: aed097f8f0e418feea8ce2670f173271bea7dd1f8803e542c2f50b82b953932d
3
+ metadata.gz: '08b93a903a5bb3b16310b3a918a1a52555a9ce5fc2a60624d0f2bb8158e3ddb5'
4
+ data.tar.gz: 88af5220a54c4f91e4506cd0423a853512dcc67235c65a22990d1c4fc1d92c25
5
5
  SHA512:
6
- metadata.gz: a6cc1c0d0e05949bb360d4e5d04091bcfb16419a80ca31361d617a1a34571d28dfc33f31820075646143b9a359b12aa66c9ec67bb859bbdc25feaca062b5adb8
7
- data.tar.gz: c6673b79dc6b8aacd205b5b368c1e044fb8b0e595fbb41371cf6f2c667fe31875cea9376ea551cb0e39a6c13c57cd654f41342b09bc4f41a1084a03092804fe9
6
+ metadata.gz: 2a41fec13f04188e895769fcb17004fdd6436aaddd8942ff4657beaffb7c0f4b63ca64d60f9877d119c1e363194ebd3c97e629216d0bf675fceb82cd49b6dcda
7
+ data.tar.gz: f5c81dbc3b627e48a0d9097fb587b37c2c299476adb87d01ace6c697e234b32f8fceee942045656f82f8c4d6ef6c082e58ad2f5619ccf2b4114b32764dae8c8f
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
 
@@ -114,15 +112,8 @@ Rails Mini Profiler provides a wide array of configuration options. You can find
114
112
  | `skip_paths` | `[]` | An array of request paths that should not be profiled. Regex allowed. |
115
113
  | `storage` | `Storage.new` | Storage configuration. See [Storage](#Storage). |
116
114
  | `ui` | `UserInterface.new` | UI configuration. See [UI](#UI). |
117
- | `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Users](#Users) |
118
-
119
- ### Request Configuration
115
+ | `user_provider` | `Rack::Request.new(env).ip` | How to identify users. See [Authorization](#Authorization) |
120
116
 
121
- You may override the configuration by sending request parameters. The following parameters are available:
122
-
123
- | Name | Description |
124
- | ---------------- | ------------------------------------------------------------------------------------------- |
125
- | `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediatly. |
126
117
 
127
118
  ### Storage
128
119
 
@@ -140,7 +131,7 @@ Rails Mini Profiler does not offer an automatic way to clean up old profiling in
140
131
 
141
132
  ```ruby
142
133
  # Clockwork
143
- every(1.month, 'purge rails mini profiler' do
134
+ every(1.month, 'purge rails mini profiler') do
144
135
  ProfiledRequestCleanupJob.perform_later
145
136
  end
146
137
 
@@ -158,23 +149,33 @@ end
158
149
 
159
150
  Rails Mini Profiler allows you to configure various UI features.
160
151
 
161
- | Name | Default | Description |
162
- |------------------|--------------|-------------------------------------------------------------------------------------------------|
163
- | `badge_enabled` | `true` | Should the hedgehog 🦔 badge be injected into pages? |
164
- | `badge_position` | `'top-left'` | Where to display the badge. Options are `'top-left', 'top-right', 'bottom-left, 'bottom-right'` |
165
- | `page_size` | `25` | The page size for lists shown in the UI. |
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. |
166
159
 
167
- ### Users
160
+ ### Request Configuration
168
161
 
169
- Profiling information is segregated by user ID. That means users cannot see each other's profiled requests.
162
+ You may override static configuration on a per-request by attaching request parameters. For example, `https://myapp.com/api/mydata?rmp_flamegraph=false`
170
163
 
171
- Per default, individual users are identified by their IP address. You may change this by setting a custom user provider:
164
+ | Name | Description |
165
+ | ---------------- | ------------------------------------------------------------------------------------------- |
166
+ | `rmp_flamegraph` | Overrides `flamegraph_enabled` If set to `true` will redirect to the flamegraph immediately. |
167
+
168
+ ### Authorization
169
+
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:
172
173
 
173
174
  ```ruby
174
175
  config.user_provider = proc { |env| Rack::Request.new(env).ip }
175
176
  ```
176
177
 
177
- 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):
178
179
 
179
180
  ```ruby
180
181
  class ApplicationController < ActionController::Base
@@ -184,15 +185,18 @@ class ApplicationController < ActionController::Base
184
185
  end
185
186
  ```
186
187
 
187
- 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`.
188
189
 
189
190
  ### Profiling in Production
190
191
 
191
192
  Rails Mini Profiler is not intended for performance reporting. There are other tools for that ( [Skylight](https://www.skylight.io/),
192
- [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.
193
196
 
194
- However, you can still use it in production to profile specific requests. Since profiling impacts performance, it is recommended
195
- 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:
196
200
 
197
201
  ```ruby
198
202
  RailsMiniProfiler.configure do |config|
@@ -200,7 +204,17 @@ RailsMiniProfiler.configure do |config|
200
204
  end
201
205
  ```
202
206
 
203
- 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
+ ```
204
218
 
205
219
  ## Why Rails Mini Profiler?
206
220
 
@@ -210,13 +224,13 @@ Improving the performance of any application is a 3-step process. You have to an
210
224
  2. Why is it slow?
211
225
  3. Did my solution fix the slowness?
212
226
 
213
- 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.
214
228
 
215
- 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.
216
230
 
217
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.
218
232
 
219
- 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.
220
234
 
221
235
  ## Troubleshooting
222
236
 
@@ -241,13 +255,13 @@ rails db:migrate
241
255
 
242
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):
243
257
 
244
- ```
258
+ ```ruby
245
259
  require "sprockets/railtie"
246
260
  ```
247
261
 
248
262
  Then, modify `application.rb`:
249
263
 
250
- ```
264
+ ```ruby
251
265
  module ApiOnly
252
266
  class Application < Rails::Application
253
267
 
@@ -257,7 +271,7 @@ module ApiOnly
257
271
  end
258
272
  ```
259
273
 
260
- **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.
261
275
 
262
276
  ### No Flamegraphs are being recored?
263
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,21 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsMiniProfiler
4
- class ApplicationController < ActionController::Base
5
- include Pagy::Backend
6
-
4
+ class ApplicationController < RailsMiniProfiler.configuration.ui.base_controller
7
5
  rescue_from ActiveRecord::RecordNotFound, with: ->(error) { handle(error, 404) }
8
6
 
9
- before_action :check_current_user
10
-
11
- protected
12
-
13
- def present(model, presenter_class = nil, **kwargs)
14
- klass = presenter_class || "RailsMiniProfiler::#{model.class.to_s.demodulize}Presenter".constantize
15
- presenter = klass.new(model, view_context, **kwargs)
16
- yield(presenter) if block_given?
17
- presenter
18
- end
7
+ before_action :check_rmp_user
19
8
 
20
9
  private
21
10
 
@@ -26,8 +15,13 @@ module RailsMiniProfiler
26
15
  end
27
16
  end
28
17
 
29
- def check_current_user
30
- redirect_back(fallback_location: root_path) unless User.get(request.env).present?
18
+ def check_rmp_user
19
+ user = User.get(request.env).present?
20
+ redirect_back(fallback_location: fallback_location) unless user
21
+ end
22
+
23
+ def fallback_location
24
+ defined?(main_app.root_path) ? main_app.root_path : '/'
31
25
  end
32
26
  end
33
27
  end
@@ -4,23 +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]
14
- @pagy, @profiled_requests = pagy(@profiled_requests, items: configuration.ui.page_size)
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)
15
15
  @profiled_requests = @profiled_requests.map { |request| present(request) }
16
16
  end
17
17
 
18
18
  def show
19
- @traces = @profiled_request.traces
20
- @traces = @traces.where("#{payload_column} LIKE ?", "%#{params[:search]}%") if params[:search]
21
- @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
22
27
  .order(:start)
23
- .map { |trace| present(trace, profiled_request: @profiled_request) }
28
+ .map { |trace| present(trace, context: context) }
24
29
  @profiled_request = present(@profiled_request)
25
30
  end
26
31
 
@@ -31,16 +36,25 @@ module RailsMiniProfiler
31
36
 
32
37
  def destroy_all
33
38
  ProfiledRequest.transaction do
34
- requests_table_name = RailsMiniProfiler.storage_configuration.profiled_requests_table.to_sym
35
- Flamegraph.joins(:profiled_request).where(requests_table_name => { user_id: user_id }).delete_all
36
- Trace.joins(:profiled_request).where(requests_table_name => { user_id: user_id }).delete_all
37
- 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
38
44
  end
39
- redirect_to profiled_requests_url, notice: 'Profiled Requests cleared'
45
+ redirect_to profiled_requests_url, notice: 'Profiled requests cleared', status: :see_other
40
46
  end
41
47
 
42
48
  private
43
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
+
44
58
  def user_id
45
59
  @user_id ||= User.get(request.env)
46
60
  end
@@ -53,6 +67,22 @@ module RailsMiniProfiler
53
67
  @configuration ||= RailsMiniProfiler.configuration
54
68
  end
55
69
 
70
+ def registry
71
+ @registry ||= RailsMiniProfiler::Tracers::Registry.new(configuration)
72
+ end
73
+
74
+ def present(model, presenter_class = nil, **kwargs)
75
+ klass = presenter_class || presenter_class(model)
76
+ klass.new(model, view_context, **kwargs)
77
+ end
78
+
79
+ def presenter_class(model)
80
+ return ProfiledRequestPresenter if model.is_a?(ProfiledRequest)
81
+
82
+ presenters = registry.presenters
83
+ presenters[model.name] || TracePresenter
84
+ end
85
+
56
86
  def payload_column
57
87
  if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
58
88
  # Cast json field to text to have access to the LIKE operator
@@ -12,7 +12,7 @@ module RailsMiniProfiler
12
12
  end
13
13
 
14
14
  def inline_svg(path, options = {})
15
- if defined?(Webpacker::Engine)
15
+ if defined?(Webpacker::Engine) && RailsMiniProfiler.configuration.ui.webpacker_enabled
16
16
  path = "media/images/#{path}"
17
17
  inline_svg_pack_tag(path, options)
18
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
+ }
@@ -0,0 +1,18 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["field"];
5
+
6
+ clear() {
7
+ this.eventTarget.value = null;
8
+ window.dispatchEvent(new CustomEvent("search-controller:submit", {}));
9
+ }
10
+
11
+ submit(event) {
12
+ event.preventDefault();
13
+
14
+ if (event.key === "Enter" || event.type === "click") {
15
+ window.dispatchEvent(new CustomEvent("search-controller:submit", {}));
16
+ }
17
+ }
18
+ }