rails_performance 0.0.1.11 → 0.0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -5
- data/app/assets/images/activity.svg +13 -0
- data/app/assets/images/close.svg +13 -0
- data/app/assets/images/export.svg +13 -0
- data/app/assets/images/import.svg +13 -0
- data/app/assets/images/menu.svg +16 -0
- data/app/controllers/rails_performance_controller.rb +19 -7
- data/app/helpers/rails_performance_helper.rb +13 -18
- data/app/views/javascripts/_javascripts.html.erb +3 -0
- data/app/views/javascripts/panel.js +9 -0
- data/app/views/javascripts/rails.js +565 -0
- data/app/views/javascripts/stupidtable.min.js +1 -0
- data/app/views/javascripts/table.js +3 -0
- data/app/views/layouts/rails_performance.html.erb +3 -0
- data/app/views/rails_performance/_panel.html.erb +1 -5
- data/app/views/rails_performance/_summary.html.erb +35 -0
- data/app/views/rails_performance/crashes.html.erb +10 -10
- data/app/views/rails_performance/recent.html.erb +10 -10
- data/app/views/rails_performance/requests.html.erb +11 -16
- data/app/views/rails_performance/summary.js.erb +10 -0
- data/app/views/shared/_header.html.erb +2 -3
- data/app/views/stylesheets/panel.css +8 -2
- data/app/views/stylesheets/style.css +9 -0
- data/config/routes.rb +2 -1
- data/lib/rails_performance/reports/breakdown_report.rb +1 -1
- data/lib/rails_performance/version.rb +1 -1
- metadata +12 -3
- data/app/views/rails_performance/breakdown.html.erb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ead4f058f985e1601b4e3eb4318867b40da0952c4ea30092afcd466e50f98f63
|
4
|
+
data.tar.gz: e5bbc0934281a451c292a62f63024778c2f2ea627f94b2f7a836080a27e2516c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2c6d72b1550a5adcf4fa73f5b6580241a06cd32d68e1b3ea5b7587c7ba86ad3070911802ea9abac5ced6c8c90f1f925f98b4e4395d41039364bd2e48f320552
|
7
|
+
data.tar.gz: 5399cda1f85d289862ef1a9c7349061e5d520044df2bfd0dbd376da866835a55b4b495271aed577a31f57cb7e35a1ec8b2b4bd6542c3973339614322bbc2d806
|
data/README.md
CHANGED
@@ -27,14 +27,13 @@ $ bundle
|
|
27
27
|
|
28
28
|
## TODO
|
29
29
|
|
30
|
-
- time/zone
|
31
|
-
- redis namespaces
|
32
|
-
- skip for tests ?
|
33
30
|
- documentation
|
34
|
-
-
|
31
|
+
- time/zone config?
|
32
|
+
- CI for tests
|
35
33
|
- better hint
|
36
34
|
- export to csv
|
37
|
-
-
|
35
|
+
- http basic auth
|
36
|
+
- better stats tooltip, do not show if nothing to show
|
38
37
|
|
39
38
|
## Contributing
|
40
39
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" ?><svg height="48" id="activity" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
|
2
|
+
.vi-primary {
|
3
|
+
fill: #FF6E6E;
|
4
|
+
}
|
5
|
+
|
6
|
+
.vi-primary, .vi-accent {
|
7
|
+
fill-rule: evenodd;
|
8
|
+
}
|
9
|
+
|
10
|
+
.vi-accent {
|
11
|
+
fill: #0C0058;
|
12
|
+
}
|
13
|
+
</style></defs><path class="vi-primary" d="M20,33a2,2,0,0,1-1.788-1.1l-3.6-7.183-2.047,2.551A2,2,0,0,1,11,28.013H7a1.994,1.994,0,1,1,0-3.988h3.039l3.4-4.237a2,2,0,0,1,3.351.354l2.7,5.386,2.588-9.03a2,2,0,0,1,3.75-.262l3.473,7.79H32a1.994,1.994,0,1,1,0,3.988H28a2,2,0,0,1-1.827-1.184L24.41,22.874l-2.487,8.678a2,2,0,0,1-1.737,1.437C20.123,33,20.061,33,20,33Z"/><path class="vi-accent" d="M37,32a6,6,0,1,1,6-6A6,6,0,0,1,37,32Zm0-8.717A2.72,2.72,0,1,0,39.728,26,2.724,2.724,0,0,0,37,23.283Z"/></svg>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" ?><svg height="48" id="close" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
|
2
|
+
.vi-primary {
|
3
|
+
fill: #FF6E6E;
|
4
|
+
}
|
5
|
+
|
6
|
+
.vi-primary, .vi-accent {
|
7
|
+
fill-rule: evenodd;
|
8
|
+
}
|
9
|
+
|
10
|
+
.vi-accent {
|
11
|
+
fill: #0C0058;
|
12
|
+
}
|
13
|
+
</style></defs><path class="vi-primary" d="M35,32l-3,3-7.5-7.5L17,35l-3-3,7.5-7.5L14,17l3-3,7.5,7.5L32,14l3,3-7.5,7.5Z"/><path class="vi-accent" d="M20,32l-3,3-3-3,3-3ZM35,17l-3,3-3-3,3-3ZM29,32l3,3,3-3-3-3ZM14,17l3,3,3-3-3-3Z"/></svg>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" ?><svg height="48" id="share" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
|
2
|
+
.vi-primary {
|
3
|
+
fill: #FF6E6E;
|
4
|
+
stroke: #fff;
|
5
|
+
stroke-linecap: round;
|
6
|
+
stroke-width: 0;
|
7
|
+
}
|
8
|
+
|
9
|
+
.vi-accent {
|
10
|
+
fill: #0C0058;
|
11
|
+
fill-rule: evenodd;
|
12
|
+
}
|
13
|
+
</style></defs><rect class="vi-primary" height="28" width="28" x="5" y="10"/><path class="vi-accent" d="M17,25H39l-3,3,2,2,6-6V23l-6-6-2,2,3,3H17v3Z"/></svg>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<?xml version="1.0" ?><svg height="48" id="import" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
|
2
|
+
.vi-primary {
|
3
|
+
fill: #FF6E6E;
|
4
|
+
stroke: #fff;
|
5
|
+
stroke-linecap: round;
|
6
|
+
stroke-width: 0;
|
7
|
+
}
|
8
|
+
|
9
|
+
.vi-accent {
|
10
|
+
fill: #0C0058;
|
11
|
+
fill-rule: evenodd;
|
12
|
+
}
|
13
|
+
</style></defs><rect class="vi-primary" height="28" width="28" x="15" y="10"/><path class="vi-accent" d="M4,25H27l-3,3,2,2,6-6V23l-6-6-2,2,3,3H4v3Z"/></svg>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<?xml version="1.0" ?><svg height="48" id="menu" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
|
2
|
+
.vi-primary {
|
3
|
+
fill: #FF6E6E;
|
4
|
+
}
|
5
|
+
|
6
|
+
.vi-primary, .vi-accent {
|
7
|
+
stroke: #fff;
|
8
|
+
stroke-linecap: round;
|
9
|
+
stroke-width: 0;
|
10
|
+
fill-rule: evenodd;
|
11
|
+
}
|
12
|
+
|
13
|
+
.vi-accent {
|
14
|
+
fill: #0C0058;
|
15
|
+
}
|
16
|
+
</style></defs><path class="vi-primary" d="M9,12H39v4H9V12ZM9,22H39v4H9V22ZM9,32H39v4H9V32Z"/><path class="vi-accent" d="M9,12h4v4H9V12ZM9,22h4v4H9V22ZM9,32h4v4H9V32ZM35,12h4v4H35V12Zm0,10h4v4H35V22Zm0,10h4v4H35V32Z"/></svg>
|
@@ -11,6 +11,25 @@ class RailsPerformanceController < ActionController::Base
|
|
11
11
|
@response_time_report_data = @response_time_report.data
|
12
12
|
end
|
13
13
|
|
14
|
+
def summary
|
15
|
+
@datasource = RP::DataSource.new(prepare_query)
|
16
|
+
db = @datasource.db
|
17
|
+
|
18
|
+
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
19
|
+
@throughput_report_data = @throughput_report.data
|
20
|
+
|
21
|
+
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
22
|
+
@response_time_report_data = @response_time_report.data
|
23
|
+
|
24
|
+
@report = RP::Reports::BreakdownReport.new(db, title: "Requests")
|
25
|
+
@data = @report.data
|
26
|
+
|
27
|
+
respond_to do |format|
|
28
|
+
format.js {}
|
29
|
+
format.any { render plain: "Doesn't open in new window" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
def crashes
|
15
34
|
@datasource = RP::DataSource.new(prepare_query({status_eq: 500}))
|
16
35
|
db = @datasource.db
|
@@ -25,13 +44,6 @@ class RailsPerformanceController < ActionController::Base
|
|
25
44
|
@data = @report.data
|
26
45
|
end
|
27
46
|
|
28
|
-
def breakdown
|
29
|
-
@datasource = RP::DataSource.new(prepare_query)
|
30
|
-
db = @datasource.db
|
31
|
-
@report = RP::Reports::BreakdownReport.new(db, title: "Breakdown Report")
|
32
|
-
@data = @report.data
|
33
|
-
end
|
34
|
-
|
35
47
|
def recent
|
36
48
|
@datasource = RP::DataSource.new(prepare_query)
|
37
49
|
db = @datasource.db
|
@@ -8,38 +8,33 @@ module RailsPerformanceHelper
|
|
8
8
|
|
9
9
|
def ms(value)
|
10
10
|
result = round_it(value)
|
11
|
-
result ? "#{result} ms" : '-'
|
11
|
+
result && result != 0 ? "#{result} ms" : '-'
|
12
12
|
end
|
13
13
|
|
14
14
|
def short_path(path, length: 60)
|
15
|
-
|
15
|
+
content_tag :span, title: path do
|
16
16
|
truncate(path, length: length)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
def link_to_path(e)
|
21
21
|
if e[:method] == 'GET'
|
22
|
-
link_to
|
22
|
+
link_to(short_path(e[:path]), e[:path], target: '_blank')
|
23
23
|
else
|
24
24
|
short_path(e[:path])
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
else
|
39
|
-
{}
|
40
|
-
end
|
41
|
-
|
42
|
-
link_to title, rails_performance_path(options), target: '_blank'
|
28
|
+
def report_name(h)
|
29
|
+
h.except(:on).collect do |k, v|
|
30
|
+
%Q{
|
31
|
+
<div class="control">
|
32
|
+
<span class="tags has-addons">
|
33
|
+
<span class="tag">#{k}</span>
|
34
|
+
<span class="tag is-success">#{v}</span>
|
35
|
+
</span>
|
36
|
+
</div>}
|
37
|
+
end.join.html_safe
|
43
38
|
end
|
44
39
|
|
45
40
|
def status_tag(status)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
<%= insert_js_file 'jquery-3.4.1.min.js' %>
|
2
|
+
<%= insert_js_file 'rails.js' %>
|
3
|
+
<%= insert_js_file 'stupidtable.min.js' %>
|
2
4
|
|
3
5
|
<%= javascript_include_tag 'https://code.highcharts.com/highcharts.js' %>
|
4
6
|
<%= javascript_include_tag 'https://code.highcharts.com/modules/data.js' %>
|
@@ -8,3 +10,4 @@
|
|
8
10
|
|
9
11
|
<%= insert_js_file 'app.js' %>
|
10
12
|
<%= insert_js_file 'panel.js' %>
|
13
|
+
<%= insert_js_file 'table.js' %>
|
@@ -8,10 +8,19 @@ function showPanel() {
|
|
8
8
|
$(".panel-overlay").show();
|
9
9
|
$('.cd-panel').addClass('cd-panel--is-visible');
|
10
10
|
$('body').addClass('panel-visible');
|
11
|
+
|
12
|
+
$('.cd-panel__content table').stupidtable();
|
11
13
|
}
|
12
14
|
|
15
|
+
|
13
16
|
$(function() {
|
14
17
|
|
18
|
+
var panel = {};
|
19
|
+
window.panel = panel;
|
20
|
+
window.panel.header = $(".panel-heading span");
|
21
|
+
window.panel.content = $(".cd-panel__content");
|
22
|
+
window.panel.close = '<a class="panel-close" href="#" onclick="javascript: hidePanel(); return false;">×</a>'
|
23
|
+
|
15
24
|
$(".toggle-panel").on('click', function(e) {
|
16
25
|
showPanel();
|
17
26
|
});
|
@@ -0,0 +1,565 @@
|
|
1
|
+
/* jshint node: true */
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Unobtrusive scripting adapter for jQuery
|
5
|
+
* https://github.com/rails/jquery-ujs
|
6
|
+
*
|
7
|
+
* Requires jQuery 1.8.0 or later.
|
8
|
+
*
|
9
|
+
* Released under the MIT license
|
10
|
+
*
|
11
|
+
*/
|
12
|
+
|
13
|
+
(function() {
|
14
|
+
'use strict';
|
15
|
+
|
16
|
+
var jqueryUjsInit = function($, undefined) {
|
17
|
+
|
18
|
+
// Cut down on the number of issues from people inadvertently including jquery_ujs twice
|
19
|
+
// by detecting and raising an error when it happens.
|
20
|
+
if ( $.rails !== undefined ) {
|
21
|
+
$.error('jquery-ujs has already been loaded!');
|
22
|
+
}
|
23
|
+
|
24
|
+
// Shorthand to make it a little easier to call public rails functions from within rails.js
|
25
|
+
var rails;
|
26
|
+
var $document = $(document);
|
27
|
+
|
28
|
+
$.rails = rails = {
|
29
|
+
// Link elements bound by jquery-ujs
|
30
|
+
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]',
|
31
|
+
|
32
|
+
// Button elements bound by jquery-ujs
|
33
|
+
buttonClickSelector: 'button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)',
|
34
|
+
|
35
|
+
// Select elements bound by jquery-ujs
|
36
|
+
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
|
37
|
+
|
38
|
+
// Form elements bound by jquery-ujs
|
39
|
+
formSubmitSelector: 'form',
|
40
|
+
|
41
|
+
// Form input elements bound by jquery-ujs
|
42
|
+
formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])',
|
43
|
+
|
44
|
+
// Form input elements disabled during form submission
|
45
|
+
disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled',
|
46
|
+
|
47
|
+
// Form input elements re-enabled after form submission
|
48
|
+
enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled',
|
49
|
+
|
50
|
+
// Form required input elements
|
51
|
+
requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])',
|
52
|
+
|
53
|
+
// Form file input elements
|
54
|
+
fileInputSelector: 'input[name][type=file]:not([disabled])',
|
55
|
+
|
56
|
+
// Link onClick disable selector with possible reenable after remote submission
|
57
|
+
linkDisableSelector: 'a[data-disable-with], a[data-disable]',
|
58
|
+
|
59
|
+
// Button onClick disable selector with possible reenable after remote submission
|
60
|
+
buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]',
|
61
|
+
|
62
|
+
// Up-to-date Cross-Site Request Forgery token
|
63
|
+
csrfToken: function() {
|
64
|
+
return $('meta[name=csrf-token]').attr('content');
|
65
|
+
},
|
66
|
+
|
67
|
+
// URL param that must contain the CSRF token
|
68
|
+
csrfParam: function() {
|
69
|
+
return $('meta[name=csrf-param]').attr('content');
|
70
|
+
},
|
71
|
+
|
72
|
+
// Make sure that every Ajax request sends the CSRF token
|
73
|
+
CSRFProtection: function(xhr) {
|
74
|
+
var token = rails.csrfToken();
|
75
|
+
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
76
|
+
},
|
77
|
+
|
78
|
+
// Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
|
79
|
+
refreshCSRFTokens: function(){
|
80
|
+
$('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken());
|
81
|
+
},
|
82
|
+
|
83
|
+
// Triggers an event on an element and returns false if the event result is false
|
84
|
+
fire: function(obj, name, data) {
|
85
|
+
var event = $.Event(name);
|
86
|
+
obj.trigger(event, data);
|
87
|
+
return event.result !== false;
|
88
|
+
},
|
89
|
+
|
90
|
+
// Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
|
91
|
+
confirm: function(message) {
|
92
|
+
return confirm(message);
|
93
|
+
},
|
94
|
+
|
95
|
+
// Default ajax function, may be overridden with custom function in $.rails.ajax
|
96
|
+
ajax: function(options) {
|
97
|
+
return $.ajax(options);
|
98
|
+
},
|
99
|
+
|
100
|
+
// Default way to get an element's href. May be overridden at $.rails.href.
|
101
|
+
href: function(element) {
|
102
|
+
return element[0].href;
|
103
|
+
},
|
104
|
+
|
105
|
+
// Checks "data-remote" if true to handle the request through a XHR request.
|
106
|
+
isRemote: function(element) {
|
107
|
+
return element.data('remote') !== undefined && element.data('remote') !== false;
|
108
|
+
},
|
109
|
+
|
110
|
+
// Submits "remote" forms and links with ajax
|
111
|
+
handleRemote: function(element) {
|
112
|
+
var method, url, data, withCredentials, dataType, options;
|
113
|
+
|
114
|
+
if (rails.fire(element, 'ajax:before')) {
|
115
|
+
withCredentials = element.data('with-credentials') || null;
|
116
|
+
dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
|
117
|
+
|
118
|
+
if (element.is('form')) {
|
119
|
+
method = element.data('ujs:submit-button-formmethod') || element.attr('method');
|
120
|
+
url = element.data('ujs:submit-button-formaction') || element.attr('action');
|
121
|
+
data = $(element[0]).serializeArray();
|
122
|
+
// memoized value from clicked submit button
|
123
|
+
var button = element.data('ujs:submit-button');
|
124
|
+
if (button) {
|
125
|
+
data.push(button);
|
126
|
+
element.data('ujs:submit-button', null);
|
127
|
+
}
|
128
|
+
element.data('ujs:submit-button-formmethod', null);
|
129
|
+
element.data('ujs:submit-button-formaction', null);
|
130
|
+
} else if (element.is(rails.inputChangeSelector)) {
|
131
|
+
method = element.data('method');
|
132
|
+
url = element.data('url');
|
133
|
+
data = element.serialize();
|
134
|
+
if (element.data('params')) data = data + '&' + element.data('params');
|
135
|
+
} else if (element.is(rails.buttonClickSelector)) {
|
136
|
+
method = element.data('method') || 'get';
|
137
|
+
url = element.data('url');
|
138
|
+
data = element.serialize();
|
139
|
+
if (element.data('params')) data = data + '&' + element.data('params');
|
140
|
+
} else {
|
141
|
+
method = element.data('method');
|
142
|
+
url = rails.href(element);
|
143
|
+
data = element.data('params') || null;
|
144
|
+
}
|
145
|
+
|
146
|
+
options = {
|
147
|
+
type: method || 'GET', data: data, dataType: dataType,
|
148
|
+
// stopping the "ajax:beforeSend" event will cancel the ajax request
|
149
|
+
beforeSend: function(xhr, settings) {
|
150
|
+
if (settings.dataType === undefined) {
|
151
|
+
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
|
152
|
+
}
|
153
|
+
if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
|
154
|
+
element.trigger('ajax:send', xhr);
|
155
|
+
} else {
|
156
|
+
return false;
|
157
|
+
}
|
158
|
+
},
|
159
|
+
success: function(data, status, xhr) {
|
160
|
+
element.trigger('ajax:success', [data, status, xhr]);
|
161
|
+
},
|
162
|
+
complete: function(xhr, status) {
|
163
|
+
element.trigger('ajax:complete', [xhr, status]);
|
164
|
+
},
|
165
|
+
error: function(xhr, status, error) {
|
166
|
+
element.trigger('ajax:error', [xhr, status, error]);
|
167
|
+
},
|
168
|
+
crossDomain: rails.isCrossDomain(url)
|
169
|
+
};
|
170
|
+
|
171
|
+
// There is no withCredentials for IE6-8 when
|
172
|
+
// "Enable native XMLHTTP support" is disabled
|
173
|
+
if (withCredentials) {
|
174
|
+
options.xhrFields = {
|
175
|
+
withCredentials: withCredentials
|
176
|
+
};
|
177
|
+
}
|
178
|
+
|
179
|
+
// Only pass url to `ajax` options if not blank
|
180
|
+
if (url) { options.url = url; }
|
181
|
+
|
182
|
+
return rails.ajax(options);
|
183
|
+
} else {
|
184
|
+
return false;
|
185
|
+
}
|
186
|
+
},
|
187
|
+
|
188
|
+
// Determines if the request is a cross domain request.
|
189
|
+
isCrossDomain: function(url) {
|
190
|
+
var originAnchor = document.createElement('a');
|
191
|
+
originAnchor.href = location.href;
|
192
|
+
var urlAnchor = document.createElement('a');
|
193
|
+
|
194
|
+
try {
|
195
|
+
urlAnchor.href = url;
|
196
|
+
// This is a workaround to a IE bug.
|
197
|
+
urlAnchor.href = urlAnchor.href;
|
198
|
+
|
199
|
+
// If URL protocol is false or is a string containing a single colon
|
200
|
+
// *and* host are false, assume it is not a cross-domain request
|
201
|
+
// (should only be the case for IE7 and IE compatibility mode).
|
202
|
+
// Otherwise, evaluate protocol and host of the URL against the origin
|
203
|
+
// protocol and host.
|
204
|
+
return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) ||
|
205
|
+
(originAnchor.protocol + '//' + originAnchor.host ===
|
206
|
+
urlAnchor.protocol + '//' + urlAnchor.host));
|
207
|
+
} catch (e) {
|
208
|
+
// If there is an error parsing the URL, assume it is crossDomain.
|
209
|
+
return true;
|
210
|
+
}
|
211
|
+
},
|
212
|
+
|
213
|
+
// Handles "data-method" on links such as:
|
214
|
+
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
215
|
+
handleMethod: function(link) {
|
216
|
+
var href = rails.href(link),
|
217
|
+
method = link.data('method'),
|
218
|
+
target = link.attr('target'),
|
219
|
+
csrfToken = rails.csrfToken(),
|
220
|
+
csrfParam = rails.csrfParam(),
|
221
|
+
form = $('<form method="post" action="' + href + '"></form>'),
|
222
|
+
metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
|
223
|
+
|
224
|
+
if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
|
225
|
+
metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
|
226
|
+
}
|
227
|
+
|
228
|
+
if (target) { form.attr('target', target); }
|
229
|
+
|
230
|
+
form.hide().append(metadataInput).appendTo('body');
|
231
|
+
form.submit();
|
232
|
+
},
|
233
|
+
|
234
|
+
// Helper function that returns form elements that match the specified CSS selector
|
235
|
+
// If form is actually a "form" element this will return associated elements outside the from that have
|
236
|
+
// the html form attribute set
|
237
|
+
formElements: function(form, selector) {
|
238
|
+
return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector);
|
239
|
+
},
|
240
|
+
|
241
|
+
/* Disables form elements:
|
242
|
+
- Caches element value in 'ujs:enable-with' data store
|
243
|
+
- Replaces element text with value of 'data-disable-with' attribute
|
244
|
+
- Sets disabled property to true
|
245
|
+
*/
|
246
|
+
disableFormElements: function(form) {
|
247
|
+
rails.formElements(form, rails.disableSelector).each(function() {
|
248
|
+
rails.disableFormElement($(this));
|
249
|
+
});
|
250
|
+
},
|
251
|
+
|
252
|
+
disableFormElement: function(element) {
|
253
|
+
var method, replacement;
|
254
|
+
|
255
|
+
method = element.is('button') ? 'html' : 'val';
|
256
|
+
replacement = element.data('disable-with');
|
257
|
+
|
258
|
+
if (replacement !== undefined) {
|
259
|
+
element.data('ujs:enable-with', element[method]());
|
260
|
+
element[method](replacement);
|
261
|
+
}
|
262
|
+
|
263
|
+
element.prop('disabled', true);
|
264
|
+
element.data('ujs:disabled', true);
|
265
|
+
},
|
266
|
+
|
267
|
+
/* Re-enables disabled form elements:
|
268
|
+
- Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
269
|
+
- Sets disabled property to false
|
270
|
+
*/
|
271
|
+
enableFormElements: function(form) {
|
272
|
+
rails.formElements(form, rails.enableSelector).each(function() {
|
273
|
+
rails.enableFormElement($(this));
|
274
|
+
});
|
275
|
+
},
|
276
|
+
|
277
|
+
enableFormElement: function(element) {
|
278
|
+
var method = element.is('button') ? 'html' : 'val';
|
279
|
+
if (element.data('ujs:enable-with') !== undefined) {
|
280
|
+
element[method](element.data('ujs:enable-with'));
|
281
|
+
element.removeData('ujs:enable-with'); // clean up cache
|
282
|
+
}
|
283
|
+
element.prop('disabled', false);
|
284
|
+
element.removeData('ujs:disabled');
|
285
|
+
},
|
286
|
+
|
287
|
+
/* For 'data-confirm' attribute:
|
288
|
+
- Fires `confirm` event
|
289
|
+
- Shows the confirmation dialog
|
290
|
+
- Fires the `confirm:complete` event
|
291
|
+
|
292
|
+
Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
|
293
|
+
Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
|
294
|
+
Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
|
295
|
+
return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
|
296
|
+
*/
|
297
|
+
allowAction: function(element) {
|
298
|
+
var message = element.data('confirm'),
|
299
|
+
answer = false, callback;
|
300
|
+
if (!message) { return true; }
|
301
|
+
|
302
|
+
if (rails.fire(element, 'confirm')) {
|
303
|
+
try {
|
304
|
+
answer = rails.confirm(message);
|
305
|
+
} catch (e) {
|
306
|
+
(console.error || console.log).call(console, e.stack || e);
|
307
|
+
}
|
308
|
+
callback = rails.fire(element, 'confirm:complete', [answer]);
|
309
|
+
}
|
310
|
+
return answer && callback;
|
311
|
+
},
|
312
|
+
|
313
|
+
// Helper function which checks for blank inputs in a form that match the specified CSS selector
|
314
|
+
blankInputs: function(form, specifiedSelector, nonBlank) {
|
315
|
+
var foundInputs = $(),
|
316
|
+
input,
|
317
|
+
valueToCheck,
|
318
|
+
radiosForNameWithNoneSelected,
|
319
|
+
radioName,
|
320
|
+
selector = specifiedSelector || 'input,textarea',
|
321
|
+
requiredInputs = form.find(selector),
|
322
|
+
checkedRadioButtonNames = {};
|
323
|
+
|
324
|
+
requiredInputs.each(function() {
|
325
|
+
input = $(this);
|
326
|
+
if (input.is('input[type=radio]')) {
|
327
|
+
|
328
|
+
// Don't count unchecked required radio as blank if other radio with same name is checked,
|
329
|
+
// regardless of whether same-name radio input has required attribute or not. The spec
|
330
|
+
// states https://www.w3.org/TR/html5/forms.html#the-required-attribute
|
331
|
+
radioName = input.attr('name');
|
332
|
+
|
333
|
+
// Skip if we've already seen the radio with this name.
|
334
|
+
if (!checkedRadioButtonNames[radioName]) {
|
335
|
+
|
336
|
+
// If none checked
|
337
|
+
if (form.find('input[type=radio]:checked[name="' + radioName + '"]').length === 0) {
|
338
|
+
radiosForNameWithNoneSelected = form.find(
|
339
|
+
'input[type=radio][name="' + radioName + '"]');
|
340
|
+
foundInputs = foundInputs.add(radiosForNameWithNoneSelected);
|
341
|
+
}
|
342
|
+
|
343
|
+
// We only need to check each name once.
|
344
|
+
checkedRadioButtonNames[radioName] = radioName;
|
345
|
+
}
|
346
|
+
} else {
|
347
|
+
valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val();
|
348
|
+
if (valueToCheck === nonBlank) {
|
349
|
+
foundInputs = foundInputs.add(input);
|
350
|
+
}
|
351
|
+
}
|
352
|
+
});
|
353
|
+
return foundInputs.length ? foundInputs : false;
|
354
|
+
},
|
355
|
+
|
356
|
+
// Helper function which checks for non-blank inputs in a form that match the specified CSS selector
|
357
|
+
nonBlankInputs: function(form, specifiedSelector) {
|
358
|
+
return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
|
359
|
+
},
|
360
|
+
|
361
|
+
// Helper function, needed to provide consistent behavior in IE
|
362
|
+
stopEverything: function(e) {
|
363
|
+
$(e.target).trigger('ujs:everythingStopped');
|
364
|
+
e.stopImmediatePropagation();
|
365
|
+
return false;
|
366
|
+
},
|
367
|
+
|
368
|
+
// Replace element's html with the 'data-disable-with' after storing original html
|
369
|
+
// and prevent clicking on it
|
370
|
+
disableElement: function(element) {
|
371
|
+
var replacement = element.data('disable-with');
|
372
|
+
|
373
|
+
if (replacement !== undefined) {
|
374
|
+
element.data('ujs:enable-with', element.html()); // store enabled state
|
375
|
+
element.html(replacement);
|
376
|
+
}
|
377
|
+
|
378
|
+
element.on('click.railsDisable', function(e) { // prevent further clicking
|
379
|
+
return rails.stopEverything(e);
|
380
|
+
});
|
381
|
+
element.data('ujs:disabled', true);
|
382
|
+
},
|
383
|
+
|
384
|
+
// Restore element to its original state which was disabled by 'disableElement' above
|
385
|
+
enableElement: function(element) {
|
386
|
+
if (element.data('ujs:enable-with') !== undefined) {
|
387
|
+
element.html(element.data('ujs:enable-with')); // set to old enabled state
|
388
|
+
element.removeData('ujs:enable-with'); // clean up cache
|
389
|
+
}
|
390
|
+
element.off('click.railsDisable'); // enable element
|
391
|
+
element.removeData('ujs:disabled');
|
392
|
+
}
|
393
|
+
};
|
394
|
+
|
395
|
+
if (rails.fire($document, 'rails:attachBindings')) {
|
396
|
+
|
397
|
+
$.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
|
398
|
+
|
399
|
+
// This event works the same as the load event, except that it fires every
|
400
|
+
// time the page is loaded.
|
401
|
+
//
|
402
|
+
// See https://github.com/rails/jquery-ujs/issues/357
|
403
|
+
// See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
|
404
|
+
$(window).on('pageshow.rails', function () {
|
405
|
+
$($.rails.enableSelector).each(function () {
|
406
|
+
var element = $(this);
|
407
|
+
|
408
|
+
if (element.data('ujs:disabled')) {
|
409
|
+
$.rails.enableFormElement(element);
|
410
|
+
}
|
411
|
+
});
|
412
|
+
|
413
|
+
$($.rails.linkDisableSelector).each(function () {
|
414
|
+
var element = $(this);
|
415
|
+
|
416
|
+
if (element.data('ujs:disabled')) {
|
417
|
+
$.rails.enableElement(element);
|
418
|
+
}
|
419
|
+
});
|
420
|
+
});
|
421
|
+
|
422
|
+
$document.on('ajax:complete', rails.linkDisableSelector, function() {
|
423
|
+
rails.enableElement($(this));
|
424
|
+
});
|
425
|
+
|
426
|
+
$document.on('ajax:complete', rails.buttonDisableSelector, function() {
|
427
|
+
rails.enableFormElement($(this));
|
428
|
+
});
|
429
|
+
|
430
|
+
$document.on('click.rails', rails.linkClickSelector, function(e) {
|
431
|
+
var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
|
432
|
+
if (!rails.allowAction(link)) return rails.stopEverything(e);
|
433
|
+
|
434
|
+
if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
|
435
|
+
|
436
|
+
if (rails.isRemote(link)) {
|
437
|
+
if (metaClick && (!method || method === 'GET') && !data) { return true; }
|
438
|
+
|
439
|
+
var handleRemote = rails.handleRemote(link);
|
440
|
+
// Response from rails.handleRemote() will either be false or a deferred object promise.
|
441
|
+
if (handleRemote === false) {
|
442
|
+
rails.enableElement(link);
|
443
|
+
} else {
|
444
|
+
handleRemote.fail( function() { rails.enableElement(link); } );
|
445
|
+
}
|
446
|
+
return false;
|
447
|
+
|
448
|
+
} else if (method) {
|
449
|
+
rails.handleMethod(link);
|
450
|
+
return false;
|
451
|
+
}
|
452
|
+
});
|
453
|
+
|
454
|
+
$document.on('click.rails', rails.buttonClickSelector, function(e) {
|
455
|
+
var button = $(this);
|
456
|
+
|
457
|
+
if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e);
|
458
|
+
|
459
|
+
if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
|
460
|
+
|
461
|
+
var handleRemote = rails.handleRemote(button);
|
462
|
+
// Response from rails.handleRemote() will either be false or a deferred object promise.
|
463
|
+
if (handleRemote === false) {
|
464
|
+
rails.enableFormElement(button);
|
465
|
+
} else {
|
466
|
+
handleRemote.fail( function() { rails.enableFormElement(button); } );
|
467
|
+
}
|
468
|
+
return false;
|
469
|
+
});
|
470
|
+
|
471
|
+
$document.on('change.rails', rails.inputChangeSelector, function(e) {
|
472
|
+
var link = $(this);
|
473
|
+
if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e);
|
474
|
+
|
475
|
+
rails.handleRemote(link);
|
476
|
+
return false;
|
477
|
+
});
|
478
|
+
|
479
|
+
$document.on('submit.rails', rails.formSubmitSelector, function(e) {
|
480
|
+
var form = $(this),
|
481
|
+
remote = rails.isRemote(form),
|
482
|
+
blankRequiredInputs,
|
483
|
+
nonBlankFileInputs;
|
484
|
+
|
485
|
+
if (!rails.allowAction(form)) return rails.stopEverything(e);
|
486
|
+
|
487
|
+
// Skip other logic when required values are missing or file upload is present
|
488
|
+
if (form.attr('novalidate') === undefined) {
|
489
|
+
if (form.data('ujs:formnovalidate-button') === undefined) {
|
490
|
+
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false);
|
491
|
+
if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
|
492
|
+
return rails.stopEverything(e);
|
493
|
+
}
|
494
|
+
} else {
|
495
|
+
// Clear the formnovalidate in case the next button click is not on a formnovalidate button
|
496
|
+
// Not strictly necessary to do here, since it is also reset on each button click, but just to be certain
|
497
|
+
form.data('ujs:formnovalidate-button', undefined);
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
if (remote) {
|
502
|
+
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
|
503
|
+
if (nonBlankFileInputs) {
|
504
|
+
// Slight timeout so that the submit button gets properly serialized
|
505
|
+
// (make it easy for event handler to serialize form without disabled values)
|
506
|
+
setTimeout(function(){ rails.disableFormElements(form); }, 13);
|
507
|
+
var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
|
508
|
+
|
509
|
+
// Re-enable form elements if event bindings return false (canceling normal form submission)
|
510
|
+
if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
|
511
|
+
|
512
|
+
return aborted;
|
513
|
+
}
|
514
|
+
|
515
|
+
rails.handleRemote(form);
|
516
|
+
return false;
|
517
|
+
|
518
|
+
} else {
|
519
|
+
// Slight timeout so that the submit button gets properly serialized
|
520
|
+
setTimeout(function(){ rails.disableFormElements(form); }, 13);
|
521
|
+
}
|
522
|
+
});
|
523
|
+
|
524
|
+
$document.on('click.rails', rails.formInputClickSelector, function(event) {
|
525
|
+
var button = $(this);
|
526
|
+
|
527
|
+
if (!rails.allowAction(button)) return rails.stopEverything(event);
|
528
|
+
|
529
|
+
// Register the pressed submit button
|
530
|
+
var name = button.attr('name'),
|
531
|
+
data = name ? {name:name, value:button.val()} : null;
|
532
|
+
|
533
|
+
var form = button.closest('form');
|
534
|
+
if (form.length === 0) {
|
535
|
+
form = $('#' + button.attr('form'));
|
536
|
+
}
|
537
|
+
form.data('ujs:submit-button', data);
|
538
|
+
|
539
|
+
// Save attributes from button
|
540
|
+
form.data('ujs:formnovalidate-button', button.attr('formnovalidate'));
|
541
|
+
form.data('ujs:submit-button-formaction', button.attr('formaction'));
|
542
|
+
form.data('ujs:submit-button-formmethod', button.attr('formmethod'));
|
543
|
+
});
|
544
|
+
|
545
|
+
$document.on('ajax:send.rails', rails.formSubmitSelector, function(event) {
|
546
|
+
if (this === event.target) rails.disableFormElements($(this));
|
547
|
+
});
|
548
|
+
|
549
|
+
$document.on('ajax:complete.rails', rails.formSubmitSelector, function(event) {
|
550
|
+
if (this === event.target) rails.enableFormElements($(this));
|
551
|
+
});
|
552
|
+
|
553
|
+
$(function(){
|
554
|
+
rails.refreshCSRFTokens();
|
555
|
+
});
|
556
|
+
}
|
557
|
+
|
558
|
+
};
|
559
|
+
|
560
|
+
if (window.jQuery) {
|
561
|
+
jqueryUjsInit(jQuery);
|
562
|
+
} else if (typeof exports === 'object' && typeof module === 'object') {
|
563
|
+
module.exports = jqueryUjsInit;
|
564
|
+
}
|
565
|
+
})();
|
@@ -0,0 +1 @@
|
|
1
|
+
!function(i){i.fn.stupidtable=function(n){return this.each(function(){var t=i(this);n=n||{},n=i.extend({},i.fn.stupidtable.default_sort_fns,n),t.data("sortFns",n),t.stupidtable_build(),t.on("click.stupidtable","thead th",function(){i(this).stupidsort()}),t.find("th[data-sort-onload=yes]").eq(0).stupidsort()})},i.fn.stupidtable.default_settings={should_redraw:function(t){return!0},will_manually_build_table:!1},i.fn.stupidtable.dir={ASC:"asc",DESC:"desc"},i.fn.stupidtable.default_sort_fns={int:function(t,n){return parseInt(t,10)-parseInt(n,10)},float:function(t,n){return parseFloat(t)-parseFloat(n)},string:function(t,n){return t.toString().localeCompare(n.toString())},"string-ins":function(t,n){return t=t.toString().toLocaleLowerCase(),n=n.toString().toLocaleLowerCase(),t.localeCompare(n)}},i.fn.stupidtable_settings=function(a){return this.each(function(){var t=i(this),n=i.extend({},i.fn.stupidtable.default_settings,a);t.stupidtable.settings=n})},i.fn.stupidsort=function(t){var a=i(this),n=a.data("sort")||null;if(null!==n){var r=a.closest("table"),e={$th:a,$table:r,datatype:n};return r.stupidtable.settings||(r.stupidtable.settings=i.extend({},i.fn.stupidtable.default_settings)),e.compare_fn=r.data("sortFns")[n],e.th_index=l(e),e.sort_dir=u(t,e),a.data("sort-dir",e.sort_dir),r.trigger("beforetablesort",{column:e.th_index,direction:e.sort_dir,$th:a}),r.css("display"),setTimeout(function(){r.stupidtable.settings.will_manually_build_table||r.stupidtable_build();var t=s(e),n=d(t,e);r.stupidtable.settings.should_redraw(e)&&(r.children("tbody").append(n),o(e),r.trigger("aftertablesort",{column:e.th_index,direction:e.sort_dir,$th:a}),r.css("display"))},10),a}},i.fn.updateSortVal=function(t){var n=i(this);return n.is("[data-sort-value]")&&n.attr("data-sort-value",t),n.data("sort-value",t),n},i.fn.stupidtable_build=function(){return this.each(function(){var t=i(this),a=[];t.children("tbody").children("tr").each(function(t,n){var e={$tr:i(n),columns:[],index:t};i(n).children("td").each(function(t,n){var a=i(n).data("sort-value");if(void 0===a){var r=i(n).text();i(n).data("sort-value",r),a=r}e.columns.push(a)}),a.push(e)}),t.data("stupidsort_internaltable",a)})};var s=function(s){var t,n=s.$table.data("stupidsort_internaltable"),d=s.th_index,a=s.$th.data("sort-multicolumn");t=a?a.split(","):[];var o=i.map(t,function(t,n){return r(s.$table,t)});return n.sort(function(t,n){for(var a=o.slice(0),r=s.compare_fn(t.columns[d],n.columns[d]);0===r&&a.length;){var e=a[0],i=e.$e.data("sort");r=(0,s.$table.data("sortFns")[i])(t.columns[e.index],n.columns[e.index]),a.shift()}return 0===r?t.index-n.index:r}),s.sort_dir!=i.fn.stupidtable.dir.ASC&&n.reverse(),n},r=function(t,n){var a,r=t.find("th"),e=parseInt(n,10);return e||0===e?a=r.eq(e):(a=r.siblings("#"+n),e=r.index(a)),{index:e,$e:a}},d=function(t,a){var n=i.map(t,function(t,n){return[[t.columns[a.th_index],t.$tr,n]]});return a.column=n,i.map(t,function(t){return t.$tr})},o=function(t){var n=t.$table,a=t.$th,r=a.data("sort-dir");n.find("th").data("sort-dir",null).removeClass("sorting-desc sorting-asc"),a.data("sort-dir",r).addClass("sorting-"+r)},u=function(t,n){var a,r=n.$th,e=i.fn.stupidtable.dir;return t?a=t:(a=t||r.data("sort-default")||e.ASC,r.data("sort-dir")&&(a=r.data("sort-dir")===e.ASC?e.DESC:e.ASC)),a},l=function(t){var n=0,a=t.$th.index();return t.$th.parents("tr").find("th").slice(0,a).each(function(){var t=i(this).attr("colspan")||1;n+=parseInt(t,10)}),n}}(window.jQuery);
|
@@ -4,7 +4,10 @@
|
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<title>Rails Performance</title>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
<%= csp_meta_tag %>
|
7
9
|
<%= render '/stylesheets/stylesheets' %>
|
10
|
+
<link rel="shortcut icon" href="/favicon.ico">
|
8
11
|
</head>
|
9
12
|
<body>
|
10
13
|
<section class="section">
|
@@ -2,14 +2,10 @@
|
|
2
2
|
<div class="cd-panel__container">
|
3
3
|
<nav class="panel">
|
4
4
|
<p class="panel-heading">
|
5
|
-
|
6
|
-
<a href='#' onclick="javascript: hidePanel(); return false;">X</a>
|
5
|
+
<span class="field is-grouped is-grouped-multiline"></span>
|
7
6
|
</p>
|
8
7
|
<div class="panel-block">
|
9
8
|
<div class="cd-panel__content">
|
10
|
-
<% 5.times do %>
|
11
|
-
<p><%= "hello world is here" * 20 %></p>
|
12
|
-
<% end %>
|
13
9
|
</div> <!-- cd-panel__content -->
|
14
10
|
</div>
|
15
11
|
</nav>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<h2 class="subtitle">Throughput</h2>
|
2
|
+
<div id="throughput_report_chart_mini" class="chart_mini"></div>
|
3
|
+
|
4
|
+
<h2 class="subtitle">Average Response Time</h2>
|
5
|
+
<div id="response_time_report_chart_mini" class="chart_mini"></div>
|
6
|
+
|
7
|
+
<h2 class="subtitle"><%= @report.title %></h2>
|
8
|
+
<table class="table is-fullwidth is-hoverable is-narrow is-size-7">
|
9
|
+
<thead>
|
10
|
+
<tr>
|
11
|
+
<th data-sort="string">Datetime</th>
|
12
|
+
<th data-sort="string">Method</th>
|
13
|
+
<th data-sort="string">Path</th>
|
14
|
+
<th data-sort="string">Format</th>
|
15
|
+
<th data-sort="int">Status</th>
|
16
|
+
<th data-sort="float">Duration</th>
|
17
|
+
<th data-sort="float">Views</th>
|
18
|
+
<th data-sort="float">DB</th>
|
19
|
+
</tr>
|
20
|
+
</thead>
|
21
|
+
<tbody>
|
22
|
+
<% @data.each do |e| %>
|
23
|
+
<tr>
|
24
|
+
<td><%= format_datetime e[:datetime] %></td>
|
25
|
+
<td><%= e[:method] %></td>
|
26
|
+
<td><%= link_to_path(e) %></td>
|
27
|
+
<td><%= e[:format] %></td>
|
28
|
+
<td><%= status_tag e[:status] %></td>
|
29
|
+
<td><%= ms e[:duration] %></td>
|
30
|
+
<td><%= ms e[:view_runtime] %></td>
|
31
|
+
<td><%= ms e[:db_runtime] %></td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</tbody>
|
35
|
+
</table>
|
@@ -4,22 +4,22 @@
|
|
4
4
|
<table class="table is-fullwidth is-hoverable is-narrow">
|
5
5
|
<thead>
|
6
6
|
<tr>
|
7
|
-
<th>Datetime</th>
|
8
|
-
<th>Controller#Action</th>
|
9
|
-
<th>Method</th>
|
10
|
-
<th>Format</th>
|
11
|
-
<th>Path</th>
|
12
|
-
<th>Status</th>
|
13
|
-
<th>Duration</th>
|
14
|
-
<th>Views</th>
|
15
|
-
<th>DB</th>
|
7
|
+
<th data-sort="string">Datetime</th>
|
8
|
+
<th data-sort="string">Controller#Action</th>
|
9
|
+
<th data-sort="string">Method</th>
|
10
|
+
<th data-sort="string">Format</th>
|
11
|
+
<th data-sort="string">Path</th>
|
12
|
+
<th data-sort="string">Status</th>
|
13
|
+
<th data-sort="float">Duration</th>
|
14
|
+
<th data-sort="float">Views</th>
|
15
|
+
<th data-sort="float">DB</th>
|
16
16
|
</tr>
|
17
17
|
</thead>
|
18
18
|
<tbody>
|
19
19
|
<% @data.each do |e| %>
|
20
20
|
<tr>
|
21
21
|
<td><%= format_datetime e[:datetime] %></td>
|
22
|
-
<td><%= e[:controller] + '#' + e[:action] %></td>
|
22
|
+
<td><%= link_to e[:controller] + '#' + e[:action], rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true %></td>
|
23
23
|
<td><%= e[:method] %></td>
|
24
24
|
<td><%= e[:format] %></td>
|
25
25
|
<td><%= link_to_path(e) %></td>
|
@@ -6,22 +6,22 @@
|
|
6
6
|
<table class="table is-fullwidth is-hoverable is-narrow">
|
7
7
|
<thead>
|
8
8
|
<tr>
|
9
|
-
<th>Datetime</th>
|
10
|
-
<th>Controller#
|
11
|
-
<th>Method</th>
|
12
|
-
<th>Format</th>
|
13
|
-
<th>Path</th>
|
14
|
-
<th>Status</th>
|
15
|
-
<th>Duration</th>
|
16
|
-
<th>Views</th>
|
17
|
-
<th>DB</th>
|
9
|
+
<th data-sort="string">Datetime</th>
|
10
|
+
<th data-sort="string">Controller#action</th>
|
11
|
+
<th data-sort="string">Method</th>
|
12
|
+
<th data-sort="string">Format</th>
|
13
|
+
<th data-sort="string">Path</th>
|
14
|
+
<th data-sort="string">Status</th>
|
15
|
+
<th data-sort="float">Duration</th>
|
16
|
+
<th data-sort="float">Views</th>
|
17
|
+
<th data-sort="float">DB</th>
|
18
18
|
</tr>
|
19
19
|
</thead>
|
20
20
|
<tbody>
|
21
21
|
<% @data.each do |e| %>
|
22
22
|
<tr>
|
23
23
|
<td><%= format_datetime e[:datetime] %></td>
|
24
|
-
<td><%= e[:controller] + '#' + e[:action] %></td>
|
24
|
+
<td><%= link_to e[:controller] + '#' + e[:action], rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true %></td>
|
25
25
|
<td><%= e[:method] %></td>
|
26
26
|
<td><%= e[:format] %></td>
|
27
27
|
<td><%= link_to_path(e) %></td>
|
@@ -9,15 +9,15 @@
|
|
9
9
|
<th colspan='3'>Slowest</th>
|
10
10
|
</tr>
|
11
11
|
<tr>
|
12
|
-
<th>Controller#
|
13
|
-
<th>Format</th>
|
14
|
-
<th>Requests</th>
|
15
|
-
<th>Duration</th>
|
16
|
-
<th>Views</th>
|
17
|
-
<th>DB</th>
|
18
|
-
<th>Duration</th>
|
19
|
-
<th>Views</th>
|
20
|
-
<th>DB</th>
|
12
|
+
<th data-sort="string">Controller#action</th>
|
13
|
+
<th data-sort="string">Format</th>
|
14
|
+
<th data-sort="int">Requests</th>
|
15
|
+
<th data-sort="float">Duration</th>
|
16
|
+
<th data-sort="float">Views</th>
|
17
|
+
<th data-sort="float">DB</th>
|
18
|
+
<th data-sort="float">Duration</th>
|
19
|
+
<th data-sort="float">Views</th>
|
20
|
+
<th data-sort="float">DB</th>
|
21
21
|
</tr>
|
22
22
|
</thead>
|
23
23
|
<tbody>
|
@@ -25,13 +25,8 @@
|
|
25
25
|
<% groups = e[:group].split("|") %>
|
26
26
|
<% c, a = groups[0].split("#") %>
|
27
27
|
<tr>
|
28
|
-
<td>
|
29
|
-
|
30
|
-
<%= link_to rails_performance.rails_performance_breakdown_path(controller_eq: c, action_eq: a), class: 'stats_icon', target: '_blank' do %>
|
31
|
-
<%= icon("stat") %>
|
32
|
-
<% end %>
|
33
|
-
</td>
|
34
|
-
<td><%= statistics_link groups[1]&.upcase, @report, e[:group] %></td>
|
28
|
+
<td><%= link_to groups[0], rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a}), remote: true %></td>
|
29
|
+
<td><%= link_to groups[1]&.upcase, rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a, format_eq: groups[1]}), remote: true %></td>
|
35
30
|
<td><%= e[:count] %></td>
|
36
31
|
<td><%= ms e[:duration_average] %></td>
|
37
32
|
<td><%= ms e[:view_runtime_average] %></td>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
window.panel.header.html(window.panel.close + '<%= j report_name(@datasource.q) %>');
|
2
|
+
window.panel.content.html("<%= j render '/rails_performance/summary' %>");
|
3
|
+
|
4
|
+
var data1 = <%= raw @throughput_report_data.to_json %>;
|
5
|
+
showTIRChart('throughput_report_chart_mini', data1);
|
6
|
+
|
7
|
+
var data2 = <%= raw @response_time_report_data.to_json %>;
|
8
|
+
showRTChart('response_time_report_chart_mini', data2);
|
9
|
+
|
10
|
+
showPanel();
|
@@ -14,11 +14,10 @@
|
|
14
14
|
|
15
15
|
<div id="navbarBasicExample" class="navbar-menu">
|
16
16
|
<div class="navbar-start">
|
17
|
-
<%= link_to '
|
18
|
-
<%= link_to 'Requests', rails_performance.rails_performance_requests_url, class: "navbar-item #{active?(:requests)}" %>
|
17
|
+
<%= link_to 'Dashboard', rails_performance.rails_performance_url, class: "navbar-item #{active?(:dashboard)}" %>
|
18
|
+
<%= link_to 'Requests Analysis', rails_performance.rails_performance_requests_url, class: "navbar-item #{active?(:requests)}" %>
|
19
19
|
<%= link_to '500 Errors', rails_performance.rails_performance_crashes_url, class: "navbar-item #{active?(:crashes)}" %>
|
20
20
|
<%= link_to 'Recent Requests', rails_performance.rails_performance_recent_url, class: "navbar-item #{active?(:recent)}" %>
|
21
|
-
<a href='#' class="navbar-item" onclick="javascript: showPanel(); return false;">Toggle</a>
|
22
21
|
</div>
|
23
22
|
|
24
23
|
<div class="navbar-end">
|
@@ -12,7 +12,13 @@
|
|
12
12
|
display: none;
|
13
13
|
}
|
14
14
|
|
15
|
+
.panel-close {
|
16
|
+
color: red;
|
17
|
+
padding-right: 10px;
|
18
|
+
}
|
19
|
+
|
15
20
|
.cd-panel {
|
21
|
+
position: relative;
|
16
22
|
visibility: hidden;
|
17
23
|
transition: visibility 0s 0.6s;
|
18
24
|
}
|
@@ -25,11 +31,11 @@
|
|
25
31
|
.cd-panel__container {
|
26
32
|
z-index: 100;
|
27
33
|
position: fixed;
|
28
|
-
width:
|
29
|
-
max-width: 500px;
|
34
|
+
width: 800px;
|
30
35
|
height: 100%;
|
31
36
|
top: 0;
|
32
37
|
transition: transform 0.3s 0s;
|
38
|
+
overflow: scroll;
|
33
39
|
}
|
34
40
|
|
35
41
|
.cd-panel--from-right .cd-panel__container {
|
@@ -12,6 +12,10 @@
|
|
12
12
|
height: 255px;
|
13
13
|
}
|
14
14
|
|
15
|
+
.chart_mini {
|
16
|
+
height: 150px;
|
17
|
+
width: 720px;
|
18
|
+
}
|
15
19
|
|
16
20
|
.logo h2 {
|
17
21
|
text-transform: uppercase;
|
@@ -24,3 +28,8 @@
|
|
24
28
|
.navbar-item.is-active {
|
25
29
|
border-bottom: 1px solid red;
|
26
30
|
}
|
31
|
+
|
32
|
+
|
33
|
+
table th[data-sort] {
|
34
|
+
cursor: pointer;
|
35
|
+
}
|
data/config/routes.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
RailsPerformance::Engine.routes.draw do
|
2
2
|
get '/' => 'rails_performance#index', as: :rails_performance
|
3
|
-
get '/breakdown' => 'rails_performance#breakdown', as: :rails_performance_breakdown
|
4
3
|
|
5
4
|
get '/requests' => 'rails_performance#requests', as: :rails_performance_requests
|
6
5
|
get '/crashes' => 'rails_performance#crashes', as: :rails_performance_crashes
|
7
6
|
get '/recent' => 'rails_performance#recent', as: :rails_performance_recent
|
7
|
+
|
8
|
+
get '/summary' => 'rails_performance#summary', as: :rails_performance_summary
|
8
9
|
end
|
9
10
|
|
10
11
|
Rails.application.routes.draw do
|
@@ -15,7 +15,7 @@ module RailsPerformance
|
|
15
15
|
status: record.status,
|
16
16
|
method: record.method,
|
17
17
|
path: record.path,
|
18
|
-
datetime: Time.
|
18
|
+
datetime: Time.at(record.datetimei.to_i),
|
19
19
|
duration: record.value['duration'],
|
20
20
|
db_runtime: record.value['db_runtime'],
|
21
21
|
view_runtime: record.value['view_runtime'],
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_performance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-02-
|
11
|
+
date: 2020-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -91,7 +91,12 @@ files:
|
|
91
91
|
- README.md
|
92
92
|
- Rakefile
|
93
93
|
- app/assets/config/rails_performance_manifest.js
|
94
|
+
- app/assets/images/activity.svg
|
95
|
+
- app/assets/images/close.svg
|
96
|
+
- app/assets/images/export.svg
|
94
97
|
- app/assets/images/home.svg
|
98
|
+
- app/assets/images/import.svg
|
99
|
+
- app/assets/images/menu.svg
|
95
100
|
- app/assets/images/stat.svg
|
96
101
|
- app/controllers/rails_performance_controller.rb
|
97
102
|
- app/helpers/rails_performance_helper.rb
|
@@ -99,13 +104,17 @@ files:
|
|
99
104
|
- app/views/javascripts/app.js
|
100
105
|
- app/views/javascripts/jquery-3.4.1.min.js
|
101
106
|
- app/views/javascripts/panel.js
|
107
|
+
- app/views/javascripts/rails.js
|
108
|
+
- app/views/javascripts/stupidtable.min.js
|
109
|
+
- app/views/javascripts/table.js
|
102
110
|
- app/views/layouts/rails_performance.html.erb
|
103
111
|
- app/views/rails_performance/_panel.html.erb
|
104
|
-
- app/views/rails_performance/
|
112
|
+
- app/views/rails_performance/_summary.html.erb
|
105
113
|
- app/views/rails_performance/crashes.html.erb
|
106
114
|
- app/views/rails_performance/index.html.erb
|
107
115
|
- app/views/rails_performance/recent.html.erb
|
108
116
|
- app/views/rails_performance/requests.html.erb
|
117
|
+
- app/views/rails_performance/summary.js.erb
|
109
118
|
- app/views/shared/_header.html.erb
|
110
119
|
- app/views/stylesheets/_stylesheets.html.erb
|
111
120
|
- app/views/stylesheets/bulma.min.css
|
@@ -1,37 +0,0 @@
|
|
1
|
-
<title><%= @report.title %></title>
|
2
|
-
|
3
|
-
<div class="card">
|
4
|
-
<div class="card-content">
|
5
|
-
<h2 class="subtitle"><%= @report.title %></h2>
|
6
|
-
<table class="table is-fullwidth is-hoverable is-narrow">
|
7
|
-
<thead>
|
8
|
-
<tr>
|
9
|
-
<th>Datetime</th>
|
10
|
-
<th>Controller#Action</th>
|
11
|
-
<th>Method</th>
|
12
|
-
<th>Path</th>
|
13
|
-
<th>Format</th>
|
14
|
-
<th>Status</th>
|
15
|
-
<th>Duration</th>
|
16
|
-
<th>Views</th>
|
17
|
-
<th>DB</th>
|
18
|
-
</tr>
|
19
|
-
</thead>
|
20
|
-
<tbody>
|
21
|
-
<% @data.each do |e| %>
|
22
|
-
<tr>
|
23
|
-
<td><%= l e[:datetime], format: :short %></td>
|
24
|
-
<td><%= e[:controller] + '#' + e[:action] %></td>
|
25
|
-
<td><%= e[:method] %></td>
|
26
|
-
<td><%= link_to_path(e) %></td>
|
27
|
-
<td><%= e[:format] %></td>
|
28
|
-
<td><%= status_tag e[:status] %></td>
|
29
|
-
<td><%= ms e[:duration] %></td>
|
30
|
-
<td><%= ms e[:view_runtime] %></td>
|
31
|
-
<td><%= ms e[:db_runtime] %></td>
|
32
|
-
</tr>
|
33
|
-
<% end %>
|
34
|
-
</tbody>
|
35
|
-
</table>
|
36
|
-
</div>
|
37
|
-
</div>
|