rack-mini-profiler 1.1.6 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 343dec64a1a5e96e086a3cbceb07c418d08a891d5f89a9315472108e125a9282
4
- data.tar.gz: d242ccef7bd8c5aeb2224f7912886a0e05ce21ef571a3f6c76f384686b5f1525
3
+ metadata.gz: 2e54608bc4885796e11d6e4fac9cc56fe478f60c5cd43d53637451ef3524f086
4
+ data.tar.gz: 6a87a58c7821a279ce08b66432de9a199a6f775f897610e03e12d497eb7ccc46
5
5
  SHA512:
6
- metadata.gz: 394c8ce3d60dbb2638fd0ea58420e8372702cfa5043bf08bdd51e731abe3019f1299bb4e3c22b92fac70f477a2fe8783720a3f2c46d393dda7ace0509b02963b
7
- data.tar.gz: cf3ba12543795194bab109ff9fddd97ff0d0c349b8f505cebae202cf30b6e6d8e5058a1385ad4a8cbc747d27108c6351a4c7ddb53ccda14b9f68b7f4f9978b48
6
+ metadata.gz: 3730c9e4610fd90cc0fac86c1cddcf6181e15cef5932ba86f8f8cbca1df77c02ba7847c2e1868b48e51299b6c51224fe533818c3cc245dd02a2da11b2298569c
7
+ data.tar.gz: 98f54eed006f7f13840481b777dbe4add5f9983c5ed367f8046a942a584a5ad3eb4ea5ae6e3aef7e4aa0760b37c78ac5dd6ee5bcd45e42091a943dfabdb343b1
@@ -1,5 +1,28 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.0.4 - 2020-08-04
4
+
5
+ - [FIX] webpacker may exist with no config, allow for that
6
+
7
+ ## 2.0.3 - 2020-07-29
8
+
9
+ - [FIX] support for deprecation free Redis 4.2
10
+ - [FEATURE] skip /packs when serving static assets
11
+ - [FEATURE] allow Net::HTTP patch to be applied with either prerpend or alias
12
+
13
+ ## 2.0.2 - 2020-05-25
14
+
15
+ - [FIX] client timings were not showing up when you clicked show trivial
16
+
17
+ ## 2.0.1 - 2020-03-17
18
+
19
+ - [REVERT] Prepend Net::HTTP patch instead of class_eval and aliasing (#429) (technique clashes with New Relic and Skylight agents)
20
+
21
+ ## 2.0.0 - 2020-03-11
22
+
23
+ - [FEATURE] Prepend Net::HTTP patch instead of class_eval and aliasing (#429)
24
+ - [FEATURE] Stop patching Rails and use `ActiveSupport::Notifications` by default (see README.md for details)
25
+
3
26
  ## 1.1.6 - 2020-01-30
4
27
 
5
28
  - [FIX] edge condition on page transition function could lead to exceptions
data/README.md CHANGED
@@ -29,7 +29,7 @@ If you feel like taking on any of this start an issue and update us on your prog
29
29
 
30
30
  ## Installation
31
31
 
32
- Install/add to Gemfile in Ruby 2.3+
32
+ Install/add to Gemfile in Ruby 2.4+
33
33
 
34
34
  ```ruby
35
35
  gem 'rack-mini-profiler'
@@ -51,6 +51,36 @@ gem 'stackprof'
51
51
 
52
52
  All you have to do is to include the Gem and you're good to go in development. See notes below for use in production.
53
53
 
54
+ #### Upgrading to version 2.0.0
55
+
56
+ Prior to version 2.0.0, Mini Profiler patched various Rails methods to get the information it needed such as template rendering time. Starting from version 2.0.0, Mini Profiler doesn't patch any Rails methods by default and relies on `ActiveSupport::Notifications` to get the information it needs from Rails. If you want Mini Profiler to keep using its patches in version 2.0.0 and later, change the gem line in your `Gemfile` to the following:
57
+
58
+ If you want to manually require Mini Profiler:
59
+ ```ruby
60
+ gem 'rack-mini-profiler', require: ['enable_rails_patches']
61
+ ```
62
+
63
+ If you don't want to manually require Mini Profiler:
64
+ ```ruby
65
+ gem 'rack-mini-profiler', require: ['enable_rails_patches', 'rack-mini-profiler']
66
+ ```
67
+
68
+ #### `Net::HTTP` stack level too deep errors
69
+
70
+ If you start seeing `SystemStackError: stack level too deep` errors from `Net::HTTP` after installing Mini Profiler, this means there is another patch for `Net::HTTP#request` that conflicts with Mini Profiler's patch in your application. To fix this, change `rack-mini-profiler` gem line in your `Gemfile` to the following:
71
+
72
+ ```ruby
73
+ gem 'rack-mini-profiler', require: ['prepend_net_http_patch', 'rack-mini-profiler']
74
+ ```
75
+
76
+ If you currently have `require: false`, remove the `'rack-mini-profiler'` string from the `require` array above so the gem line becomes like this:
77
+
78
+ ```ruby
79
+ gem 'rack-mini-profiler', require: ['prepend_net_http_patch']
80
+ ```
81
+
82
+ This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this [ruby issue](https://bugs.ruby-lang.org/issues/11120) for details. The fix is to apply all patches the same way. Mini Profiler by default will apply its patch using method aliasing, but you can change that to module prepend by adding `require: ['prepend_net_http_patch']` to the gem line as shown above.
83
+
54
84
  #### Rails and manual initialization
55
85
 
56
86
  In case you need to make sure rack_mini_profiler is initialized after all other gems, or you want to execute some code before rack_mini_profiler required:
@@ -315,7 +345,7 @@ Option|Default|Description
315
345
  pre_authorize_cb|Rails: dev only<br>Rack: always on|A lambda callback that returns true to make mini_profiler visible on a given request.
316
346
  position|`'top-left'`|Display mini_profiler on `'top-right'`, `'top-left'`, `'bottom-right'` or `'bottom-left'`.
317
347
  skip_paths|`[]`|Paths that skip profiling.
318
- skip_schema_queries|Rails dev: `'true'`<br>Othwerwise: `'false'`|`'true'` to log schema queries.
348
+ skip_schema_queries|Rails dev: `true`<br>Othwerwise: `false`|`true` to skip schema queries.
319
349
  auto_inject|`true`|`true` to inject the miniprofiler script in the page.
320
350
  backtrace_ignores|`[]`|Regexes of lines to be removed from backtraces.
321
351
  backtrace_includes|Rails: `[/^\/?(app\|config\|lib\|test)/]`<br>Rack: `[]`|Regexes of lines to keep in backtraces.
@@ -331,32 +361,13 @@ html_container|`body`|The HTML container (as a jQuery selector) to inject the mi
331
361
  show_total_sql_count|`false`|Displays the total number of SQL executions.
332
362
  enable_advanced_debugging_tools|`false`|Enables sensitive debugging tools that can be used via the UI. In production we recommend keeping this disabled as memory and environment debugging tools can expose contents of memory that may contain passwords.
333
363
 
334
- ### Custom middleware ordering (required if using `Rack::Deflate` with Rails)
335
-
336
- If you are using `Rack::Deflate` with rails and rack-mini-profiler in its default configuration,
337
- `Rack::MiniProfiler` will be injected (as always) at position 0 in the middleware stack. This
338
- will result in it attempting to inject html into the already-compressed response body. To fix this,
339
- the middleware ordering must be overriden.
340
-
341
- To do this, first add `, require: false` to the gemfile entry for rack-mini-profiler.
342
- This will prevent the railtie from running. Then, customize the initialization
343
- in the initializer like so:
344
-
345
- ```ruby
346
- require 'rack-mini-profiler'
347
-
348
- Rack::MiniProfilerRails.initialize!(Rails.application)
349
-
350
- Rails.application.middleware.delete(Rack::MiniProfiler)
351
- Rails.application.middleware.insert_after(Rack::Deflater, Rack::MiniProfiler)
352
- ```
353
-
354
- Deleting the middleware and then reinserting it is a bit inelegant, but
355
- a sufficient and costless solution. It is possible that rack-mini-profiler might
356
- support this scenario more directly if it is found that
357
- there is significant need for this confriguration or that
358
- the above recipe causes problems.
364
+ ### Using MiniProfiler with `Rack::Deflate` middleware
359
365
 
366
+ If you are using `Rack::Deflate` with Rails and `rack-mini-profiler` in its default configuration,
367
+ `Rack::MiniProfiler` will be injected (as always) at position 0 in the middleware stack,
368
+ which means it will run after `Rack::Deflate` on response processing. To prevent attempting to inject
369
+ HTML in already compressed response body MiniProfiler will suppress compression by setting
370
+ `identity` encoding in `Accept-Encoding` request header.
360
371
 
361
372
  ## Special query strings
362
373
 
@@ -393,6 +404,16 @@ if JSON.const_defined?(:Pure)
393
404
  end
394
405
  ```
395
406
 
407
+ ## Development
408
+
409
+ If you want to contribute to this project, that's great, thank you! You can run the following rake task:
410
+
411
+ ```
412
+ $ bundle exec rake client_dev
413
+ ```
414
+
415
+ which will start a local Sinatra server at `http://localhost:9292` where you'll be able to preview your changes. Refreshing the page should be enough to see any changes you make to files in the `lib/html` directory.
416
+
396
417
  ## Running the Specs
397
418
 
398
419
  ```
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ MINI_PROFILER_ENABLE_RAILS_PATCHES = true
5
+ end
@@ -207,8 +207,7 @@
207
207
  top: 0px; }
208
208
  .profiler-results.profiler-top.profiler-left {
209
209
  left: 0px; }
210
- .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-totals,
211
- .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
210
+ .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,
212
211
  .profiler-results.profiler-top.profiler-left .profiler-controls {
213
212
  -webkit-border-bottom-right-radius: 10px;
214
213
  -moz-border-radius-bottomright: 10px;
@@ -218,8 +217,7 @@
218
217
  border-right: 1px solid #888; }
219
218
  .profiler-results.profiler-top.profiler-right {
220
219
  right: 0px; }
221
- .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-totals,
222
- .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
220
+ .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-top.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,
223
221
  .profiler-results.profiler-top.profiler-right .profiler-controls {
224
222
  -webkit-border-bottom-left-radius: 10px;
225
223
  -moz-border-radius-bottomleft: 10px;
@@ -231,8 +229,7 @@
231
229
  bottom: 0px; }
232
230
  .profiler-results.profiler-bottom.profiler-left {
233
231
  left: 0px; }
234
- .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-totals,
235
- .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-result:first-child .profiler-button,
232
+ .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-left.profiler-no-controls .profiler-result:first-child .profiler-button,
236
233
  .profiler-results.profiler-bottom.profiler-left .profiler-controls {
237
234
  -webkit-border-top-right-radius: 10px;
238
235
  -moz-border-radius-topright: 10px;
@@ -242,8 +239,7 @@
242
239
  border-right: 1px solid #888; }
243
240
  .profiler-results.profiler-bottom.profiler-right {
244
241
  right: 0px; }
245
- .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-totals,
246
- .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-result:first-child .profiler-button,
242
+ .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-totals, .profiler-results.profiler-bottom.profiler-right.profiler-no-controls .profiler-result:first-child .profiler-button,
247
243
  .profiler-results.profiler-bottom.profiler-right .profiler-controls {
248
244
  -webkit-border-bottom-top-radius: 10px;
249
245
  -moz-border-radius-topleft: 10px;
@@ -349,7 +345,6 @@
349
345
  @media print {
350
346
  .profiler-results {
351
347
  display: none; } }
352
-
353
348
  .profiler-queries-bg {
354
349
  z-index: 2147483642;
355
350
  display: none;
@@ -108,18 +108,8 @@ var MiniProfiler = (function() {
108
108
  // ie is buggy strip out functions
109
109
  var copy = {
110
110
  navigation: {},
111
- timing: {}
111
+ timing: clientPerformance.timing.toJSON()
112
112
  };
113
- var timing = extend({}, clientPerformance.timing);
114
-
115
- for (p in timing) {
116
- if (
117
- timing.hasOwnProperty(p) &&
118
- !(typeof timing[p] === "function")
119
- ) {
120
- copy.timing[p] = timing[p];
121
- }
122
- }
123
113
 
124
114
  if (clientPerformance.navigation) {
125
115
  copy.navigation.redirectCount =
@@ -147,10 +137,13 @@ var MiniProfiler = (function() {
147
137
  (function() {
148
138
  var request = new XMLHttpRequest();
149
139
  var url = options.path + "results";
150
- var params = "id="
151
- .concat(id, "&clientPerformance=")
152
- .concat(clientPerformance, "&clientProbes=")
153
- .concat(clientProbes, "&popup=1");
140
+ var params = {
141
+ id: id,
142
+ clientPerformance: clientPerformance,
143
+ clientProbes: clientProbes,
144
+ popup: 1
145
+ };
146
+ var queryParam = toQueryString(params);
154
147
  request.open("POST", url, true);
155
148
 
156
149
  request.onload = function() {
@@ -172,24 +165,45 @@ var MiniProfiler = (function() {
172
165
  "Content-Type",
173
166
  "application/x-www-form-urlencoded"
174
167
  );
175
- request.send(params);
168
+ request.send(queryParam);
176
169
  })();
177
170
  }
178
171
  }
179
172
  };
180
173
 
181
- var extend = function extend(out) {
182
- out = out || {};
183
-
184
- for (var i = 1; i < _arguments.length; i++) {
185
- if (!_arguments[i]) continue;
186
-
187
- for (var key in _arguments[i]) {
188
- if (_arguments[i].hasOwnProperty(key)) out[key] = _arguments[i][key];
174
+ var toQueryString = function toQueryString(data, parentKey) {
175
+ var result = [];
176
+ for (var key in data) {
177
+ var val = data[key];
178
+ var newKey = !parentKey ? key : parentKey + "[" + key + "]";
179
+ if (
180
+ typeof val === "object" &&
181
+ !Array.isArray(val) &&
182
+ val !== null &&
183
+ val !== undefined
184
+ ) {
185
+ result[result.length] = toQueryString(val, newKey);
186
+ } else {
187
+ if (Array.isArray(val)) {
188
+ val.forEach(function(v) {
189
+ result[result.length] =
190
+ encodeURIComponent(newKey + "[]") + "=" + encodeURIComponent(v);
191
+ });
192
+ } else if (val === null || val === undefined) {
193
+ result[result.length] = encodeURIComponent(newKey) + "=";
194
+ } else {
195
+ result[result.length] =
196
+ encodeURIComponent(newKey) +
197
+ "=" +
198
+ encodeURIComponent(val.toString());
199
+ }
189
200
  }
190
201
  }
191
-
192
- return out;
202
+ return result
203
+ .filter(function(element) {
204
+ return element && element.length > 0;
205
+ })
206
+ .join("&");
193
207
  };
194
208
 
195
209
  var renderTemplate = function renderTemplate(json) {
@@ -128,8 +128,8 @@
128
128
  {{? it.custom_link}}
129
129
  <a href="{{= it.custom_link }}" class="profiler-custom-link" target="_blank">{{= it.custom_link_name }}</a>
130
130
  {{?}}
131
- {{? it.has_trivial_timings}}
132
- <a class="profiler-toggle-trivial" data-show-on-load="{{= it.has_all_trivial_timings }}" title="toggles any rows with &lt; {{= it.trivial_duration_threshold_milliseconds }} ms">
131
+ {{? it.page.has_trivial_timings}}
132
+ <a class="profiler-toggle-trivial" data-show-on-load="{{= it.page.has_all_trivial_timings }}" title="toggles any rows with &lt; {{= it.page.trivial_duration_threshold_milliseconds }} ms">
133
133
  show trivial
134
134
  </a>
135
135
  {{?}}
@@ -11,7 +11,7 @@ var out=' <div class="profiler-result"> <div class="profiler-button ';if(it.has_
11
11
  }
12
12
  MiniProfiler.templates["linksTemplate"] = function anonymous(it
13
13
  ) {
14
- var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.has_all_trivial_timings )+'" title="toggles any rows with &lt; '+( it.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
14
+ var out=' <a href="'+( MiniProfiler.shareUrl(it.page.id) )+'" class="profiler-share-profiler-results" target="_blank">share</a> <a href="'+( MiniProfiler.moreUrl(it.timing.name) )+'" class="profiler-more-actions">more</a> ';if(it.custom_link){out+=' <a href="'+( it.custom_link )+'" class="profiler-custom-link" target="_blank">'+( it.custom_link_name )+'</a> ';}out+=' ';if(it.page.has_trivial_timings){out+=' <a class="profiler-toggle-trivial" data-show-on-load="'+( it.page.has_all_trivial_timings )+'" title="toggles any rows with &lt; '+( it.page.trivial_duration_threshold_milliseconds )+' ms"> show trivial </a> ';}return out;
15
15
  }
16
16
  MiniProfiler.templates["timingTemplate"] = function anonymous(it
17
17
  ) {
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Rack
3
3
  class MiniProfiler
4
- ASSET_VERSION = '67dd1c2571ced7fc74ae7f1813e47bdf'
4
+ ASSET_VERSION = '015eebd28435014a417b0b8cf057054d'
5
5
  end
6
6
  end
@@ -5,6 +5,11 @@ module Rack
5
5
  class << self
6
6
 
7
7
  include Rack::MiniProfiler::ProfilingMethods
8
+ attr_accessor :subscribe_sql_active_record
9
+
10
+ def patch_rails?
11
+ !!defined?(Rack::MINI_PROFILER_ENABLE_RAILS_PATCHES)
12
+ end
8
13
 
9
14
  def generate_id
10
15
  rand(36**20).to_s(36)
@@ -67,6 +72,17 @@ module Rack
67
72
  This feature is disabled by default, to enable set the enable_advanced_debugging_tools option to true in Mini Profiler config.
68
73
  TEXT
69
74
  end
75
+
76
+ def binds_to_params(binds)
77
+ return if binds.nil? || config.max_sql_param_length == 0
78
+ # map ActiveRecord::Relation::QueryAttribute to [name, value]
79
+ params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
80
+ if (skip = config.skip_sql_param_names)
81
+ params.map { |(n, v)| n =~ skip ? [n, nil] : [n, v] }
82
+ else
83
+ params
84
+ end
85
+ end
70
86
  end
71
87
 
72
88
  #
@@ -136,7 +152,7 @@ module Rack
136
152
  resources_env = env.dup
137
153
  resources_env['PATH_INFO'] = file_name
138
154
 
139
- rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => 'max-age:86400')
155
+ rack_file = Rack::File.new(MiniProfiler.resources_root, 'Cache-Control' => "max-age:#{cache_control_value}")
140
156
  rack_file.call(resources_env)
141
157
  end
142
158
 
@@ -292,6 +308,15 @@ module Rack
292
308
  status, headers, body = @app.call(env)
293
309
  end
294
310
  end
311
+ elsif path == '/rack-mini-profiler/requests'
312
+ blank_page_html = <<~HTML
313
+ <html>
314
+ <head></head>
315
+ <body></body>
316
+ </html>
317
+ HTML
318
+
319
+ status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]]
295
320
  else
296
321
  status, headers, body = @app.call(env)
297
322
  end
@@ -348,17 +373,6 @@ module Rack
348
373
  return client_settings.handle_cookie(self.flamegraph(flamegraph))
349
374
  end
350
375
 
351
- if path == '/rack-mini-profiler/requests'
352
- blank_page_html = <<~HTML
353
- <html>
354
- <head></head>
355
- <body></body>
356
- </html>
357
- HTML
358
-
359
- status, headers, body = [200, { 'Content-Type' => 'text/html' }, [blank_page_html.dup]]
360
- end
361
-
362
376
  begin
363
377
  @storage.save(page_struct)
364
378
  # no matter what it is, it should be unviewed, otherwise we will miss POST
@@ -657,5 +671,8 @@ Append the following to your query string:
657
671
  current.inject_js = false
658
672
  end
659
673
 
674
+ def cache_control_value
675
+ 86400
676
+ end
660
677
  end
661
678
  end
@@ -33,7 +33,7 @@ module Rack
33
33
 
34
34
  def set_unviewed(user, id)
35
35
  key = user_key(user)
36
- if redis.exists(prefixed_id(id))
36
+ if redis.call([:exists, prefixed_id(id)]) == 1
37
37
  expire_at = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i + redis.ttl(prefixed_id(id))
38
38
  redis.zadd(key, expire_at, id)
39
39
  end
@@ -44,7 +44,7 @@ module Rack
44
44
  key = user_key(user)
45
45
  redis.del(key)
46
46
  ids.each do |id|
47
- if redis.exists(prefixed_id(id))
47
+ if redis.call([:exists, prefixed_id(id)]) == 1
48
48
  expire_at = Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i + redis.ttl(prefixed_id(id))
49
49
  redis.zadd(key, expire_at, id)
50
50
  end
@@ -6,6 +6,7 @@ module Rack
6
6
  # Timing system for a custom timers such as cache, redis, RPC, external API
7
7
  # calls, etc.
8
8
  class Custom < TimerStruct::Base
9
+ attr_accessor :parent
9
10
  def initialize(type, duration_ms, page, parent)
10
11
  @parent = parent
11
12
  @page = page
@@ -11,7 +11,7 @@ module Rack
11
11
  end
12
12
  end
13
13
 
14
- attr_accessor :children_duration
14
+ attr_accessor :children_duration, :start, :parent
15
15
 
16
16
  def initialize(name, page, parent)
17
17
  start_millis = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i - page[:started]
@@ -62,10 +62,6 @@ module Rack
62
62
  self[:start_milliseconds]
63
63
  end
64
64
 
65
- def start
66
- @start
67
- end
68
-
69
65
  def depth
70
66
  self[:depth]
71
67
  end
@@ -91,6 +87,20 @@ module Rack
91
87
  end
92
88
  end
93
89
 
90
+ def move_child(child, destination)
91
+ if index = self[:children].index(child)
92
+ self[:children].slice!(index)
93
+ self[:has_children] = self[:children].size > 0
94
+
95
+ destination[:children].push(child)
96
+ destination[:has_children] = true
97
+
98
+ child[:parent_timing_id] = destination[:id]
99
+ child.parent = destination
100
+ child.adjust_depth
101
+ end
102
+ end
103
+
94
104
  def add_sql(query, elapsed_ms, page, params = nil, skip_backtrace = false, full_backtrace = false)
95
105
  TimerStruct::Sql.new(query, elapsed_ms, page, self, params, skip_backtrace, full_backtrace).tap do |timer|
96
106
  self[:sql_timings].push(timer)
@@ -102,6 +112,19 @@ module Rack
102
112
  end
103
113
  end
104
114
 
115
+ def move_sql(sql, destination)
116
+ if index = self[:sql_timings].index(sql)
117
+ self[:sql_timings].slice!(index)
118
+ self[:has_sql_timings] = self[:sql_timings].size > 0
119
+ self[:sql_timings_duration_milliseconds] -= sql[:duration_milliseconds]
120
+ destination[:sql_timings].push(sql)
121
+ destination[:has_sql_timings] = true
122
+ destination[:sql_timings_duration_milliseconds] += sql[:duration_milliseconds]
123
+ sql[:parent_timing_id] = destination[:id]
124
+ sql.parent = destination
125
+ end
126
+ end
127
+
105
128
  def add_custom(type, elapsed_ms, page)
106
129
  TimerStruct::Custom.new(type, elapsed_ms, page, self).tap do |timer|
107
130
  timer[:parent_timing_id] = self[:id]
@@ -119,18 +142,37 @@ module Rack
119
142
  end
120
143
  end
121
144
 
145
+ def move_custom(type, custom, destination)
146
+ if index = self[:custom_timings][type]&.index(custom)
147
+ custom[:parent_timing_id] = destination[:id]
148
+ custom.parent = destination
149
+ self[:custom_timings][type].slice!(index)
150
+ if self[:custom_timings][type].size == 0
151
+ self[:custom_timings].delete(type)
152
+ self[:custom_timing_stats].delete(type)
153
+ else
154
+ self[:custom_timing_stats][type][:count] -= 1
155
+ self[:custom_timing_stats][type][:duration] -= custom[:duration_milliseconds]
156
+ end
157
+ destination[:custom_timings][type] ||= []
158
+ destination[:custom_timings][type].push(custom)
159
+ destination[:custom_timing_stats][type] ||= { count: 0, duration: 0.0 }
160
+ destination[:custom_timing_stats][type][:count] += 1
161
+ destination[:custom_timing_stats][type][:duration] += custom[:duration_milliseconds]
162
+ end
163
+ end
164
+
122
165
  def record_time(milliseconds = nil)
123
166
  milliseconds ||= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @start) * 1000
124
167
  self[:duration_milliseconds] = milliseconds
125
168
  self[:is_trivial] = true if milliseconds < self[:trivial_duration_threshold_milliseconds]
126
- self[:duration_without_children_milliseconds] = milliseconds - @children_duration
127
-
128
- if @parent
129
- @parent.children_duration += milliseconds
130
- end
131
-
169
+ self[:duration_without_children_milliseconds] = milliseconds - self[:children].sum(&:duration_ms)
132
170
  end
133
171
 
172
+ def adjust_depth
173
+ self[:depth] = self.parent ? self.parent[:depth] + 1 : 0
174
+ self[:children].each(&:adjust_depth)
175
+ end
134
176
  end
135
177
  end
136
178
  end
@@ -6,6 +6,8 @@ module Rack
6
6
  # Timing system for a SQL query
7
7
  module TimerStruct
8
8
  class Sql < TimerStruct::Base
9
+ attr_accessor :parent
10
+
9
11
  def initialize(query, duration_ms, page, parent, params = nil, skip_backtrace = false, full_backtrace = false)
10
12
 
11
13
  stack_trace = nil
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  class MiniProfiler
5
- VERSION = '1.1.6'
5
+ VERSION = '2.0.4'
6
6
  end
7
7
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fileutils'
4
+ require_relative './railtie_methods'
4
5
 
5
6
  module Rack::MiniProfilerRails
7
+ extend Rack::MiniProfilerRailsMethods
6
8
 
7
9
  # call direct if needed to do a defer init
8
10
  def self.initialize!(app)
@@ -28,6 +30,8 @@ module Rack::MiniProfilerRails
28
30
 
29
31
  if serves_static_assets?(app)
30
32
  c.skip_paths << app.config.assets.prefix
33
+ wp_assets_path = get_webpacker_assets_path()
34
+ c.skip_paths << wp_assets_path if wp_assets_path
31
35
  end
32
36
 
33
37
  unless Rails.env.development? || Rails.env.test?
@@ -55,17 +59,81 @@ module Rack::MiniProfilerRails
55
59
 
56
60
  # Install the Middleware
57
61
  app.middleware.insert(0, Rack::MiniProfiler)
62
+ c.enable_advanced_debugging_tools = Rails.env.development?
63
+
64
+ if ::Rack::MiniProfiler.patch_rails?
65
+ # Attach to various Rails methods
66
+ ActiveSupport.on_load(:action_controller) do
67
+ ::Rack::MiniProfiler.profile_method(ActionController::Base, :process) { |action| "Executing action: #{action}" }
68
+ end
69
+
70
+ ActiveSupport.on_load(:action_view) do
71
+ ::Rack::MiniProfiler.profile_method(ActionView::Template, :render) { |x, y| "Rendering: #{@virtual_path}" }
72
+ end
73
+ else
74
+ subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
75
+ next if !should_measure?
76
+
77
+ current = Rack::MiniProfiler.current
78
+ description = "Executing action: #{payload[:action]}"
79
+ Thread.current[get_key(payload)] = current.current_timer
80
+ Rack::MiniProfiler.current.current_timer = current.current_timer.add_child(description)
81
+ end
82
+
83
+ subscribe("process_action.action_controller") do |name, start, finish, id, payload|
84
+ next if !should_measure?
85
+
86
+ key = get_key(payload)
87
+ parent_timer = Thread.current[key]
88
+ next if !parent_timer
89
+
90
+ Thread.current[key] = nil
91
+ Rack::MiniProfiler.current.current_timer.record_time
92
+ Rack::MiniProfiler.current.current_timer = parent_timer
93
+ end
94
+
95
+ subscribe("render_partial.action_view") do |name, start, finish, id, payload|
96
+ render_notification_handler(shorten_identifier(payload[:identifier]), finish, start)
97
+ end
58
98
 
59
- # Attach to various Rails methods
60
- ActiveSupport.on_load(:action_controller) do
61
- ::Rack::MiniProfiler.profile_method(ActionController::Base, :process) { |action| "Executing action: #{action}" }
99
+ subscribe("render_template.action_view") do |name, start, finish, id, payload|
100
+ render_notification_handler(shorten_identifier(payload[:identifier]), finish, start)
101
+ end
102
+
103
+ if Rack::MiniProfiler.subscribe_sql_active_record
104
+ # we don't want to subscribe if we've already patched a DB driver
105
+ # otherwise we would end up with 2 records for every query
106
+ subscribe("sql.active_record") do |name, start, finish, id, payload|
107
+ next if !should_measure?
108
+ next if payload[:name] =~ /SCHEMA/ && Rack::MiniProfiler.config.skip_schema_queries
109
+
110
+ Rack::MiniProfiler.record_sql(
111
+ payload[:sql],
112
+ (finish - start) * 1000,
113
+ Rack::MiniProfiler.binds_to_params(payload[:binds])
114
+ )
115
+ end
116
+ end
62
117
  end
63
- ActiveSupport.on_load(:action_view) do
64
- ::Rack::MiniProfiler.profile_method(ActionView::Template, :render) { |x, y| "Rendering: #{@virtual_path}" }
118
+ @already_initialized = true
119
+ end
120
+
121
+ def self.subscribe(event, &blk)
122
+ if ActiveSupport::Notifications.respond_to?(:monotonic_subscribe)
123
+ ActiveSupport::Notifications.monotonic_subscribe(event) { |*args| blk.call(*args) }
124
+ else
125
+ ActiveSupport::Notifications.subscribe(event) do |name, start, finish, id, payload|
126
+ blk.call(name, start.to_f, finish.to_f, id, payload)
127
+ end
65
128
  end
129
+ end
66
130
 
67
- c.enable_advanced_debugging_tools = Rails.env.development?
68
- @already_initialized = true
131
+ def self.get_key(payload)
132
+ "mini_profiler_parent_timer_#{payload[:controller]}_#{payload[:action]}".to_sym
133
+ end
134
+
135
+ def self.shorten_identifier(identifier)
136
+ identifier.split('/').last(2).join('/')
69
137
  end
70
138
 
71
139
  def self.serves_static_assets?(app)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack::MiniProfilerRailsMethods
4
+ def render_notification_handler(name, finish, start, name_as_description: false)
5
+ return if !should_measure?
6
+
7
+ description = name_as_description ? name : "Rendering: #{name}"
8
+ current = Rack::MiniProfiler.current.current_timer
9
+ node = current.add_child(description)
10
+ duration = finish - start
11
+ duration_ms = duration * 1000
12
+ node.start -= duration
13
+ node[:start_milliseconds] -= duration_ms
14
+ node.record_time(duration_ms)
15
+
16
+ children_duration = 0
17
+ to_be_moved = { requests: [], sql: [], custom: {} }
18
+ current.children.each do |child|
19
+ next if child == node
20
+ if should_move?(child, node)
21
+ to_be_moved[:requests] << child
22
+ children_duration += child[:duration_milliseconds]
23
+ end
24
+ end
25
+ node[:duration_without_children_milliseconds] = duration_ms - children_duration
26
+ to_be_moved[:requests].each { |req| current.move_child(req, node) }
27
+
28
+ current.sql_timings.each do |sql|
29
+ to_be_moved[:sql] << sql if should_move?(sql, node)
30
+ end
31
+ to_be_moved[:sql].each { |sql| current.move_sql(sql, node) }
32
+
33
+ current.custom_timings.each do |type, timings|
34
+ to_be_moved[:custom] = []
35
+ timings.each do |custom|
36
+ to_be_moved[:custom] << custom if should_move?(custom, node)
37
+ end
38
+ to_be_moved[:custom].each { |custom| current.move_custom(type, custom, node) }
39
+ end
40
+ end
41
+
42
+ def should_measure?
43
+ current = Rack::MiniProfiler.current
44
+ current && current.measure
45
+ end
46
+
47
+ def should_move?(child, node)
48
+ start = :start_milliseconds
49
+ duration = :duration_milliseconds
50
+ child[start] >= node[start] &&
51
+ child[start] + child[duration] <= node[start] + node[duration]
52
+ end
53
+
54
+ def get_webpacker_assets_path
55
+ if defined?(Webpacker) && Webpacker.config.config_path.exist?
56
+ Webpacker.config.public_output_path.to_s.gsub(Webpacker.config.public_path.to_s, "")
57
+ end
58
+ end
59
+
60
+ extend self
61
+ end
@@ -15,17 +15,6 @@ module Rack
15
15
  end
16
16
  end
17
17
 
18
- def binds_to_params(binds)
19
- return if binds.nil? || Rack::MiniProfiler.config.max_sql_param_length == 0
20
- # map ActiveRecord::Relation::QueryAttribute to [name, value]
21
- params = binds.map { |c| c.kind_of?(Array) ? [c.first, c.last] : [c.name, c.value] }
22
- if (skip = Rack::MiniProfiler.config.skip_sql_param_names)
23
- params.map { |(n, v)| n =~ skip ? [n, nil] : [n, v] }
24
- else
25
- params
26
- end
27
- end
28
-
29
18
  def log_with_miniprofiler(*args, &block)
30
19
  return log_without_miniprofiler(*args, &block) unless SqlPatches.should_measure?
31
20
 
@@ -37,7 +26,7 @@ module Rack
37
26
  return rval if Rack::MiniProfiler.config.skip_schema_queries && name =~ (/SCHEMA/)
38
27
 
39
28
  elapsed_time = SqlPatches.elapsed_time(start)
40
- Rack::MiniProfiler.record_sql(sql, elapsed_time, binds_to_params(binds))
29
+ Rack::MiniProfiler.record_sql(sql, elapsed_time, Rack::MiniProfiler.binds_to_params(binds))
41
30
  rval
42
31
  end
43
32
  end
@@ -2,15 +2,25 @@
2
2
 
3
3
  if (defined?(Net) && defined?(Net::HTTP))
4
4
 
5
- Net::HTTP.class_eval do
6
- def request_with_mini_profiler(*args, &block)
7
- request = args[0]
8
- Rack::MiniProfiler.step("Net::HTTP #{request.method} #{request.path}") do
9
- request_without_mini_profiler(*args, &block)
5
+ if defined?(Rack::MINI_PROFILER_PREPEND_NET_HTTP_PATCH)
6
+ module NetHTTPWithMiniProfiler
7
+ def request(request, *args, &block)
8
+ Rack::MiniProfiler.step("Net::HTTP #{request.method} #{request.path}") do
9
+ super
10
+ end
10
11
  end
11
12
  end
12
- alias request_without_mini_profiler request
13
- alias request request_with_mini_profiler
13
+ Net::HTTP.prepend(NetHTTPWithMiniProfiler)
14
+ else
15
+ Net::HTTP.class_eval do
16
+ def request_with_mini_profiler(*args, &block)
17
+ request = args[0]
18
+ Rack::MiniProfiler.step("Net::HTTP #{request.method} #{request.path}") do
19
+ request_without_mini_profiler(*args, &block)
20
+ end
21
+ end
22
+ alias request_without_mini_profiler request
23
+ alias request request_with_mini_profiler
24
+ end
14
25
  end
15
-
16
26
  end
@@ -23,18 +23,26 @@ class SqlPatches
23
23
  ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time).to_f * 1000).round(1)
24
24
  end
25
25
 
26
+ def self.patch_rails?
27
+ ::Rack::MiniProfiler.patch_rails?
28
+ end
29
+
26
30
  def self.sql_patches
27
31
  patches = []
28
32
 
29
33
  patches << 'mysql2' if defined?(Mysql2::Client) && Mysql2::Client.class == Class
30
34
  patches << 'pg' if defined?(PG::Result) && PG::Result.class == Class
31
35
  patches << 'oracle_enhanced' if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) && ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class == Class &&
32
- SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
36
+ SqlPatches.correct_version?('~> 1.5.0', ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter) &&
37
+ patch_rails?
33
38
  # if the adapters were directly patched, don't patch again
34
- return patches unless patches.empty?
39
+ if !patches.empty?
40
+ Rack::MiniProfiler.subscribe_sql_active_record = false
41
+ return patches
42
+ end
35
43
  patches << 'sequel' if defined?(Sequel::Database) && Sequel::Database.class == Class
36
- patches << 'activerecord' if defined?(ActiveRecord) && ActiveRecord.class == Module
37
-
44
+ patches << 'activerecord' if defined?(ActiveRecord) && ActiveRecord.class == Module && patch_rails?
45
+ Rack::MiniProfiler.subscribe_sql_active_record = patches.empty? && !patch_rails?
38
46
  patches
39
47
  end
40
48
 
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rack
4
+ MINI_PROFILER_PREPEND_NET_HTTP_PATCH = true
5
+ end
@@ -27,7 +27,6 @@ require 'mini_profiler/context'
27
27
  require 'mini_profiler/client_settings'
28
28
  require 'mini_profiler/gc_profiler'
29
29
  require 'mini_profiler/profiler'
30
-
31
30
  require 'patches/sql_patches'
32
31
  require 'patches/net_patches'
33
32
 
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.authors = ["Sam Saffron", "Robin Ward", "Aleks Totic"]
12
12
  s.description = "Profiling toolkit for Rack applications with Rails integration. Client Side profiling, DB profiling and Server profiling."
13
13
  s.email = "sam.saffron@gmail.com"
14
- s.homepage = "http://miniprofiler.com"
14
+ s.homepage = "https://miniprofiler.com"
15
15
  s.license = "MIT"
16
16
  s.files = [
17
17
  'rack-mini-profiler.gemspec',
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  "CHANGELOG.md"
22
22
  ]
23
23
  s.add_runtime_dependency 'rack', '>= 1.2.0'
24
- s.required_ruby_version = '>= 2.3.0'
24
+ s.required_ruby_version = '>= 2.4.0'
25
25
 
26
26
  s.metadata = {
27
27
  'source_code_uri' => 'https://github.com/MiniProfiler/rack-mini-profiler',
@@ -30,7 +30,6 @@ Gem::Specification.new do |s|
30
30
 
31
31
  s.add_development_dependency 'rake', '< 11'
32
32
  s.add_development_dependency 'rack-test'
33
- s.add_development_dependency 'activerecord', '~> 3.0'
34
33
  s.add_development_dependency 'dalli'
35
34
  s.add_development_dependency 'rspec', '~> 3.6.0'
36
35
  s.add_development_dependency 'redis'
@@ -39,6 +38,10 @@ Gem::Specification.new do |s|
39
38
  s.add_development_dependency 'rubocop'
40
39
  s.add_development_dependency 'mini_racer'
41
40
  s.add_development_dependency 'nokogiri'
41
+ s.add_development_dependency 'rubocop-discourse'
42
+ s.add_development_dependency 'listen'
43
+ s.add_development_dependency 'webpacker', '~> 5.1'
44
+ s.add_development_dependency 'rails', '~> 5.1'
42
45
 
43
46
  s.require_paths = ["lib"]
44
47
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-mini-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  - Robin Ward
9
9
  - Aleks Totic
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-01-30 00:00:00.000000000 Z
13
+ date: 2020-08-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -54,20 +54,6 @@ dependencies:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
- - !ruby/object:Gem::Dependency
58
- name: activerecord
59
- requirement: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - "~>"
62
- - !ruby/object:Gem::Version
63
- version: '3.0'
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: '3.0'
71
57
  - !ruby/object:Gem::Dependency
72
58
  name: dalli
73
59
  requirement: !ruby/object:Gem::Requirement
@@ -180,6 +166,62 @@ dependencies:
180
166
  - - ">="
181
167
  - !ruby/object:Gem::Version
182
168
  version: '0'
169
+ - !ruby/object:Gem::Dependency
170
+ name: rubocop-discourse
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ - !ruby/object:Gem::Dependency
184
+ name: listen
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ - !ruby/object:Gem::Dependency
198
+ name: webpacker
199
+ requirement: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - "~>"
202
+ - !ruby/object:Gem::Version
203
+ version: '5.1'
204
+ type: :development
205
+ prerelease: false
206
+ version_requirements: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - "~>"
209
+ - !ruby/object:Gem::Version
210
+ version: '5.1'
211
+ - !ruby/object:Gem::Dependency
212
+ name: rails
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - "~>"
216
+ - !ruby/object:Gem::Version
217
+ version: '5.1'
218
+ type: :development
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - "~>"
223
+ - !ruby/object:Gem::Version
224
+ version: '5.1'
183
225
  description: Profiling toolkit for Rack applications with Rails integration. Client
184
226
  Side profiling, DB profiling and Server profiling.
185
227
  email: sam.saffron@gmail.com
@@ -191,6 +233,7 @@ extra_rdoc_files:
191
233
  files:
192
234
  - CHANGELOG.md
193
235
  - README.md
236
+ - lib/enable_rails_patches.rb
194
237
  - lib/generators/rack_profiler/USAGE
195
238
  - lib/generators/rack_profiler/install_generator.rb
196
239
  - lib/generators/rack_profiler/templates/rack_profiler.rb
@@ -223,6 +266,7 @@ files:
223
266
  - lib/mini_profiler/timer_struct/sql.rb
224
267
  - lib/mini_profiler/version.rb
225
268
  - lib/mini_profiler_rails/railtie.rb
269
+ - lib/mini_profiler_rails/railtie_methods.rb
226
270
  - lib/patches/db/activerecord.rb
227
271
  - lib/patches/db/mongo.rb
228
272
  - lib/patches/db/moped.rb
@@ -237,15 +281,16 @@ files:
237
281
  - lib/patches/db/sequel.rb
238
282
  - lib/patches/net_patches.rb
239
283
  - lib/patches/sql_patches.rb
284
+ - lib/prepend_net_http_patch.rb
240
285
  - lib/rack-mini-profiler.rb
241
286
  - rack-mini-profiler.gemspec
242
- homepage: http://miniprofiler.com
287
+ homepage: https://miniprofiler.com
243
288
  licenses:
244
289
  - MIT
245
290
  metadata:
246
291
  source_code_uri: https://github.com/MiniProfiler/rack-mini-profiler
247
292
  changelog_uri: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/CHANGELOG.md
248
- post_install_message:
293
+ post_install_message:
249
294
  rdoc_options: []
250
295
  require_paths:
251
296
  - lib
@@ -253,7 +298,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
253
298
  requirements:
254
299
  - - ">="
255
300
  - !ruby/object:Gem::Version
256
- version: 2.3.0
301
+ version: 2.4.0
257
302
  required_rubygems_version: !ruby/object:Gem::Requirement
258
303
  requirements:
259
304
  - - ">="
@@ -261,7 +306,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
306
  version: '0'
262
307
  requirements: []
263
308
  rubygems_version: 3.0.3
264
- signing_key:
309
+ signing_key:
265
310
  specification_version: 4
266
311
  summary: Profiles loading speed for rack applications.
267
312
  test_files: []