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.
Files changed (38) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +36 -12
  3. data/README.md +16 -12
  4. data/docs/DSL.md +152 -0
  5. data/docs/GettingStarted.md +51 -0
  6. data/docs/README.md +6 -0
  7. data/lib/rack/templates/assets/css/profiler.css +1 -1
  8. data/lib/rack/templates/assets/css/rwpt.css +1 -1
  9. data/lib/rack/templates/assets/sass/_variables.scss +21 -2
  10. data/lib/rack/templates/assets/sass/profiler.scss +73 -61
  11. data/lib/rack/templates/assets/sass/rwpt.scss +2 -2
  12. data/lib/rack/templates/panel/show.erb +4 -4
  13. data/lib/rack/templates/profiler.erb +4 -0
  14. data/lib/rack/web_profiler/collector.rb +197 -125
  15. data/lib/rack/web_profiler/collectors/rack_collector.rb +74 -0
  16. data/lib/rack/web_profiler/{collector/rack → collectors}/request_collector.rb +43 -60
  17. data/lib/rack/web_profiler/{collector → collectors}/ruby_collector.rb +4 -3
  18. data/lib/rack/web_profiler/{collector → collectors}/time_collector.rb +12 -6
  19. data/lib/rack/web_profiler/collectors.rb +21 -27
  20. data/lib/rack/web_profiler/config.rb +4 -9
  21. data/lib/rack/web_profiler/controller.rb +131 -126
  22. data/lib/rack/web_profiler/engine.rb +10 -14
  23. data/lib/rack/web_profiler/model.rb +74 -23
  24. data/lib/rack/web_profiler/request.rb +22 -7
  25. data/lib/rack/web_profiler/response.rb +27 -0
  26. data/lib/rack/web_profiler/rouge/html_formatter.rb +25 -0
  27. data/lib/rack/web_profiler/router.rb +11 -6
  28. data/lib/rack/web_profiler/version.rb +1 -1
  29. data/lib/rack/web_profiler/view.rb +255 -139
  30. data/lib/rack/web_profiler.rb +47 -4
  31. data/rack-webprofiler.gemspec +1 -3
  32. metadata +32 -32
  33. data/lib/rack/web_profiler/collector/debug_collector.rb +0 -31
  34. data/lib/rack/web_profiler/collector/erb_collector.rb +0 -0
  35. data/lib/rack/web_profiler/collector/performance_collector.rb +0 -1
  36. data/lib/rack/web_profiler/collector/rack/rack_collector.rb +0 -23
  37. data/lib/rack/web_profiler/collector/view.rb +0 -44
  38. data/lib/rack/web_profiler/model/collection_record.rb +0 -46
@@ -1,18 +1,24 @@
1
1
  module Rack
2
- class WebProfiler::Collector::TimeCollector
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
- collector_name "time"
10
- position 3
9
+ identifier "time"
10
+ label "Time"
11
+ position 3
11
12
 
12
13
  collect do |request, _response|
13
- store :runtime, request.runtime
14
+ runtime = request.env[WebProfiler::ENV_RUNTIME] * 1000.0
14
15
 
15
- status :warning if request.runtime >= 500
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
- <%= (data(:runtime) * 1000.0).round(2) %> ms
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 name.
18
+ # Get a collector definition by his identifier.
15
19
  #
16
- # @param name [String]
20
+ # @param identifier [String]
17
21
  #
18
22
  # @return [Rack::WebProfiler::Collector::DSL::Definition, nil]
