rack-webprofiler 0.1.0.pre.beta2 → 0.1.0
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.
- 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
|