rack-webprofiler 0.1.0.pre.beta2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +36 -12
- data/README.md +16 -12
- data/docs/DSL.md +152 -0
- data/docs/GettingStarted.md +51 -0
- data/docs/README.md +6 -0
- data/lib/rack/templates/assets/css/profiler.css +1 -1
- data/lib/rack/templates/assets/css/rwpt.css +1 -1
- data/lib/rack/templates/assets/sass/_variables.scss +21 -2
- data/lib/rack/templates/assets/sass/profiler.scss +73 -61
- data/lib/rack/templates/assets/sass/rwpt.scss +2 -2
- data/lib/rack/templates/panel/show.erb +4 -4
- data/lib/rack/templates/profiler.erb +4 -0
- data/lib/rack/web_profiler/collector.rb +197 -125
- data/lib/rack/web_profiler/collectors/rack_collector.rb +74 -0
- data/lib/rack/web_profiler/{collector/rack → collectors}/request_collector.rb +43 -60
- data/lib/rack/web_profiler/{collector → collectors}/ruby_collector.rb +4 -3
- data/lib/rack/web_profiler/{collector → collectors}/time_collector.rb +12 -6
- data/lib/rack/web_profiler/collectors.rb +21 -27
- data/lib/rack/web_profiler/config.rb +4 -9
- data/lib/rack/web_profiler/controller.rb +131 -126
- data/lib/rack/web_profiler/engine.rb +10 -14
- data/lib/rack/web_profiler/model.rb +74 -23
- data/lib/rack/web_profiler/request.rb +22 -7
- data/lib/rack/web_profiler/response.rb +27 -0
- data/lib/rack/web_profiler/rouge/html_formatter.rb +25 -0
- data/lib/rack/web_profiler/router.rb +11 -6
- data/lib/rack/web_profiler/version.rb +1 -1
- data/lib/rack/web_profiler/view.rb +255 -139
- data/lib/rack/web_profiler.rb +47 -4
- data/rack-webprofiler.gemspec +1 -3
- metadata +32 -32
- data/lib/rack/web_profiler/collector/debug_collector.rb +0 -31
- data/lib/rack/web_profiler/collector/erb_collector.rb +0 -0
- data/lib/rack/web_profiler/collector/performance_collector.rb +0 -1
- data/lib/rack/web_profiler/collector/rack/rack_collector.rb +0 -23
- data/lib/rack/web_profiler/collector/view.rb +0 -44
- data/lib/rack/web_profiler/model/collection_record.rb +0 -46
@@ -1,18 +1,24 @@
|
|
1
1
|
module Rack
|
2
|
-
class WebProfiler::
|
2
|
+
class WebProfiler::Collectors::TimeCollector
|
3
3
|
include Rack::WebProfiler::Collector::DSL
|
4
4
|
|
5
5
|
icon <<-'ICON'
|
6
6
|
data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+R3JvdXAgMjwvdGl0bGU+ICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPiAgICA8ZGVmcz48L2RlZnM+ICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICAgICAgPGcgaWQ9IkRlc2t0b3AtMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEwLjAwMDAwMCwgLTE3MC4wMDAwMDApIiBmaWxsPSIjNTg1NDczIj4gICAgICAgICAgICA8ZyBpZD0iUGVyZm9ybWFuY2UiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDAwMDAwLCAxNjAuMDAwMDAwKSI+ICAgICAgICAgICAgICAgIDxnIGlkPSJHcm91cC0yIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMC4wMDAwMDAsIDEwLjAwMDAwMCkiPiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTEwLDE4IEwxMCwxOCBDMTQuNDE4Mjc4LDE4IDE4LDE0LjQxODI3OCAxOCwxMCBDMTgsNS41ODE3MjIgMTQuNDE4Mjc4LDIgMTAsMiBDNS41ODE3MjIsMiAyLDUuNTgxNzIyIDIsMTAgQzIsMTQuNDE4Mjc4IDUuNTgxNzIyLDE4IDEwLDE4IEwxMCwxOCBMMTAsMTggWiBNMCwxMCBDMCw0LjQ3NzE1MjUgNC40NzcxNTI1LDAgMTAsMCBDMTUuNTIyODQ3NSwwIDIwLDQuNDc3MTUyNSAyMCwxMCBDMjAsMTUuNTIyODQ3NSAxNS41MjI4NDc1LDIwIDEwLDIwIEM0LjQ3NzE1MjUsMjAgMCwxNS41MjI4NDc1IDAsMTAgWiIgaWQ9IlNoYXBlIj48L3BhdGg+ICAgICAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlLTQzIiB4PSI5IiB5PSI0IiB3aWR0aD0iMiIgaGVpZ2h0PSI1Ij48L3JlY3Q+ICAgICAgICAgICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlLTQzLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0LjAwMDAwMCwgMTAuMDAwMDAwKSByb3RhdGUoLTI3MC4wMDAwMDApIHRyYW5zbGF0ZSgtMTQuMDAwMDAwLCAtMTAuMDAwMDAwKSAiIHg9IjEzIiB5PSI4IiB3aWR0aD0iMiIgaGVpZ2h0PSI0Ij48L3JlY3Q+ICAgICAgICAgICAgICAgIDwvZz4gICAgICAgICAgICA8L2c+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=
|
7
7
|
ICON
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
identifier "time"
|
10
|
+
label "Time"
|
11
|
+
position 3
|
11
12
|
|
12
13
|
collect do |request, _response|
|
13
|
-
|
14
|
+
runtime = request.env[WebProfiler::ENV_RUNTIME] * 1000.0
|
14
15
|
|
15
|
-
|
16
|
+
store :runtime, runtime
|
17
|
+
|
18
|
+
status :warning if runtime >= 200
|
19
|
+
status :danger if runtime >= 1000
|
20
|
+
|
21
|
+
show_panel false
|
16
22
|
end
|
17
23
|
|
18
24
|
template __FILE__, type: :DATA
|
@@ -21,5 +27,5 @@ end
|
|
21
27
|
|
22
28
|
__END__
|
23
29
|
<% tab_content do %>
|
24
|
-
<%=
|
30
|
+
<%= data(:runtime).round(2) %> ms
|
25
31
|
<% end %>
|
@@ -1,9 +1,13 @@
|
|
1
1
|
module Rack
|
2
|
-
|
3
2
|
# Collectors.
|
4
3
|
#
|
5
4
|
# Container of Collector objects.
|
6
5
|
class WebProfiler::Collectors
|
6
|
+
# Collectors
|
7
|
+
autoload :RackCollector, "rack/web_profiler/collectors/rack_collector"
|
8
|
+
autoload :RequestCollector, "rack/web_profiler/collectors/request_collector"
|
9
|
+
autoload :RubyCollector, "rack/web_profiler/collectors/ruby_collector"
|
10
|
+
autoload :TimeCollector, "rack/web_profiler/collectors/time_collector"
|
7
11
|
|
8
12
|
# Initialize.
|
9
13
|
def initialize
|
@@ -11,14 +15,14 @@ module Rack
|
|
11
15
|
@sorted_collectors = {}
|
12
16
|
end
|
13
17
|
|
14
|
-
# Get a collector definition by his
|
18
|
+
# Get a collector definition by his identifier.
|
15
19
|
#
|
16
|
-
# @param
|
20
|
+
# @param identifier [String]
|
17
21
|
#
|
18
22
|
# @return [Rack::WebProfiler::Collector::DSL::Definition, nil]
|
19
|
-
def
|
20
|
-
|
21
|
-
@sorted_collectors[
|
23
|
+
def definition_by_identifier(identifier)
|
24
|
+
identifier = identifier.to_sym
|
25
|
+
@sorted_collectors[identifier] unless @sorted_collectors[identifier].nil?
|
22
26
|
end
|
23
27
|
|
24
28
|
# Returns all collectors definition.
|
@@ -33,15 +37,12 @@ module Rack
|
|
33
37
|
# @param collector_class [Array, Class]
|
34
38
|
#
|
35
39
|
# @raise [ArgumentError] if `collector_class' is not a Class or is not an instance of Rack::WebProfiler::Collector::DSL
|
36
|
-
# or a collector with this
|
40
|
+
# or a collector with this identifier is already registrered.
|
37
41
|
def add_collector(collector_class)
|
38
|
-
if collector_class.is_a? Array
|
39
|
-
return collector_class.each { |c| add_collector(c) }
|
40
|
-
end
|
42
|
+
return collector_class.each { |c| add_collector(c) } if collector_class.is_a? Array
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
44
|
+
|
45
|
+
raise ArgumentError, "`collector_class' must be a class" unless collector_class.is_a? Class
|
45
46
|
|
46
47
|
unless collector_class.included_modules.include?(Rack::WebProfiler::Collector::DSL)
|
47
48
|
raise ArgumentError, "#{collector_class.class.name} must be an instance of \"Rack::WebProfiler::Collector::DSL\""
|
@@ -49,8 +50,8 @@ module Rack
|
|
49
50
|
|
50
51
|
definition = collector_class.definition
|
51
52
|
|
52
|
-
if
|
53
|
-
raise ArgumentError, "A collector with
|
53
|
+
if definition_by_identifier(definition.identifier)
|
54
|
+
raise ArgumentError, "A collector with identifier \“#{definition.identifier}\" already exists"
|
54
55
|
end
|
55
56
|
|
56
57
|
return false unless definition.is_enabled?
|
@@ -66,17 +67,10 @@ module Rack
|
|
66
67
|
#
|
67
68
|
# @raise [ArgumentError] if `collector_class' is not a Class or if this collector is not registered.
|
68
69
|
def remove_collector(collector_class)
|
69
|
-
if collector_class.is_a? Array
|
70
|
-
return collector_class.each { |c| remove_collector(c) }
|
71
|
-
end
|
72
|
-
|
73
|
-
unless collector_class.is_a? Class
|
74
|
-
raise ArgumentError, "`collector_class' must be a class"
|
75
|
-
end
|
70
|
+
return collector_class.each { |c| remove_collector(c) } if collector_class.is_a? Array
|
76
71
|
|
77
|
-
unless
|
78
|
-
|
79
|
-
end
|
72
|
+
raise ArgumentError, "`collector_class' must be a class" unless collector_class.is_a? Class
|
73
|
+
raise ArgumentError, "No collector found with class \“#{collector_class}\"" unless @collectors[collector_class]
|
80
74
|
|
81
75
|
@collectors.delete(collector_class)
|
82
76
|
|
@@ -85,12 +79,12 @@ module Rack
|
|
85
79
|
|
86
80
|
private
|
87
81
|
|
88
|
-
# Sort collectors by definition
|
82
|
+
# Sort collectors by definition identifier.
|
89
83
|
def sort_collectors!
|
90
84
|
@sorted_collectors = {}
|
91
85
|
|
92
86
|
tmp = @collectors.sort_by { |_klass, definition| definition.position }
|
93
|
-
tmp.each { |_k, v| @sorted_collectors[v.
|
87
|
+
tmp.each { |_k, v| @sorted_collectors[v.identifier.to_sym] = v }
|
94
88
|
end
|
95
89
|
end
|
96
90
|
end
|
@@ -6,13 +6,10 @@ module Rack
|
|
6
6
|
attr_accessor :collectors, :tmp_dir
|
7
7
|
|
8
8
|
DEFAULT_COLLECTORS = [
|
9
|
-
|
10
|
-
Rack::WebProfiler::
|
11
|
-
Rack::WebProfiler::
|
12
|
-
|
13
|
-
# Rack
|
14
|
-
# Rack::WebProfiler::Collector::Rack::RackCollector,
|
15
|
-
Rack::WebProfiler::Collector::Rack::RequestCollector,
|
9
|
+
Rack::WebProfiler::Collectors::RackCollector,
|
10
|
+
Rack::WebProfiler::Collectors::RequestCollector,
|
11
|
+
Rack::WebProfiler::Collectors::RubyCollector,
|
12
|
+
Rack::WebProfiler::Collectors::TimeCollector,
|
16
13
|
].freeze
|
17
14
|
def initialize
|
18
15
|
@collectors = Rack::WebProfiler::Collectors.new
|
@@ -21,8 +18,6 @@ module Rack
|
|
21
18
|
end
|
22
19
|
|
23
20
|
# Setup the configuration
|
24
|
-
#
|
25
|
-
# @param [block] block
|
26
21
|
def build!
|
27
22
|
unless block_given?
|
28
23
|
# @todo raise an Exception if no block given
|
@@ -2,150 +2,155 @@ require "erb"
|
|
2
2
|
require "json"
|
3
3
|
|
4
4
|
module Rack
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
class WebProfiler
|
6
|
+
# Controller
|
7
|
+
#
|
8
|
+
# Generate the views of the WebProfiler.
|
9
|
+
class Controller
|
10
|
+
# Initialize.
|
11
|
+
#
|
12
|
+
# @param request [Rack::WebProfiler::Request]
|
13
|
+
def initialize(request)
|
14
|
+
@request = request
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
return json(@collections, 200, {only: [:token, :http_method, :http_status, :url, :ip]}) if prefer_json?
|
24
|
-
return json(@collections, to_json_opts: {only: [:token, :http_method, :http_status, :url, :ip]}) if prefer_json?
|
25
|
-
erb "panel/index.erb", layout: "panel/layout.erb"
|
26
|
-
end
|
17
|
+
# List the webprofiler history.
|
18
|
+
#
|
19
|
+
# @return [Rack::Response]
|
20
|
+
def index
|
21
|
+
@collections = Rack::WebProfiler::Model::CollectionRecord.order(Sequel.desc(:created_at))
|
22
|
+
.limit(20)
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def show(token)
|
34
|
-
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
35
|
-
return not_found if @collection.nil?
|
36
|
-
|
37
|
-
@collectors = Rack::WebProfiler.config.collectors.all
|
38
|
-
@collector = nil
|
39
|
-
|
40
|
-
unless @request.params['panel'].nil?
|
41
|
-
@collector = @collectors[@request.params['panel'].to_sym]
|
42
|
-
else
|
43
|
-
@collector = @collectors.values.first
|
24
|
+
return json(@collections, to_json_opts: {
|
25
|
+
only: [:token, :http_method, :http_status, :url, :ip]
|
26
|
+
}) if prefer_json?
|
27
|
+
|
28
|
+
erb "panel/index.erb", layout: "panel/layout.erb"
|
44
29
|
end
|
45
|
-
return not_found if @collector.nil?
|
46
30
|
|
47
|
-
|
48
|
-
|
49
|
-
|
31
|
+
# Show the webprofiler panel.
|
32
|
+
#
|
33
|
+
# @param token [String] The collection token
|
34
|
+
#
|
35
|
+
# @return [Rack::Response]
|
36
|
+
def show(token)
|
37
|
+
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
38
|
+
return not_found if @collection.nil?
|
50
39
|
|
51
|
-
|
52
|
-
|
53
|
-
# @param token [String] The collection token
|
54
|
-
#
|
55
|
-
# @return [Rack::Response]
|
56
|
-
def show_toolbar(token)
|
57
|
-
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
58
|
-
return erb nil, status: 404 if @collection.nil?
|
40
|
+
@collectors = Rack::WebProfiler.config.collectors.all
|
41
|
+
@collector = nil
|
59
42
|
|
60
|
-
|
43
|
+
unless @request.params["panel"].nil?
|
44
|
+
@collector = @collectors[@request.params["panel"].to_sym]
|
45
|
+
else
|
46
|
+
@collector = @collectors.values.first
|
47
|
+
end
|
61
48
|
|
62
|
-
|
63
|
-
end
|
49
|
+
return not_found if @collector.nil?
|
64
50
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def delete
|
69
|
-
Rack::WebProfiler::Model.clean
|
51
|
+
return json(@collection) if prefer_json?
|
52
|
+
erb "panel/show.erb", layout: "panel/layout.erb"
|
53
|
+
end
|
70
54
|
|
71
|
-
|
72
|
-
|
55
|
+
# Print the webprofiler toolbar.
|
56
|
+
#
|
57
|
+
# @param token [String] The collection token
|
58
|
+
#
|
59
|
+
# @return [Rack::Response]
|
60
|
+
def show_toolbar(token)
|
61
|
+
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
62
|
+
return erb nil, status: 404 if @collection.nil?
|
73
63
|
|
74
|
-
|
64
|
+
@collectors = Rack::WebProfiler.config.collectors.all
|
75
65
|
|
76
|
-
|
77
|
-
|
78
|
-
# @return [Boolean]
|
79
|
-
#
|
80
|
-
# @private
|
81
|
-
def prefer_json?
|
82
|
-
prefered_http_accept == "application/json"
|
83
|
-
end
|
66
|
+
erb "profiler.erb"
|
67
|
+
end
|
84
68
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def prefered_http_accept
|
91
|
-
Rack::Utils.best_q_match(@request.env["HTTP_ACCEPT"], %w[text/html application/json])
|
92
|
-
end
|
69
|
+
# Clean the webprofiler.
|
70
|
+
#
|
71
|
+
# @return [Rack::Response]
|
72
|
+
def delete
|
73
|
+
Rack::WebProfiler::Model.clean!
|
93
74
|
|
94
|
-
|
95
|
-
|
96
|
-
# @param path [string]
|
97
|
-
#
|
98
|
-
# @return [Rack::Response]
|
99
|
-
#
|
100
|
-
# @private
|
101
|
-
def redirect(path)
|
102
|
-
Rack::Response.new([], 302, {
|
103
|
-
"Location" => "#{@request.base_url}#{path}",
|
104
|
-
})
|
105
|
-
end
|
75
|
+
redirect WebProfiler::Router.url_for_profiler
|
76
|
+
end
|
106
77
|
|
107
|
-
|
108
|
-
#
|
109
|
-
# @return [Rack::Response]
|
110
|
-
#
|
111
|
-
# @private
|
112
|
-
def not_found
|
113
|
-
erb "404.erb", layout: "panel/layout.erb", status: 404
|
114
|
-
end
|
78
|
+
private
|
115
79
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
#
|
125
|
-
# @private
|
126
|
-
def erb(path, layout: nil, variables: nil, status: 200)
|
127
|
-
v = WebProfiler::View.new(path, layout: layout)
|
80
|
+
# Is "application/json" reponse is prefered?
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
#
|
84
|
+
# @private
|
85
|
+
def prefer_json?
|
86
|
+
prefered_http_accept == "application/json"
|
87
|
+
end
|
128
88
|
|
129
|
-
|
89
|
+
# Returns the prefered Content-Type response between html and json.
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
#
|
93
|
+
# @private
|
94
|
+
def prefered_http_accept
|
95
|
+
Rack::Utils.best_q_match(@request.env["HTTP_ACCEPT"], %w(text/html application/json))
|
96
|
+
end
|
130
97
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
98
|
+
# Redirection.
|
99
|
+
#
|
100
|
+
# @param path [string]
|
101
|
+
#
|
102
|
+
# @return [Rack::Response]
|
103
|
+
#
|
104
|
+
# @private
|
105
|
+
def redirect(path)
|
106
|
+
Rack::Response.new([], 302, {
|
107
|
+
"Location" => "#{@request.base_url}#{path}",
|
108
|
+
})
|
109
|
+
end
|
135
110
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
111
|
+
# 404 page.
|
112
|
+
#
|
113
|
+
# @return [Rack::Response]
|
114
|
+
#
|
115
|
+
# @private
|
116
|
+
def not_found
|
117
|
+
erb "404.erb", layout: "panel/layout.erb", status: 404
|
118
|
+
end
|
119
|
+
|
120
|
+
# Render a HTML reponse from an ERB template.
|
121
|
+
#
|
122
|
+
# @param path [String] Path to the ERB template
|
123
|
+
# @option layout [String, nil] Path to the ERB layout
|
124
|
+
# @option variables [Hash, nil] List of variables to the view
|
125
|
+
# @option status [Integer] HTTP status code
|
126
|
+
#
|
127
|
+
# @return [Rack::Response]
|
128
|
+
#
|
129
|
+
# @private
|
130
|
+
def erb(path, layout: nil, variables: nil, status: 200)
|
131
|
+
v = WebProfiler::View.new(path, layout: layout)
|
132
|
+
|
133
|
+
variables ||= binding
|
134
|
+
|
135
|
+
Rack::Response.new(v.result(variables), status, {
|
136
|
+
"Content-Type" => "text/html",
|
137
|
+
})
|
138
|
+
end
|
139
|
+
|
140
|
+
# Render a JSON response from an Array or a Hash.
|
141
|
+
#
|
142
|
+
# @param data [Array, Hash] Data
|
143
|
+
# @option status [Integer]
|
144
|
+
# @option to_json_opts [Hash]
|
145
|
+
#
|
146
|
+
# @return [Rack::Response]
|
147
|
+
#
|
148
|
+
# @private
|
149
|
+
def json(data = {}, status: 200, to_json_opts: {})
|
150
|
+
Rack::Response.new(data.send(:to_json, to_json_opts), status, {
|
151
|
+
"Content-Type" => "application/json",
|
152
|
+
})
|
153
|
+
end
|
149
154
|
end
|
150
155
|
end
|
151
156
|
end
|
@@ -5,30 +5,26 @@ module Rack
|
|
5
5
|
# Process request.
|
6
6
|
#
|
7
7
|
# @param request [Rack::WebProfiler::Request]
|
8
|
-
# @param body
|
9
|
-
# @param status
|
10
|
-
# @param headers
|
8
|
+
# @param body [Array, String]
|
9
|
+
# @param status [Integer]
|
10
|
+
# @param headers [Hash]
|
11
11
|
#
|
12
|
-
# @return [Rack::Response]
|
12
|
+
# @return [Rack::WebProfiler::Response, Rack::Response]
|
13
13
|
def process(request, body, status, headers)
|
14
|
-
response = Rack::Response.new(body, status, headers)
|
14
|
+
response = Rack::WebProfiler::Response.new(request, body, status, headers)
|
15
15
|
record = collect!(request, response)
|
16
16
|
|
17
|
-
return response if !headers[CONTENT_TYPE].nil? and !headers[CONTENT_TYPE].include? "text/html"
|
18
|
-
|
19
17
|
@token = record.token
|
20
18
|
@url = WebProfiler::Router.url_for_toolbar(record.token)
|
21
19
|
|
22
|
-
response = Rack::Response.new([], status, headers)
|
23
|
-
|
24
20
|
response.header["X-RackWebProfiler-Token"] = @token
|
25
21
|
response.header["X-RackWebProfiler-Url"] = WebProfiler::Router.url_for_profiler(record.token)
|
26
22
|
|
27
|
-
if
|
28
|
-
|
29
|
-
|
23
|
+
return response if !headers[CONTENT_TYPE].nil? and !headers[CONTENT_TYPE].include? "text/html"
|
24
|
+
|
25
|
+
response = Rack::Response.new([], status, response.headers)
|
30
26
|
|
31
|
-
if body.
|
27
|
+
if body.respond_to?(:each)
|
32
28
|
body.each { |fragment| response.write inject(fragment) }
|
33
29
|
elsif body.is_a? String
|
34
30
|
response.write inject(body)
|
@@ -87,7 +83,7 @@ module Rack
|
|
87
83
|
create_record!
|
88
84
|
save_collected_datas!
|
89
85
|
|
90
|
-
@record.save
|
86
|
+
@record.save({ transaction: true })
|
91
87
|
end
|
92
88
|
|
93
89
|
private
|
@@ -1,34 +1,85 @@
|
|
1
1
|
require "sequel"
|
2
2
|
|
3
3
|
module Rack
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
4
|
+
class WebProfiler
|
5
|
+
# Model
|
6
|
+
class Model
|
7
|
+
class << self
|
8
|
+
# Get the WebProfiler database.
|
9
|
+
#
|
10
|
+
# @return [Sequel::SQLite::Database]
|
11
|
+
def database
|
12
|
+
@db ||= Sequel.connect("sqlite://#{db_file_path}", {
|
13
|
+
single_threaded: true,
|
14
|
+
# timeout: 5000,
|
15
|
+
# # single_threaded: true, # = mieux
|
16
|
+
# pool_timeout: 5000,
|
17
|
+
# max_connections: 1,
|
18
|
+
})
|
19
|
+
end
|
20
|
+
|
21
|
+
# Remove the database content.
|
22
|
+
def clean!
|
23
|
+
return unless ::File.exist?(db_file_path)
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
25
|
+
::File.delete(db_file_path)
|
26
|
+
@db = nil
|
27
|
+
@db_file_path = nil
|
28
|
+
end
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
private
|
31
|
+
|
32
|
+
# Returns the db file path.
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
def db_file_path
|
36
|
+
@db_file_path ||= ::File.join(WebProfiler.config.tmp_dir, "rack-webprofiler.db")
|
37
|
+
end
|
23
38
|
end
|
24
39
|
|
25
|
-
|
40
|
+
class CollectionRecord < Sequel::Model(Rack::WebProfiler::Model.database)
|
41
|
+
# Plugins.
|
42
|
+
plugin :schema
|
43
|
+
plugin :hook_class_methods
|
44
|
+
plugin :serialization
|
45
|
+
plugin :json_serializer
|
46
|
+
plugin :timestamps
|
47
|
+
|
48
|
+
# Attributes:
|
49
|
+
# - id
|
50
|
+
# - token
|
51
|
+
# - ip
|
52
|
+
# - url
|
53
|
+
# - http_method
|
54
|
+
# - http_status
|
55
|
+
# - content_type
|
56
|
+
# - datas
|
57
|
+
# - created_at
|
58
|
+
set_schema do
|
59
|
+
primary_key :id
|
60
|
+
|
61
|
+
varchar :token, unique: true, empty: false
|
62
|
+
varchar :url, empty: false
|
63
|
+
varchar :ip, empty: false
|
64
|
+
varchar :http_method, empty: false
|
65
|
+
integer :http_status
|
66
|
+
varchar :content_type
|
67
|
+
string :datas, text: true, empty: false
|
68
|
+
datetime :created_at
|
69
|
+
end
|
70
|
+
create_table unless table_exists?
|
71
|
+
|
72
|
+
# Serialization.
|
73
|
+
serialize_attributes :marshal, :datas
|
74
|
+
|
75
|
+
# Hooks.
|
76
|
+
before_create :before_create
|
26
77
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
78
|
+
# Generate a token to the record before create it.
|
79
|
+
def before_create
|
80
|
+
token = Time.now.to_f.to_s.delete(".").to_i
|
81
|
+
self.token = token.to_s(36)
|
82
|
+
end
|
32
83
|
end
|
33
84
|
end
|
34
85
|
end
|
@@ -1,18 +1,33 @@
|
|
1
1
|
module Rack
|
2
2
|
#
|
3
3
|
class WebProfiler::Request < Rack::Request
|
4
|
-
|
4
|
+
# Get HTTP headers.
|
5
|
+
#
|
6
|
+
# @return [Hash]
|
7
|
+
def http_headers
|
8
|
+
env.select { |k, _v| (k.start_with?("HTTP_") && k != "HTTP_VERSION") || k == "CONTENT_TYPE" }
|
9
|
+
.collect { |k, v| [k.sub(/^HTTP_/, ""), v] }
|
10
|
+
.collect { |k, v| [k.split("_").collect(&:capitalize).join("-"), v] }
|
11
|
+
end
|
5
12
|
|
6
|
-
|
7
|
-
|
13
|
+
# Get body has a String.
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
def body_string
|
17
|
+
@body.to_s
|
8
18
|
end
|
9
19
|
|
10
|
-
|
11
|
-
|
20
|
+
# Get full HTTP request in HTTP format.
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
def raw
|
24
|
+
headers = http_headers.map { |k, v| "#{k}: #{v}\r\n" }.join
|
25
|
+
format "%s %s %s\r\n%s\r\n%s", request_method.upcase, fullpath, env["SERVER_PROTOCOL"], headers, body_string
|
12
26
|
end
|
13
27
|
|
14
|
-
def
|
15
|
-
@
|
28
|
+
def freeze # :nodoc:
|
29
|
+
@body = body.read
|
30
|
+
super
|
16
31
|
end
|
17
32
|
end
|
18
33
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rack
|
2
|
+
#
|
3
|
+
class WebProfiler::Response < Rack::Response
|
4
|
+
# Initialize.
|
5
|
+
#
|
6
|
+
# @param request [Rack::WebProfiler::Request]
|
7
|
+
# @param body [String, Array]
|
8
|
+
# @param status [Integer]
|
9
|
+
# @param headers [Hash]
|
10
|
+
def initialize(request, body = [], status = 200, headers = {})
|
11
|
+
@request = request
|
12
|
+
@version = "1.0"
|
13
|
+
@version = "1.1" unless request.env["SERVER_PROTOCOL"] == "HTTP/1.0"
|
14
|
+
|
15
|
+
super(body, status, headers)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get full HTTP response in HTTP format.
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def raw
|
22
|
+
formated_headers = headers.map { |k, v| "#{k}: #{v}\r\n" }.join
|
23
|
+
status_text = Rack::Utils::HTTP_STATUS_CODES[status]
|
24
|
+
format "HTTP/%s %s %s\r\n%s\r\n%s", @version, status, status_text, formated_headers, body.join
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|