rack-mini-profiler 0.1.3 → 0.1.8

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.

Potentially problematic release.


This version of rack-mini-profiler might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -15,3 +15,18 @@
15
15
  * Added option to display full backtraces pp=full-backtrace
16
16
  * Cleaned up railties, got rid of the post authorize callback
17
17
  * Version 0.1.3
18
+
19
+ 12-July-2012 - Sam
20
+
21
+ * Fixed incorrect profiling steps (was not indenting or measuring start time right
22
+ * Implemented native PG and MySql2 interceptors, this gives way more accurate times
23
+ * Refactored context so its a proper class and not a hash
24
+ * Added some more client probing built in to rails
25
+ * More tests
26
+
27
+ 18-July-2012 - Sam
28
+
29
+ * Added First Paint time for chrome
30
+ * Bug fix to ensure non Rails installs have mini profiler
31
+ * Version 0.1.7
32
+
data/README.md CHANGED
@@ -47,6 +47,24 @@ class MyApp < Sinatra::Base
47
47
  end
48
48
  ```
49
49
 
50
+ ## Storage
51
+
52
+ By default, rack-mini-profiler stores its results in a memory store:
53
+
54
+ ```ruby
55
+ # our default
56
+ Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
57
+ ```
58
+
59
+ There are 2 other available storage engines, `RedisStore` and `FileStore`.
60
+
61
+ MemoryStore is stores results in a processes heap - something that does not work well in a multi process environment.
62
+ FileStore stores results in the file system - something that may not work well in a multi machine environment.
63
+
64
+ Additionally you may implement an AbstractStore for your own provider.
65
+
66
+ Rails hooks up a FileStore for all environments.
67
+
50
68
  ## Running the Specs
51
69
 
52
70
  ```
@@ -1 +1,75 @@
1
- .profiler-result,.profiler-queries{color:#555;line-height:1;font-size:12px;}.profiler-result pre,.profiler-queries pre,.profiler-result code,.profiler-queries code,.profiler-result label,.profiler-queries label,.profiler-result table,.profiler-queries table,.profiler-result tbody,.profiler-queries tbody,.profiler-result thead,.profiler-queries thead,.profiler-result tfoot,.profiler-queries tfoot,.profiler-result tr,.profiler-queries tr,.profiler-result th,.profiler-queries th,.profiler-result td,.profiler-queries td{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;background-color:transparent;overflow:visible;max-height:none;}.profiler-result table,.profiler-queries table{border-collapse:collapse;border-spacing:0;}.profiler-result a,.profiler-queries a,.profiler-result a:hover,.profiler-queries a:hover{cursor:pointer;color:#07c;}.profiler-result a,.profiler-queries a{text-decoration:none;}.profiler-result a:hover,.profiler-queries a:hover{text-decoration:underline;}.profiler-result{font-family:Helvetica,Arial,sans-serif;}.profiler-result .profiler-toggle-duration-with-children{float:right;}.profiler-result table.profiler-client-timings{margin-top:10px;}.profiler-result .profiler-label{color:#555;overflow:hidden;text-overflow:ellipsis;}.profiler-result .profiler-unit{color:#aaa;}.profiler-result .profiler-trivial{display:none;}.profiler-result .profiler-trivial td,.profiler-result .profiler-trivial td *{color:#aaa !important;}.profiler-result pre,.profiler-result code,.profiler-result .profiler-number,.profiler-result .profiler-unit{font-family:Consolas,monospace,serif;}.profiler-result .profiler-number{color:#111;}.profiler-result .profiler-info{text-align:right;}.profiler-result .profiler-info .profiler-name{float:left;}.profiler-result .profiler-info .profiler-server-time{white-space:nowrap;}.profiler-result .profiler-timings th{background-color:#fff;color:#aaa;text-align:right;}.profiler-result .profiler-timings th,.profiler-result .profiler-timings td{white-space:nowrap;}.profiler-result .profiler-timings .profiler-duration-with-children{display:none;}.profiler-result .profiler-timings .profiler-duration{font-family:Consolas,monospace,serif;color:#111;text-align:right;}.profiler-result .profiler-timings .profiler-indent{letter-spacing:4px;}.profiler-result .profiler-timings .profiler-queries-show .profiler-number,.profiler-result .profiler-timings .profiler-queries-show .profiler-unit{color:#07c;}.profiler-result .profiler-timings .profiler-queries-duration{padding-left:6px;}.profiler-result .profiler-timings .profiler-percent-in-sql{white-space:nowrap;text-align:right;}.profiler-result .profiler-timings tfoot td{padding-top:10px;text-align:right;}.profiler-result .profiler-timings tfoot td a{font-size:95%;display:inline-block;margin-left:12px;}.profiler-result .profiler-timings tfoot td a:first-child{float:left;margin-left:0px;}.profiler-result .profiler-queries{font-family:Helvetica,Arial,sans-serif;}.profiler-result .profiler-queries .profiler-stack-trace{margin-bottom:15px;}.profiler-result .profiler-queries pre{font-family:Consolas,monospace,serif;white-space:pre-wrap;}.profiler-result .profiler-queries th{background-color:#fff;border-bottom:1px solid #555;font-weight:bold;padding:15px;white-space:nowrap;}.profiler-result .profiler-queries td{padding:15px;text-align:left;background-color:#fff;}.profiler-result .profiler-queries td:last-child{padding-right:25px;}.profiler-result .profiler-queries .profiler-odd td{background-color:#e5e5e5;}.profiler-result .profiler-queries .profiler-since-start,.profiler-result .profiler-queries .profiler-duration{text-align:right;}.profiler-result .profiler-queries .profiler-info div{text-align:right;margin-bottom:5px;}.profiler-result .profiler-queries .profiler-gap-info,.profiler-result .profiler-queries .profiler-gap-info td{background-color:#ccc;}.profiler-result .profiler-queries .profiler-gap-info .profiler-unit{color:#777;}.profiler-result .profiler-queries .profiler-gap-info .profiler-info{text-align:right;}.profiler-result .profiler-queries .profiler-gap-info.profiler-trivial-gaps{display:none;}.profiler-result .profiler-queries .profiler-trivial-gap-container{text-align:center;}.profiler-result .profiler-queries .str{color:maroon;}.profiler-result .profiler-queries .kwd{color:#00008b;}.profiler-result .profiler-queries .com{color:gray;}.profiler-result .profiler-queries .typ{color:#2b91af;}.profiler-result .profiler-queries .lit{color:maroon;}.profiler-result .profiler-queries .pun{color:#000;}.profiler-result .profiler-queries .pln{color:#000;}.profiler-result .profiler-queries .tag{color:maroon;}.profiler-result .profiler-queries .atn{color:red;}.profiler-result .profiler-queries .atv{color:blue;}.profiler-result .profiler-queries .dec{color:purple;}.profiler-result .profiler-warning,.profiler-result .profiler-warning *,.profiler-result .profiler-warning .profiler-queries-show,.profiler-result .profiler-warning .profiler-queries-show .profiler-unit{color:#f00;}.profiler-result .profiler-warning:hover,.profiler-result .profiler-warning *:hover,.profiler-result .profiler-warning .profiler-queries-show:hover,.profiler-result .profiler-warning .profiler-queries-show .profiler-unit:hover{color:#f00;}.profiler-result .profiler-nuclear{color:#f00;font-weight:bold;padding-right:2px;}.profiler-result .profiler-nuclear:hover{color:#f00;}.profiler-results{z-index:2147483643;position:fixed;top:0px;}.profiler-results.profiler-left{left:0px;}.profiler-results.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,.profiler-results.profiler-left .profiler-controls{-webkit-border-bottom-right-radius:10px;-moz-border-radius-bottomright:10px;border-bottom-right-radius:10px;}.profiler-results.profiler-left .profiler-button,.profiler-results.profiler-left .profiler-controls{border-right:1px solid #888;}.profiler-results.profiler-right{right:0px;}.profiler-results.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,.profiler-results.profiler-right .profiler-controls{-webkit-border-bottom-left-radius:10px;-moz-border-radius-bottomleft:10px;border-bottom-left-radius:10px;}.profiler-results.profiler-right .profiler-button,.profiler-results.profiler-right .profiler-controls{border-left:1px solid #888;}.profiler-results .profiler-button,.profiler-results .profiler-controls{display:none;z-index:2147483640;border-bottom:1px solid #888;background-color:#fff;padding:4px 7px;text-align:right;cursor:pointer;}.profiler-results .profiler-button.profiler-button-active,.profiler-results .profiler-controls.profiler-button-active{background-color:maroon;}.profiler-results .profiler-button.profiler-button-active .profiler-number,.profiler-results .profiler-controls.profiler-button-active .profiler-number,.profiler-results .profiler-button.profiler-button-active .profiler-nuclear,.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear{color:#fff;font-weight:bold;}.profiler-results .profiler-button.profiler-button-active .profiler-unit,.profiler-results .profiler-controls.profiler-button-active .profiler-unit{color:#fff;font-weight:normal;}.profiler-results .profiler-controls{display:block;font-size:12px;font-family:Consolas,monospace,serif;cursor:default;text-align:center;}.profiler-results .profiler-controls span{border-right:1px solid #aaa;padding-right:5px;margin-right:5px;cursor:pointer;}.profiler-results .profiler-controls span:last-child{border-right:none;}.profiler-results .profiler-popup{display:none;z-index:2147483641;position:absolute;background-color:#fff;border:1px solid #aaa;padding:5px 10px;text-align:left;line-height:18px;overflow:auto;-moz-box-shadow:0px 1px 15px #555;-webkit-box-shadow:0px 1px 15px #555;box-shadow:0px 1px 15px #555;}.profiler-results .profiler-popup .profiler-info{margin-bottom:3px;padding-bottom:2px;border-bottom:1px solid #ddd;}.profiler-results .profiler-popup .profiler-info .profiler-name{font-size:110%;font-weight:bold;}.profiler-results .profiler-popup .profiler-info .profiler-name .profiler-overall-duration{display:none;}.profiler-results .profiler-popup .profiler-info .profiler-server-time{font-size:95%;}.profiler-results .profiler-popup .profiler-timings th,.profiler-results .profiler-popup .profiler-timings td{padding-left:6px;padding-right:6px;}.profiler-results .profiler-popup .profiler-timings th{font-size:95%;padding-bottom:3px;}.profiler-results .profiler-popup .profiler-timings .profiler-label{max-width:275px;}.profiler-results .profiler-queries{display:none;z-index:2147483643;position:absolute;overflow-y:auto;overflow-x:hidden;background-color:#fff;}.profiler-results .profiler-queries th{font-size:17px;}.profiler-results.profiler-min .profiler-result{display:none;}.profiler-results.profiler-min .profiler-controls span{display:none;}.profiler-results.profiler-min .profiler-controls .profiler-min-max{border-right:none;padding:0px;margin:0px;}.profiler-queries-bg{z-index:2147483642;display:none;background:#000;opacity:0.7;position:absolute;top:0px;left:0px;min-width:100%;}.profiler-result-full .profiler-result{width:950px;margin:30px auto;}.profiler-result-full .profiler-result .profiler-button{display:none;}.profiler-result-full .profiler-result .profiler-popup .profiler-info{font-size:25px;border-bottom:1px solid #aaa;padding-bottom:3px;margin-bottom:25px;}.profiler-result-full .profiler-result .profiler-popup .profiler-info .profiler-overall-duration{padding-right:20px;font-size:80%;color:#888;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings td,.profiler-result-full .profiler-result .profiler-popup .profiler-timings th{padding-left:8px;padding-right:8px;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings th{padding-bottom:7px;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings td{font-size:14px;padding-bottom:4px;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings td:first-child{padding-left:10px;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings .profiler-label{max-width:550px;}.profiler-result-full .profiler-result .profiler-queries{margin:25px 0;}.profiler-result-full .profiler-result .profiler-queries table{width:100%;}.profiler-result-full .profiler-result .profiler-queries th{font-size:16px;color:#555;line-height:20px;}.profiler-result-full .profiler-result .profiler-queries td{padding:15px 10px;text-align:left;}.profiler-result-full .profiler-result .profiler-queries .profiler-info div{text-align:right;margin-bottom:5px;}
1
+ .profiler-result,.profiler-queries{color:#555;line-height:1;font-size:12px;}.profiler-result pre,.profiler-queries pre,.profiler-result code,.profiler-queries code,.profiler-result label,.profiler-queries label,.profiler-result table,.profiler-queries table,.profiler-result tbody,.profiler-queries tbody,.profiler-result thead,.profiler-queries thead,.profiler-result tfoot,.profiler-queries tfoot,.profiler-result tr,.profiler-queries tr,.profiler-result th,.profiler-queries th,.profiler-result td,.profiler-queries td{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;background-color:transparent;overflow:visible;max-height:none;}
2
+ .profiler-result table,.profiler-queries table{border-collapse:collapse;border-spacing:0;}
3
+ .profiler-result a,.profiler-queries a,.profiler-result a:hover,.profiler-queries a:hover{cursor:pointer;color:#0077cc;}
4
+ .profiler-result a,.profiler-queries a{text-decoration:none;}.profiler-result a:hover,.profiler-queries a:hover{text-decoration:underline;}
5
+ .profiler-result{font-family:Helvetica,Arial,sans-serif;}.profiler-result .profiler-toggle-duration-with-children{float:right;}
6
+ .profiler-result table.profiler-client-timings{margin-top:10px;}
7
+ .profiler-result .profiler-label{color:#555555;overflow:hidden;text-overflow:ellipsis;}
8
+ .profiler-result .profiler-unit{color:#aaaaaa;}
9
+ .profiler-result .profiler-trivial{display:none;}.profiler-result .profiler-trivial td,.profiler-result .profiler-trivial td *{color:#aaaaaa !important;}
10
+ .profiler-result pre,.profiler-result code,.profiler-result .profiler-number,.profiler-result .profiler-unit{font-family:Consolas,monospace,serif;}
11
+ .profiler-result .profiler-number{color:#111111;}
12
+ .profiler-result .profiler-info{text-align:right;}.profiler-result .profiler-info .profiler-name{float:left;}
13
+ .profiler-result .profiler-info .profiler-server-time{white-space:nowrap;}
14
+ .profiler-result .profiler-timings th{background-color:#fff;color:#aaaaaa;text-align:right;}
15
+ .profiler-result .profiler-timings th,.profiler-result .profiler-timings td{white-space:nowrap;}
16
+ .profiler-result .profiler-timings .profiler-duration-with-children{display:none;}
17
+ .profiler-result .profiler-timings .profiler-duration{font-family:Consolas,monospace,serif;color:#111111;text-align:right;}
18
+ .profiler-result .profiler-timings .profiler-indent{letter-spacing:4px;}
19
+ .profiler-result .profiler-timings .profiler-queries-show .profiler-number,.profiler-result .profiler-timings .profiler-queries-show .profiler-unit{color:#0077cc;}
20
+ .profiler-result .profiler-timings .profiler-queries-duration{padding-left:6px;}
21
+ .profiler-result .profiler-timings .profiler-percent-in-sql{white-space:nowrap;text-align:right;}
22
+ .profiler-result .profiler-timings tfoot td{padding-top:10px;text-align:right;}.profiler-result .profiler-timings tfoot td a{font-size:95%;display:inline-block;margin-left:12px;}.profiler-result .profiler-timings tfoot td a:first-child{float:left;margin-left:0px;}
23
+ .profiler-result .profiler-queries{font-family:Helvetica,Arial,sans-serif;}.profiler-result .profiler-queries .profiler-stack-trace{margin-bottom:15px;}
24
+ .profiler-result .profiler-queries pre{font-family:Consolas,monospace,serif;white-space:pre-wrap;}
25
+ .profiler-result .profiler-queries th{background-color:#fff;border-bottom:1px solid #555;font-weight:bold;padding:15px;white-space:nowrap;}
26
+ .profiler-result .profiler-queries td{padding:15px;text-align:left;background-color:#fff;}.profiler-result .profiler-queries td:last-child{padding-right:25px;}
27
+ .profiler-result .profiler-queries .profiler-odd td{background-color:#e5e5e5;}
28
+ .profiler-result .profiler-queries .profiler-since-start,.profiler-result .profiler-queries .profiler-duration{text-align:right;}
29
+ .profiler-result .profiler-queries .profiler-info div{text-align:right;margin-bottom:5px;}
30
+ .profiler-result .profiler-queries .profiler-gap-info,.profiler-result .profiler-queries .profiler-gap-info td{background-color:#ccc;}
31
+ .profiler-result .profiler-queries .profiler-gap-info .profiler-unit{color:#777;}
32
+ .profiler-result .profiler-queries .profiler-gap-info .profiler-info{text-align:right;}
33
+ .profiler-result .profiler-queries .profiler-gap-info.profiler-trivial-gaps{display:none;}
34
+ .profiler-result .profiler-queries .profiler-trivial-gap-container{text-align:center;}
35
+ .profiler-result .profiler-queries .str{color:#800000;}
36
+ .profiler-result .profiler-queries .kwd{color:#00008b;}
37
+ .profiler-result .profiler-queries .com{color:#808080;}
38
+ .profiler-result .profiler-queries .typ{color:#2b91af;}
39
+ .profiler-result .profiler-queries .lit{color:#800000;}
40
+ .profiler-result .profiler-queries .pun{color:#000000;}
41
+ .profiler-result .profiler-queries .pln{color:#000000;}
42
+ .profiler-result .profiler-queries .tag{color:#800000;}
43
+ .profiler-result .profiler-queries .atn{color:#ff0000;}
44
+ .profiler-result .profiler-queries .atv{color:#0000ff;}
45
+ .profiler-result .profiler-queries .dec{color:#800080;}
46
+ .profiler-result .profiler-warning,.profiler-result .profiler-warning *,.profiler-result .profiler-warning .profiler-queries-show,.profiler-result .profiler-warning .profiler-queries-show .profiler-unit{color:#f00;}.profiler-result .profiler-warning:hover,.profiler-result .profiler-warning *:hover,.profiler-result .profiler-warning .profiler-queries-show:hover,.profiler-result .profiler-warning .profiler-queries-show .profiler-unit:hover{color:#f00;}
47
+ .profiler-result .profiler-nuclear{color:#f00;font-weight:bold;padding-right:2px;}.profiler-result .profiler-nuclear:hover{color:#f00;}
48
+ .profiler-results{z-index:2147483643;position:fixed;top:0px;}.profiler-results.profiler-left{left:0px;}.profiler-results.profiler-left.profiler-no-controls .profiler-result:last-child .profiler-button,.profiler-results.profiler-left .profiler-controls{-webkit-border-bottom-right-radius:10px;-moz-border-radius-bottomright:10px;border-bottom-right-radius:10px;}
49
+ .profiler-results.profiler-left .profiler-button,.profiler-results.profiler-left .profiler-controls{border-right:1px solid #888888;}
50
+ .profiler-results.profiler-right{right:0px;}.profiler-results.profiler-right.profiler-no-controls .profiler-result:last-child .profiler-button,.profiler-results.profiler-right .profiler-controls{-webkit-border-bottom-left-radius:10px;-moz-border-radius-bottomleft:10px;border-bottom-left-radius:10px;}
51
+ .profiler-results.profiler-right .profiler-button,.profiler-results.profiler-right .profiler-controls{border-left:1px solid #888888;}
52
+ .profiler-results .profiler-button,.profiler-results .profiler-controls{display:none;z-index:2147483640;border-bottom:1px solid #888888;background-color:#fff;padding:4px 7px;text-align:right;cursor:pointer;}.profiler-results .profiler-button.profiler-button-active,.profiler-results .profiler-controls.profiler-button-active{background-color:maroon;}.profiler-results .profiler-button.profiler-button-active .profiler-number,.profiler-results .profiler-controls.profiler-button-active .profiler-number,.profiler-results .profiler-button.profiler-button-active .profiler-nuclear,.profiler-results .profiler-controls.profiler-button-active .profiler-nuclear{color:#fff;font-weight:bold;}
53
+ .profiler-results .profiler-button.profiler-button-active .profiler-unit,.profiler-results .profiler-controls.profiler-button-active .profiler-unit{color:#fff;font-weight:normal;}
54
+ .profiler-results .profiler-controls{display:block;font-size:12px;font-family:Consolas,monospace,serif;cursor:default;text-align:center;}.profiler-results .profiler-controls span{border-right:1px solid #aaaaaa;padding-right:5px;margin-right:5px;cursor:pointer;}
55
+ .profiler-results .profiler-controls span:last-child{border-right:none;}
56
+ .profiler-results .profiler-popup{display:none;z-index:2147483641;position:absolute;background-color:#fff;border:1px solid #aaa;padding:5px 10px;text-align:left;line-height:18px;overflow:auto;-moz-box-shadow:0px 1px 15px #555555;-webkit-box-shadow:0px 1px 15px #555555;box-shadow:0px 1px 15px #555555;}.profiler-results .profiler-popup .profiler-info{margin-bottom:3px;padding-bottom:2px;border-bottom:1px solid #ddd;}.profiler-results .profiler-popup .profiler-info .profiler-name{font-size:110%;font-weight:bold;}.profiler-results .profiler-popup .profiler-info .profiler-name .profiler-overall-duration{display:none;}
57
+ .profiler-results .profiler-popup .profiler-info .profiler-server-time{font-size:95%;}
58
+ .profiler-results .profiler-popup .profiler-timings th,.profiler-results .profiler-popup .profiler-timings td{padding-left:6px;padding-right:6px;}
59
+ .profiler-results .profiler-popup .profiler-timings th{font-size:95%;padding-bottom:3px;}
60
+ .profiler-results .profiler-popup .profiler-timings .profiler-label{max-width:275px;}
61
+ .profiler-results .profiler-queries{display:none;z-index:2147483643;position:absolute;overflow-y:auto;overflow-x:hidden;background-color:#fff;}.profiler-results .profiler-queries th{font-size:17px;}
62
+ .profiler-results.profiler-min .profiler-result{display:none;}
63
+ .profiler-results.profiler-min .profiler-controls span{display:none;}
64
+ .profiler-results.profiler-min .profiler-controls .profiler-min-max{border-right:none;padding:0px;margin:0px;}
65
+ .profiler-queries-bg{z-index:2147483642;display:none;background:#000;opacity:0.7;position:absolute;top:0px;left:0px;min-width:100%;}
66
+ .profiler-result-full .profiler-result{width:950px;margin:30px auto;}.profiler-result-full .profiler-result .profiler-button{display:none;}
67
+ .profiler-result-full .profiler-result .profiler-popup .profiler-info{font-size:25px;border-bottom:1px solid #aaaaaa;padding-bottom:3px;margin-bottom:25px;}.profiler-result-full .profiler-result .profiler-popup .profiler-info .profiler-overall-duration{padding-right:20px;font-size:80%;color:#888;}
68
+ .profiler-result-full .profiler-result .profiler-popup .profiler-timings td,.profiler-result-full .profiler-result .profiler-popup .profiler-timings th{padding-left:8px;padding-right:8px;}
69
+ .profiler-result-full .profiler-result .profiler-popup .profiler-timings th{padding-bottom:7px;}
70
+ .profiler-result-full .profiler-result .profiler-popup .profiler-timings td{font-size:14px;padding-bottom:4px;}.profiler-result-full .profiler-result .profiler-popup .profiler-timings td:first-child{padding-left:10px;}
71
+ .profiler-result-full .profiler-result .profiler-popup .profiler-timings .profiler-label{max-width:550px;}
72
+ .profiler-result-full .profiler-result .profiler-queries{margin:25px 0;}.profiler-result-full .profiler-result .profiler-queries table{width:100%;}
73
+ .profiler-result-full .profiler-result .profiler-queries th{font-size:16px;color:#555;line-height:20px;}
74
+ .profiler-result-full .profiler-result .profiler-queries td{padding:15px 10px;text-align:left;}
75
+ .profiler-result-full .profiler-result .profiler-queries .profiler-info div{text-align:right;margin-bottom:5px;}
@@ -100,6 +100,18 @@ var MiniProfiler = (function ($) {
100
100
  copy.navigation.redirectCount = clientPerformance.navigation.redirectCount;
101
101
  }
102
102
  clientPerformance = copy;
103
+
104
+ // hack to add chrome timings
105
+ if (window.chrome && window.chrome.loadTimes) {
106
+ var chromeTimes = window.chrome.loadTimes();
107
+ if (chromeTimes.firstPaintTime) {
108
+ clientPerformance.timing["First Paint Time"] = Math.round(chromeTimes.firstPaintTime * 1000);
109
+ }
110
+ if (chromeTimes.firstPaintTime) {
111
+ clientPerformance.timing["First Paint After Load Time"] = Math.round(chromeTimes.firstPaintAfterLoadTime * 1000);
112
+ }
113
+
114
+ }
103
115
  }
104
116
  }
105
117
 
@@ -593,7 +605,7 @@ var MiniProfiler = (function ($) {
593
605
 
594
606
  for (var i = 0; i < clientTimings.Timings.length; i++) {
595
607
  t = clientTimings.Timings[i];
596
- var trivial = t.Name != "Dom Complete" && t.Name != "Response";
608
+ var trivial = t.Name != "Dom Complete" && t.Name != "Response" && t.Name != "First Paint Time";
597
609
  trivial = t.Duration < 2 ? trivial : false;
598
610
  list.push(
599
611
  {
@@ -811,4 +823,4 @@ PR_TAG:"tag",PR_TYPE:S}})()
811
823
  PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],["kwd",/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,
812
824
  null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^[a-z_][\w-]*/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),["sql"])
813
825
 
814
- ;
826
+ ;
@@ -1,4 +1,4 @@
1
- .box-shadow(@dx, @dy, @radius, @color) {
1
+ .box-shadow(@dx, @dy, @radius, @color) {
2
2
  -moz-box-shadow: @dx @dy @radius @color;
3
3
  -webkit-box-shadow: @dx @dy @radius @color;
4
4
  box-shadow: @dx @dy @radius @color;
@@ -1,6 +1,6 @@
1
- <script type="text/javascript">
1
+ <script type="text/javascript">
2
2
  (function(){{
3
- var init = function() {{
3
+ var init = function() {{
4
4
  var load = function(s,f){{
5
5
  var sc = document.createElement('script');
6
6
  sc.async = 'async';
@@ -14,8 +14,8 @@
14
14
  }};
15
15
 
16
16
  document.getElementsByTagName('head')[0].appendChild(sc);
17
- }};
18
-
17
+ }};
18
+
19
19
  var initMp = function(){{
20
20
  load('{path}includes.js?v={version}',function(){{
21
21
  MiniProfiler.init({{
@@ -32,18 +32,18 @@
32
32
  }});
33
33
  }});
34
34
  }};
35
- if ({useExistingjQuery}) {{
35
+ if ({useExistingjQuery} && typeof(jQuery) === 'function') {{
36
36
  jQueryMP = jQuery;
37
37
  initMp();
38
38
  }} else {{
39
39
  load('{path}jquery.1.7.1.js?v={version}', initMp);
40
40
  }}
41
-
41
+
42
42
  }};
43
43
 
44
- var w = 0;
44
+ var w = 0;
45
45
  var f = false;
46
- var deferInit = function(){{
46
+ var deferInit = function(){{
47
47
  if (f) return;
48
48
  if (window.performance && window.performance.timing && window.performance.timing.loadEventEnd == 0 && w < 10000){{
49
49
  setTimeout(deferInit, 100);
@@ -59,4 +59,4 @@
59
59
  var o = window.onload;
60
60
  window.onload = function(){{if(o)o; deferInit()}};
61
61
  }})();
62
- </script>
62
+ </script>
@@ -6,11 +6,24 @@ module Rack
6
6
  # This class holds the client timings
7
7
  class ClientTimerStruct < TimerStruct
8
8
 
9
+ def self.init_instrumentation
10
+ "<script type=\"text/javascript\">mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()</script>"
11
+ end
12
+
13
+ def self.instrument(name,orig)
14
+ probe = "<script>mPt.probe('#{name}')</script>"
15
+ wrapped = probe
16
+ wrapped << orig
17
+ wrapped << probe
18
+ wrapped
19
+ end
20
+
21
+
9
22
  def initialize(env={})
10
23
  super
11
24
  end
12
25
 
13
- def init_from_form_data(env, page_struct)
26
+ def self.init_from_form_data(env, page_struct)
14
27
  timings = []
15
28
  clientTimes, clientPerf, baseTime = nil
16
29
  form = env['rack.request.form_hash']
@@ -21,6 +34,26 @@ module Rack
21
34
  baseTime = clientTimes['navigationStart'].to_i if clientTimes
22
35
  return unless clientTimes && baseTime
23
36
 
37
+ probes = form['clientProbes']
38
+ translated = {}
39
+ if probes && probes != "null"
40
+ probes.each do |id, val|
41
+ name = val["n"]
42
+ translated[name] ||= {}
43
+ if translated[name][:start]
44
+ translated[name][:finish] = val["d"]
45
+ else
46
+ translated[name][:start] = val["d"]
47
+ end
48
+ end
49
+ end
50
+
51
+ translated.each do |name, data|
52
+ h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
53
+ h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
54
+ timings.push(h)
55
+ end
56
+
24
57
  clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
25
58
  start = clientTimes[k].to_i - baseTime
26
59
  finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
@@ -34,8 +67,10 @@ module Rack
34
67
  timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
35
68
  end
36
69
 
37
- self['RedirectCount'] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirectCount']
38
- self['Timings'] = timings
70
+ rval = self.new
71
+ rval['RedirectCount'] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirectCount']
72
+ rval['Timings'] = timings
73
+ rval
39
74
  end
40
75
  end
41
76
 
@@ -14,7 +14,7 @@ module Rack
14
14
 
15
15
  attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
16
16
  :backtrace_remove, :backtrace_filter, :skip_schema_queries,
17
- :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode
17
+ :storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode, :use_existing_jquery
18
18
 
19
19
  def self.default
20
20
  new.instance_eval {
@@ -30,6 +30,7 @@ module Rack
30
30
  @storage = MiniProfiler::MemoryStore
31
31
  @user_provider = Proc.new{|env| Rack::Request.new(env).ip}
32
32
  @authorization_mode = :allow_all
33
+ @use_existing_jquery = false
33
34
  self
34
35
  }
35
36
  end
@@ -0,0 +1,10 @@
1
+ class Rack::MiniProfiler::Context
2
+ attr_accessor :inject_js,:current_timer,:page_struct,:skip_backtrace,:full_backtrace,:discard, :mpt_init
3
+
4
+ def initialize(opts = {})
5
+ opts.each do |k,v|
6
+ self.instance_variable_set('@' + k, v)
7
+ end
8
+ end
9
+
10
+ end
@@ -16,7 +16,7 @@ module Rack
16
16
  "Level" => 0,
17
17
  "User" => "unknown user",
18
18
  "HasUserViewed" => false,
19
- "ClientTimings" => ClientTimerStruct.new,
19
+ "ClientTimings" => nil,
20
20
  "DurationMilliseconds" => 0,
21
21
  "HasTrivialTimings" => true,
22
22
  "HasAllTrivialTimigs" => false,
@@ -35,15 +35,19 @@ module Rack
35
35
  def duration_ms
36
36
  @attributes['Root']['DurationMilliseconds']
37
37
  end
38
+
39
+ def root
40
+ @attributes['Root']
41
+ end
38
42
 
39
43
  def to_json(*a)
40
44
  attribs = @attributes.merge(
41
45
  "Started" => '/Date(%d)/' % @attributes['Started'],
42
46
  "DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
43
47
  )
44
- ::JSON.generate(attribs, a[0])
48
+ ::JSON.generate(attribs, :max_nesting => 100)
45
49
  end
46
50
  end
47
51
 
48
52
  end
49
- end
53
+ end
@@ -6,54 +6,103 @@ require 'mini_profiler/page_timer_struct'
6
6
  require 'mini_profiler/sql_timer_struct'
7
7
  require 'mini_profiler/client_timer_struct'
8
8
  require 'mini_profiler/request_timer_struct'
9
- require 'mini_profiler/body_add_proxy'
10
9
  require 'mini_profiler/storage/abstract_store'
11
10
  require 'mini_profiler/storage/memory_store'
12
11
  require 'mini_profiler/storage/redis_store'
13
12
  require 'mini_profiler/storage/file_store'
14
13
  require 'mini_profiler/config'
14
+ require 'mini_profiler/profiling_methods'
15
+ require 'mini_profiler/context'
15
16
 
16
17
  module Rack
17
18
 
18
19
  class MiniProfiler
19
20
 
20
- VERSION = 'rZlycOOTnzxZvxTmFuOEV0dSmu4P5m5bLrCtwJHVXPA='.freeze
21
- @@instance = nil
21
+ VERSION = '104'.freeze
22
22
 
23
- def self.instance
24
- @@instance
25
- end
23
+ class << self
24
+
25
+ include Rack::MiniProfiler::ProfilingMethods
26
26
 
27
- def self.generate_id
28
- rand(36**20).to_s(36)
29
- end
27
+ def generate_id
28
+ rand(36**20).to_s(36)
29
+ end
30
30
 
31
- def self.reset_config
32
- @config = Config.default
33
- end
31
+ def reset_config
32
+ @config = Config.default
33
+ end
34
34
 
35
- # So we can change the configuration if we want
36
- def self.config
37
- @config ||= Config.default
38
- end
35
+ # So we can change the configuration if we want
36
+ def config
37
+ @config ||= Config.default
38
+ end
39
+
40
+ def share_template
41
+ return @share_template unless @share_template.nil?
42
+ @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
43
+ end
44
+
45
+ def current
46
+ Thread.current[:mini_profiler_private]
47
+ end
48
+
49
+ def current=(c)
50
+ # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
51
+ Thread.current[:mini_profiler_private]= c
52
+ end
53
+
54
+ # discard existing results, don't track this request
55
+ def discard_results
56
+ self.current.discard = true if current
57
+ end
58
+
59
+ # user has the mini profiler cookie, only used when config.authorization_mode == :whitelist
60
+ def has_profiling_cookie?(env)
61
+ env['HTTP_COOKIE'] && env['HTTP_COOKIE'].include?("__profilin=stylin")
62
+ end
63
+
64
+ # remove the mini profiler cookie, only used when config.authorization_mode == :whitelist
65
+ def remove_profiling_cookie(headers)
66
+ Rack::Utils.delete_cookie_header!(headers, '__profilin')
67
+ end
39
68
 
40
- def self.share_template
41
- return @share_template unless @share_template.nil?
42
- @share_template = ::File.read(::File.expand_path("../html/share.html", ::File.dirname(__FILE__)))
69
+ def set_profiling_cookie(headers)
70
+ Rack::Utils.set_cookie_header!(headers, '__profilin', 'stylin')
71
+ end
72
+
73
+ def create_current(env={}, options={})
74
+ # profiling the request
75
+ self.current = Context.new
76
+ self.current.inject_js = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
77
+ self.current.page_struct = PageTimerStruct.new(env)
78
+ self.current.current_timer = current.page_struct['Root']
79
+ end
80
+
81
+ def authorize_request
82
+ Thread.current[:mp_authorized] = true
83
+ end
84
+
85
+ def deauthorize_request
86
+ Thread.current[:mp_authorized] = nil
87
+ end
88
+
89
+ def request_authorized?
90
+ Thread.current[:mp_authorized]
91
+ end
43
92
  end
44
93
 
45
94
  #
46
95
  # options:
47
96
  # :auto_inject - should script be automatically injected on every html page (not xhr)
48
97
  def initialize(app, config = nil)
49
- @@instance = self
50
98
  MiniProfiler.config.merge!(config)
51
99
  @config = MiniProfiler.config
52
100
  @app = app
53
101
  @config.base_url_path << "/" unless @config.base_url_path.end_with? "/"
54
102
  unless @config.storage_instance
55
- @storage = @config.storage_instance = @config.storage.new(@config.storage_options)
103
+ @config.storage_instance = @config.storage.new(@config.storage_options)
56
104
  end
105
+ @storage = @config.storage_instance
57
106
  end
58
107
 
59
108
  def user(env)
@@ -69,7 +118,7 @@ module Rack
69
118
  return [404, {}, ["Request not found: #{request['id']} - user #{user(env)}"]]
70
119
  end
71
120
  unless page_struct['HasUserViewed']
72
- page_struct['ClientTimings'].init_from_form_data(env, page_struct)
121
+ page_struct['ClientTimings'] = ClientTimerStruct.init_from_form_data(env, page_struct)
73
122
  page_struct['HasUserViewed'] = true
74
123
  @storage.save(page_struct)
75
124
  @storage.set_viewed(user(env), id)
@@ -102,18 +151,19 @@ module Rack
102
151
  return [404, {}, ["Not found"]] unless ::File.exists? full_path
103
152
  f = Rack::File.new nil
104
153
  f.path = full_path
105
- f.cache_control = "max-age:86400"
106
- f.serving env
107
- end
108
154
 
109
- def self.current
110
- Thread.current['profiler.mini.private']
111
- end
155
+ begin
156
+ f.cache_control = "max-age:86400"
157
+ f.serving env
158
+ rescue
159
+ # old versions of rack have a different api
160
+ status, headers, body = f.serving
161
+ headers.merge! 'Cache-Control' => "max-age:86400"
162
+ [status, headers, body]
163
+ end
164
+
165
+ end
112
166
 
113
- def self.current=(c)
114
- # we use TLS cause we need access to this from sql blocks and code blocks that have no access to env
115
- Thread.current['profiler.mini.private'] = c
116
- end
117
167
 
118
168
  def current
119
169
  MiniProfiler.current
@@ -123,48 +173,11 @@ module Rack
123
173
  MiniProfiler.current=c
124
174
  end
125
175
 
126
- # discard existing results, don't track this request
127
- def self.discard_results
128
- current[:discard] = true if current
129
- end
130
-
131
- # user has the mini profiler cookie, only used when config.authorization_mode == :whitelist
132
- def self.has_profiling_cookie?(env)
133
- env['HTTP_COOKIE'] && env['HTTP_COOKIE'].include?("__profilin=stylin")
134
- end
135
-
136
- # remove the mini profiler cookie, only used when config.authorization_mode == :whitelist
137
- def self.remove_profiling_cookie(headers)
138
- Rack::Utils.delete_cookie_header!(headers, '__profilin')
139
- end
140
-
141
- def self.set_profiling_cookie(headers)
142
- Rack::Utils.set_cookie_header!(headers, '__profilin', 'stylin')
143
- end
144
176
 
145
177
  def config
146
178
  @config
147
179
  end
148
180
 
149
- def self.create_current(env={}, options={})
150
- # profiling the request
151
- self.current = {}
152
- self.current['inject_js'] = config.auto_inject && (!env['HTTP_X_REQUESTED_WITH'].eql? 'XMLHttpRequest')
153
- self.current['page_struct'] = PageTimerStruct.new(env)
154
- self.current['current_timer'] = current['page_struct']['Root']
155
- end
156
-
157
- def self.authorize_request
158
- Thread.current[:mp_authorized] = true
159
- end
160
-
161
- def self.deauthorize_request
162
- Thread.current[:mp_authorized] = nil
163
- end
164
-
165
- def self.request_authorized?
166
- Thread.current[:mp_authorized]
167
- end
168
181
 
169
182
  def call(env)
170
183
  status = headers = body = nil
@@ -174,12 +187,12 @@ module Rack
174
187
  (@config.skip_paths && @config.skip_paths.any?{ |p| path[0,p.length] == p}) ||
175
188
  env["QUERY_STRING"] =~ /pp=skip/
176
189
 
177
- has_profiling_cookie = self.class.has_profiling_cookie?(env)
190
+ has_profiling_cookie = MiniProfiler.has_profiling_cookie?(env)
178
191
 
179
192
  if skip_it || (@config.authorization_mode == :whitelist && !has_profiling_cookie)
180
193
  status,headers,body = @app.call(env)
181
194
  if !skip_it && @config.authorization_mode == :whitelist && !has_profiling_cookie && MiniProfiler.request_authorized?
182
- self.class.set_profiling_cookie(headers)
195
+ MiniProfiler.set_profiling_cookie(headers)
183
196
  end
184
197
  return [status,headers,body]
185
198
  end
@@ -188,34 +201,38 @@ module Rack
188
201
  return serve_html(env) if env['PATH_INFO'].start_with? @config.base_url_path
189
202
 
190
203
  MiniProfiler.create_current(env, @config)
191
-
192
204
  MiniProfiler.deauthorize_request if @config.authorization_mode == :whitelist
193
205
  if env["QUERY_STRING"] =~ /pp=no-backtrace/
194
- current['skip-backtrace'] = true
206
+ current.skip_backtrace = true
195
207
  elsif env["QUERY_STRING"] =~ /pp=full-backtrace/
196
- current['full-backtrace'] = true
208
+ current.full_backtrace = true
197
209
  end
198
210
 
199
211
  done_sampling = false
200
212
  quit_sampler = false
201
213
  backtraces = nil
214
+ missing_stacktrace = false
202
215
  if env["QUERY_STRING"] =~ /pp=sample/
203
216
  backtraces = []
204
217
  t = Thread.current
205
218
  Thread.new {
206
- require 'stacktrace'
207
- if !t.respond_to? :stacktrace
208
- quit_sampler = true
209
- return
210
- end
211
- i = 10000 # for sanity never grab more than 10k samples
212
- while i > 0
213
- break if done_sampling
214
- i -= 1
215
- backtraces << t.stacktrace
216
- sleep 0.001
219
+ begin
220
+ require 'stacktrace' rescue nil
221
+ if !t.respond_to? :stacktrace
222
+ missing_stacktrace = true
223
+ quit_sampler = true
224
+ return
225
+ end
226
+ i = 10000 # for sanity never grab more than 10k samples
227
+ while i > 0
228
+ break if done_sampling
229
+ i -= 1
230
+ backtraces << t.stacktrace
231
+ sleep 0.001
232
+ end
233
+ ensure
234
+ quit_sampler = true
217
235
  end
218
- quit_sampler = true
219
236
  }
220
237
  end
221
238
 
@@ -230,14 +247,14 @@ module Rack
230
247
  end
231
248
  end
232
249
 
233
- skip_it = current[:discard]
250
+ skip_it = current.discard
234
251
  if (config.authorization_mode == :whitelist && !MiniProfiler.request_authorized?)
235
252
  MiniProfiler.remove_profiling_cookie(headers)
236
253
  skip_it = true
237
254
  end
238
255
 
239
256
  return [status,headers,body] if skip_it
240
-
257
+
241
258
  # we must do this here, otherwise current[:discard] is not being properly treated
242
259
  if env["QUERY_STRING"] =~ /pp=env/
243
260
  body.close if body.respond_to? :close
@@ -249,11 +266,12 @@ module Rack
249
266
  return help
250
267
  end
251
268
 
252
- page_struct = current['page_struct']
269
+ page_struct = current.page_struct
253
270
  page_struct['Root'].record_time((Time.now - start) * 1000)
254
271
 
255
272
  if backtraces
256
273
  body.close if body.respond_to? :close
274
+ return help(:stacktrace) if missing_stacktrace
257
275
  return analyze(backtraces, page_struct)
258
276
  end
259
277
 
@@ -265,30 +283,44 @@ module Rack
265
283
  # inject headers, script
266
284
  if status == 200
267
285
 
286
+ # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
287
+ # Rack::ETag has already inserted some nonesense in the chain
288
+ headers.delete('ETag')
289
+ headers.delete('Date')
290
+ headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
291
+
268
292
  # inject header
269
293
  if headers.is_a? Hash
270
294
  headers['X-MiniProfiler-Ids'] = ids_json(env)
271
295
  end
272
296
 
273
297
  # inject script
274
- if current['inject_js'] \
298
+ if current.inject_js \
275
299
  && headers.has_key?('Content-Type') \
276
300
  && !headers['Content-Type'].match(/text\/html/).nil? then
277
- body = MiniProfiler::BodyAddProxy.new(body, self.get_profile_script(env))
301
+
302
+ response = Rack::Response.new([], status, headers)
303
+ script = self.get_profile_script(env)
304
+ if String === body
305
+ response.write inject(body,script)
306
+ else
307
+ body.each { |fragment| response.write inject(fragment, script) }
308
+ end
309
+ body.close if body.respond_to? :close
310
+ return response.finish
278
311
  end
279
312
  end
280
313
 
281
- # mini profiler is meddling with stuff, we can not cache cause we will get incorrect data
282
- # Rack::ETag has already inserted some nonesense in the chain
283
- headers.delete('ETag')
284
- headers.delete('Date')
285
- headers['Cache-Control'] = 'must-revalidate, private, max-age=0'
286
314
  [status, headers, body]
287
315
  ensure
288
316
  # Make sure this always happens
289
317
  current = nil
290
318
  end
291
319
 
320
+ def inject(fragment, script)
321
+ fragment.sub(/<\/body>/i, script + "</body>")
322
+ end
323
+
292
324
  def dump_env(env)
293
325
  headers = {'Content-Type' => 'text/plain'}
294
326
  body = ""
@@ -298,7 +330,7 @@ module Rack
298
330
  [200, headers, [body]]
299
331
  end
300
332
 
301
- def help
333
+ def help(category = nil)
302
334
  headers = {'Content-Type' => 'text/plain'}
303
335
  body = "Append the following to your query string:
304
336
 
@@ -309,24 +341,48 @@ module Rack
309
341
  pp=full-backtrace : enable full backtrace for SQL executed
310
342
  pp=sample : sample stack traces and return a report isolating heavy usage (requires the stacktrace gem)
311
343
  "
312
- #headers['Content-Length'] = body.length
344
+ if (category == :stacktrace)
345
+ body = "pp=stacktrace requires the stacktrace gem - add gem 'stacktrace' to your Gemfile"
346
+ end
347
+
313
348
  [200, headers, [body]]
314
349
  end
315
350
 
316
351
  def analyze(traces, page_struct)
317
352
  headers = {'Content-Type' => 'text/plain'}
318
353
  body = "Collected: #{traces.count} stack traces. Duration(ms): #{page_struct.duration_ms}"
354
+
355
+ seen = {}
356
+ fulldump = ""
319
357
  traces.each do |trace|
320
- body << "\n\n"
358
+ fulldump << "\n\n"
359
+ distinct = {}
321
360
  trace.each do |frame|
322
- body << "#{frame.klass} #{frame.method}\n"
361
+ name = "#{frame.klass} #{frame.method}"
362
+ unless distinct[name]
363
+ distinct[name] = true
364
+ seen[name] ||= 0
365
+ seen[name] += 1
366
+ end
367
+ fulldump << name << "\n"
368
+ end
369
+ end
370
+
371
+ body << "\n\nStack Trace Analysis\n"
372
+ seen.to_a.sort{|x,y| y[1] <=> x[1]}.each do |name, count|
373
+ if count > traces.count / 10
374
+ body << "#{name} x #{count}\n"
323
375
  end
324
376
  end
377
+
378
+ body << "\n\n\nRaw traces \n"
379
+ body << fulldump
380
+
325
381
  [200, headers, [body]]
326
382
  end
327
383
 
328
384
  def ids_json(env)
329
- ids = [current['page_struct']["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])
385
+ ids = [current.page_struct["Id"]] + (@storage.get_unviewed_ids(user(env)) || [])
330
386
  ::JSON.generate(ids.uniq)
331
387
  end
332
388
 
@@ -345,9 +401,9 @@ module Rack
345
401
  showChildren = false
346
402
  maxTracesToShow = 10
347
403
  showControls = false
348
- currentId = current['page_struct']["Id"]
404
+ currentId = current.page_struct["Id"]
349
405
  authorized = true
350
- useExistingjQuery = false
406
+ useExistingjQuery = @config.use_existing_jquery
351
407
  # TODO : cache this snippet
352
408
  script = IO.read(::File.expand_path('../html/profile_handler.js', ::File.dirname(__FILE__)))
353
409
  # replace the variables
@@ -357,52 +413,13 @@ module Rack
357
413
  end
358
414
  # replace the '{{' and '}}''
359
415
  script.gsub!(/\{\{/, '{').gsub!(/\}\}/, '}')
360
- current['inject_js'] = false
416
+ current.inject_js = false
361
417
  script
362
418
  end
363
419
 
364
420
  # cancels automatic injection of profile script for the current page
365
421
  def cancel_auto_inject(env)
366
- current['inject_js'] = false
367
- end
368
-
369
- # perform a profiling step on given block
370
- def self.step(name)
371
- if current
372
- old_timer = current['current_timer']
373
- new_step = RequestTimerStruct.new(name, current['page_struct'])
374
- current['current_timer'] = new_step
375
- new_step['Name'] = name
376
- start = Time.now
377
- result = yield if block_given?
378
- new_step.record_time((Time.now - start)*1000)
379
- old_timer.add_child(new_step)
380
- current['current_timer'] = old_timer
381
- result
382
- else
383
- yield if block_given?
384
- end
385
- end
386
-
387
- def self.profile_method(klass, method, &blk)
388
- default_name = klass.to_s + " " + method.to_s
389
- with_profiling = (method.to_s + "_with_mini_profiler").intern
390
- without_profiling = (method.to_s + "_without_mini_profiler").intern
391
-
392
- klass.send :alias_method, without_profiling, method
393
- klass.send :define_method, with_profiling do |*args, &orig|
394
- name = default_name
395
- name = blk.bind(self).call(*args) if blk
396
- ::Rack::MiniProfiler.step name do
397
- self.send without_profiling, *args, &orig
398
- end
399
- end
400
- klass.send :alias_method, method, with_profiling
401
- end
402
-
403
- def record_sql(query, elapsed_ms)
404
- c = current
405
- c['current_timer'].add_sql(query, elapsed_ms, c['page_struct'], c['skip-backtrace'], c['full-backtrace']) if (c && c['current_timer'])
422
+ current.inject_js = false
406
423
  end
407
424
 
408
425
  end