19
- def definition_by_name(name)
20
- name = name.to_sym
21
- @sorted_collectors[name] unless @sorted_collectors[name].nil?
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 name is already registrered.
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
- unless collector_class.is_a? Class
43
- raise ArgumentError, "`collector_class' must be a class"
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 definition_by_name(definition.name)
53
- raise ArgumentError, "A collector with name \“#{definition.name}\" already exists"
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 @collectors[collector_class]
78
- raise ArgumentError, "No collector found with class \“#{collector_class}\""
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 name.
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.name.to_sym] = 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
- # Commons
10
- Rack::WebProfiler::Collector::RubyCollector,
11
- Rack::WebProfiler::Collector::TimeCollector,
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
- # Controller
6
- #
7
- # Generate the views of the WebProfiler.
8
- class WebProfiler::Controller
9
- # Initialize
10
- #
11
- # @param request [Rack::WebProfiler::Request]
12
- def initialize(request)
13
- @request = request
14
- end
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
- # List the webprofiler history.
17
- #
18
- # @return [Rack::Response]
19
- def index
20
- @collections = Rack::WebProfiler::Model::CollectionRecord.order(Sequel.desc(:created_at))
21
- .limit(20)
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
- # Show the webprofiler panel.
29
- #
30
- # @param token [String] The collection token
31
- #
32
- # @return [Rack::Response]
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
- return json(@collection) if prefer_json?
48
- erb "panel/show.erb", layout: "panel/layout.erb"
49
- end
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
- # Print the webprofiler toolbar.
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
- @collectors = Rack::WebProfiler.config.collectors.all
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
- erb "profiler.erb"
63
- end
49
+ return not_found if @collector.nil?
64
50
 
65
- # Clean the webprofiler.
66
- #
67
- # @return [Rack::Response]
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
- redirect WebProfiler::Router.url_for_profiler
72
- end
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
- private
64
+ @collectors = Rack::WebProfiler.config.collectors.all
75
65
 
76
- # Is "application/json" reponse is prefered?
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
- # Returns the prefered Content-Type response between html and json.
86
- #
87
- # @return [String]
88
- #
89
- # @private
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
- # Redirection.
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
- # 404 page.
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
- # Render a HTML reponse from an ERB template.
117
- #
118
- # @param path [String] Path to the ERB template
119
- # @option layout [String, nil] Path to the ERB layout
120
- # @option variables [Hash, nil] List of variables to the view
121
- # @option status [Integer] HTTP status code
122
- #
123
- # @return [Rack::Response]
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
- variables ||= binding
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
- Rack::Response.new(v.result(variables), status, {
132
- "Content-Type" => "text/html",
133
- })
134
- end
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
- # Render a JSON response from an Array or a Hash.
137
- #
138
- # @param data [Array, Hash] Data
139
- # @param status [Integer]
140
- # @param opts [Hash]
141
- #
142
- # @return [Rack::Response]
143
- #
144
- # @private
145
- def json(data = {}, status: 200, to_json_opts: {})
146
- Rack::Response.new(data.send(:to_json, opts), status, {
147
- "Content-Type" => "application/json",
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 defined? Rails and body.is_a? ActionDispatch::Response::RackBody
28
- body = body.body
29
- end
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.is_a? Array
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
- # Model
5
- class WebProfiler::Model
6
- autoload :CollectionRecord, "rack/web_profiler/model/collection_record"
7
-
8
- class << self
9
- # Get the WebProfiler database.
10
- #
11
- # @return [Sequel::SQLite::Database]
12
- def database
13
- @db ||= Sequel.connect("sqlite://#{db_file_path}", timeout: 50)
14
- end
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
- # Remove the database content.
17
- def clean
18
- return unless ::File.exist?(db_file_path)
25
+ ::File.delete(db_file_path)
26
+ @db = nil
27
+ @db_file_path = nil
28
+ end
19
29
 
20
- ::File.delete(db_file_path)
21
- @db = nil
22
- @db_file_path = nil
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
- private
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
- # Returns the db file path.
28
- #
29
- # @return [String]
30
- def db_file_path
31
- @db_file_path ||= ::File.join(WebProfiler.config.tmp_dir, "rack-webprofiler.db")
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
- attr_reader :runtime, :exception
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
- def start_runtime!
7
- @request_start ||= Time.now.to_f
13
+ # Get body has a String.
14
+ #
15
+ # @return [String]
16
+ def body_string
17
+ @body.to_s
8
18
  end
9
19
 
10
- def save_runtime!
11
- @runtime ||= Time.now.to_f - @request_start
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 save_exception(e)
15
- @exception = e
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