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.
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