rack-webprofiler 0.1.0.pre.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +27 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +34 -0
- data/.rubocop_todo.yml +0 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +39 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +15 -0
- data/LICENSE +21 -0
- data/README.md +94 -0
- data/Rakefile +17 -0
- data/bin/ci-prepare +16 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/examples/README.md +56 -0
- data/examples/rack/config.ru +52 -0
- data/examples/sinatra/app.rb +37 -0
- data/examples/sinatra/config.ru +3 -0
- data/lib/rack/templates/404.erb +1 -0
- data/lib/rack/templates/assets/rwpt.css +9 -0
- data/lib/rack/templates/assets/rwpt.js +0 -0
- data/lib/rack/templates/async.erb +120 -0
- data/lib/rack/templates/panel/index.erb +29 -0
- data/lib/rack/templates/panel/layout.erb +17 -0
- data/lib/rack/templates/panel/show.erb +19 -0
- data/lib/rack/templates/profiler.erb +70 -0
- data/lib/rack/web_profiler/auto_configure/rails.rb +12 -0
- data/lib/rack/web_profiler/collector/debug_collector.rb +31 -0
- data/lib/rack/web_profiler/collector/erb_collector.rb +0 -0
- data/lib/rack/web_profiler/collector/performance_collector.rb +1 -0
- data/lib/rack/web_profiler/collector/rack/rack_collector.rb +23 -0
- data/lib/rack/web_profiler/collector/rack/request_collector.rb +33 -0
- data/lib/rack/web_profiler/collector/rails/active_record_collector.rb +25 -0
- data/lib/rack/web_profiler/collector/rails/logger_collector.rb +22 -0
- data/lib/rack/web_profiler/collector/rails/rails_collector.rb +25 -0
- data/lib/rack/web_profiler/collector/rails/request_collector.rb +50 -0
- data/lib/rack/web_profiler/collector/ruby_collector.rb +46 -0
- data/lib/rack/web_profiler/collector/sinatra/request_collector.rb +38 -0
- data/lib/rack/web_profiler/collector/sinatra/sinatra_collector.rb +25 -0
- data/lib/rack/web_profiler/collector/time_collector.rb +23 -0
- data/lib/rack/web_profiler/collector.rb +142 -0
- data/lib/rack/web_profiler/collectors.rb +88 -0
- data/lib/rack/web_profiler/config.rb +61 -0
- data/lib/rack/web_profiler/controller.rb +125 -0
- data/lib/rack/web_profiler/engine.rb +109 -0
- data/lib/rack/web_profiler/erb.rb +9 -0
- data/lib/rack/web_profiler/model/collection_record.rb +45 -0
- data/lib/rack/web_profiler/model.rb +35 -0
- data/lib/rack/web_profiler/request.rb +14 -0
- data/lib/rack/web_profiler/router.rb +71 -0
- data/lib/rack/web_profiler/version.rb +5 -0
- data/lib/rack/web_profiler.rb +70 -0
- data/lib/rack/webprofiler.rb +1 -0
- data/rack-webprofiler.gemspec +38 -0
- metadata +198 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
require "docile"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
|
5
|
+
# Collector
|
6
|
+
class WebProfiler::Collector
|
7
|
+
autoload :RubyCollector, "rack/web_profiler/collector/ruby_collector"
|
8
|
+
autoload :TimeCollector, "rack/web_profiler/collector/time_collector"
|
9
|
+
|
10
|
+
module Rack
|
11
|
+
autoload :RackCollector, "rack/web_profiler/collector/rack/rack_collector"
|
12
|
+
autoload :RequestCollector, "rack/web_profiler/collector/rack/request_collector"
|
13
|
+
end
|
14
|
+
|
15
|
+
module Rails
|
16
|
+
autoload :ActiveRecordCollector, "rack/web_profiler/collector/rails/active_record_collector"
|
17
|
+
autoload :LoggerCollector, "rack/web_profiler/collector/rails/logger_collector"
|
18
|
+
autoload :RailsCollector, "rack/web_profiler/collector/rails/rails_collector"
|
19
|
+
autoload :RequestCollector, "rack/web_profiler/collector/rails/request_collector"
|
20
|
+
end
|
21
|
+
|
22
|
+
module Sinatra
|
23
|
+
autoload :RequestCollector, "rack/web_profiler/collector/sinatra/request_collector"
|
24
|
+
autoload :SinatraCollector, "rack/web_profiler/collector/sinatra/sinatra_collector"
|
25
|
+
end
|
26
|
+
|
27
|
+
# DSL
|
28
|
+
module DSL
|
29
|
+
def self.included(base)
|
30
|
+
base.extend(ClassMethods)
|
31
|
+
base.class_eval do
|
32
|
+
@definition = Definition.new
|
33
|
+
@definition.position = 1
|
34
|
+
@definition.is_enabled = true
|
35
|
+
@definition.klass = self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
attr_reader :definition
|
41
|
+
|
42
|
+
def icon(icon = nil); definition.icon = icon; end
|
43
|
+
def collector_name(name = nil); definition.name = name; end
|
44
|
+
def position(position = nil); definition.position = position.to_i; end
|
45
|
+
def collect(&block); definition.collect = block; end
|
46
|
+
|
47
|
+
def template(template = nil, type: :file)
|
48
|
+
template = get_data_contents(template) if type == :DATA
|
49
|
+
definition.template = template
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_enabled?(is_enabled = true)
|
53
|
+
definition.is_enabled = Proc.new if block_given?
|
54
|
+
definition.is_enabled = is_enabled
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def get_data_contents(path)
|
60
|
+
data = ""
|
61
|
+
::File.open(path, "rb") do |f|
|
62
|
+
begin
|
63
|
+
line = f.gets
|
64
|
+
end until line.nil? || /^__END__$/ === line
|
65
|
+
data << line while line = f.gets
|
66
|
+
end
|
67
|
+
data
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Definition
|
73
|
+
#
|
74
|
+
# Collector definition.
|
75
|
+
class Definition
|
76
|
+
attr_accessor :icon, :name, :position, :collect, :template, :is_enabled, :klass
|
77
|
+
attr_reader :data_storage
|
78
|
+
|
79
|
+
# Collect the data who the Collector need.
|
80
|
+
#
|
81
|
+
# @param request [Rack::WebProfiler::Request]
|
82
|
+
# @param response [Rack::Response]
|
83
|
+
#
|
84
|
+
# @return [Rack::WebProfiler::Collector::DSL::DataStorage]
|
85
|
+
def collect!(request, response)
|
86
|
+
@data_storage = Docile.dsl_eval(DataStorage.new, request, response, &collect)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Is the collector enabled.
|
90
|
+
#
|
91
|
+
# @return [Boolean]
|
92
|
+
def is_enabled?
|
93
|
+
return !!@is_enabled.call if @is_enabled.is_a?(Proc)
|
94
|
+
!!@is_enabled
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# DataStorage
|
99
|
+
#
|
100
|
+
# Used to store datas who Collector needs.
|
101
|
+
#
|
102
|
+
# @todo do DataStorage compatible with Marshal
|
103
|
+
class DataStorage
|
104
|
+
attr_reader :datas
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
@datas = Hash.new
|
108
|
+
@status = nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# Store a value.
|
112
|
+
#
|
113
|
+
# @param k [String, Symbol]
|
114
|
+
# @param v
|
115
|
+
def store(k, v)
|
116
|
+
# @todo check data format (must be compatible with Marshal)
|
117
|
+
@datas[k.to_sym] = v
|
118
|
+
end
|
119
|
+
|
120
|
+
# Status.
|
121
|
+
#
|
122
|
+
# @param v [Symbol, nil]
|
123
|
+
#
|
124
|
+
# @return [Symbol, nil]
|
125
|
+
def status(v = nil)
|
126
|
+
# @todo check status?
|
127
|
+
@status = v.to_sym unless v.nil?
|
128
|
+
@status
|
129
|
+
end
|
130
|
+
|
131
|
+
# Transform DataStorage to an Hash
|
132
|
+
#
|
133
|
+
# @return [Hash<Symbol, Object>]
|
134
|
+
def to_h
|
135
|
+
{
|
136
|
+
datas: @datas,
|
137
|
+
status: @status
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Rack
|
2
|
+
|
3
|
+
# Collectors.
|
4
|
+
#
|
5
|
+
# Container of Collector objects.
|
6
|
+
class WebProfiler::Collectors
|
7
|
+
|
8
|
+
# Initialize.
|
9
|
+
def initialize
|
10
|
+
@collectors = {}
|
11
|
+
@sorted_collectors = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get a collector definition by his name.
|
15
|
+
#
|
16
|
+
# @param name [String]
|
17
|
+
#
|
18
|
+
# @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?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns all collectors definition.
|
25
|
+
#
|
26
|
+
# @return [Hash<Symbol, Rack::WebProfiler::Collector::DSL::Definition>]
|
27
|
+
def all
|
28
|
+
@sorted_collectors
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a collector.
|
32
|
+
#
|
33
|
+
# @param collector_class [Class]
|
34
|
+
#
|
35
|
+
# @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.
|
37
|
+
def add_collector(collector_class)
|
38
|
+
unless collector_class.is_a? Class
|
39
|
+
raise ArgumentError, "`collector_class' must be a class"
|
40
|
+
end
|
41
|
+
|
42
|
+
unless collector_class.included_modules.include?(Rack::WebProfiler::Collector::DSL)
|
43
|
+
raise ArgumentError, "#{collector_class.class.name} must be an instance of \"Rack::WebProfiler::Collector::DSL\""
|
44
|
+
end
|
45
|
+
|
46
|
+
definition = collector_class.definition
|
47
|
+
|
48
|
+
if definition_by_name(definition.name)
|
49
|
+
raise ArgumentError, "A collector with name \“#{definition.name}\" already exists"
|
50
|
+
end
|
51
|
+
|
52
|
+
return false unless definition.is_enabled?
|
53
|
+
|
54
|
+
@collectors[collector_class] = definition
|
55
|
+
|
56
|
+
sort_collectors!
|
57
|
+
end
|
58
|
+
|
59
|
+
# Remove a collector.
|
60
|
+
#
|
61
|
+
# @param collector_class [Class]
|
62
|
+
#
|
63
|
+
# @raise [ArgumentError] if `collector_class' is not a Class or if this collector is not registered.
|
64
|
+
def remove_collector(collector_class)
|
65
|
+
unless collector_class.is_a? Class
|
66
|
+
raise ArgumentError, "`collector_class' must be a class"
|
67
|
+
end
|
68
|
+
|
69
|
+
unless @collectors[collector_class]
|
70
|
+
raise ArgumentError, "No collector found with class \“#{collector_class}\""
|
71
|
+
end
|
72
|
+
|
73
|
+
@collectors.delete(collector_class)
|
74
|
+
|
75
|
+
sort_collectors!
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Sort collectors by definition name.
|
81
|
+
def sort_collectors!
|
82
|
+
@sorted_collectors = {}
|
83
|
+
|
84
|
+
tmp = @collectors.sort_by { |_klass, definition| definition.position }
|
85
|
+
tmp.each { |_k, v| @sorted_collectors[v.name.to_sym] = v }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
# Config
|
5
|
+
class WebProfiler::Config
|
6
|
+
attr_accessor :collectors, :tmp_dir
|
7
|
+
|
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,
|
16
|
+
|
17
|
+
# Rails
|
18
|
+
# Rack::WebProfiler::Collector::Rails::ActiveRecordCollector,
|
19
|
+
# Rack::WebProfiler::Collector::Rails::LoggerCollector,
|
20
|
+
Rack::WebProfiler::Collector::Rails::RailsCollector,
|
21
|
+
Rack::WebProfiler::Collector::Rails::RequestCollector,
|
22
|
+
|
23
|
+
# Sinatra
|
24
|
+
Rack::WebProfiler::Collector::Sinatra::RequestCollector,
|
25
|
+
Rack::WebProfiler::Collector::Sinatra::SinatraCollector,
|
26
|
+
].freeze
|
27
|
+
def initialize
|
28
|
+
@collectors = Rack::WebProfiler::Collectors.new
|
29
|
+
|
30
|
+
load_defaults!
|
31
|
+
end
|
32
|
+
|
33
|
+
# Setup the configuration
|
34
|
+
#
|
35
|
+
# @param [block] block
|
36
|
+
def build!
|
37
|
+
unless block_given?
|
38
|
+
# @todo raise an Exception if no block given
|
39
|
+
end
|
40
|
+
instance_eval(&Proc.new)
|
41
|
+
end
|
42
|
+
|
43
|
+
# protected
|
44
|
+
|
45
|
+
def tmp_dir
|
46
|
+
FileUtils.mkdir_p @tmp_dir
|
47
|
+
@tmp_dir
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def load_defaults!
|
53
|
+
# Add default collectors
|
54
|
+
DEFAULT_COLLECTORS.each do |collector_class|
|
55
|
+
@collectors.add_collector(collector_class)
|
56
|
+
end
|
57
|
+
|
58
|
+
@tmp_dir = Dir.tmpdir
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "json"
|
3
|
+
|
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
|
+
@contents_for = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# List the webprofiler history.
|
18
|
+
def index
|
19
|
+
@collections = Rack::WebProfiler::Model::CollectionRecord.order(Sequel.desc(:created_at))
|
20
|
+
.limit(20)
|
21
|
+
|
22
|
+
erb "panel/index.erb", layout: "panel/layout.erb"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Show the webprofiler panel.
|
26
|
+
def show(token)
|
27
|
+
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
28
|
+
return error404 if @collection.nil?
|
29
|
+
|
30
|
+
@collectors = Rack::WebProfiler.config.collectors.all
|
31
|
+
|
32
|
+
# @todo return json if request.content_type ask json (same for xml?)
|
33
|
+
# @example json {} if @request.media_type.include? "json"
|
34
|
+
|
35
|
+
erb "panel/show.erb", layout: "panel/layout.erb"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print the webprofiler toolbar
|
39
|
+
def show_toolbar(token)
|
40
|
+
@collection = Rack::WebProfiler::Model::CollectionRecord[token: token]
|
41
|
+
return erb nil, status: 404 if @collection.nil?
|
42
|
+
|
43
|
+
@collectors = Rack::WebProfiler.config.collectors.all
|
44
|
+
# @todo process the callector views
|
45
|
+
# @collectors = Rack::WebProfiler::Collector.render_tabs(@record)
|
46
|
+
|
47
|
+
erb "profiler.erb"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Clean the webprofiler.
|
51
|
+
def delete
|
52
|
+
Rack::WebProfiler::Model.clean
|
53
|
+
|
54
|
+
redirect WebProfiler::Router.url_for_profiler
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def redirect(path)
|
60
|
+
Rack::Response.new([], 302, {
|
61
|
+
"Location" => "#{@request.base_url}#{path}",
|
62
|
+
})
|
63
|
+
end
|
64
|
+
|
65
|
+
# Render a HTML reponse from an ERB template.
|
66
|
+
#
|
67
|
+
# @param path [String] Path to the ERB template
|
68
|
+
#
|
69
|
+
# @return [Rack::Response]
|
70
|
+
def erb(path, layout: nil, status: 200)
|
71
|
+
content = ""
|
72
|
+
|
73
|
+
unless path.nil?
|
74
|
+
templates = [read_template(path)]
|
75
|
+
templates << read_template(layout) unless layout.nil?
|
76
|
+
|
77
|
+
content = templates.inject(nil) do |prev, temp|
|
78
|
+
_render_erb(temp) { prev }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Rack::Response.new(content, status, {
|
83
|
+
"Content-Type" => "text/html",
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
# Render a JSON response from an Array or a Hash.
|
88
|
+
#
|
89
|
+
# @param data [Array, Hash] Data
|
90
|
+
#
|
91
|
+
# @return [Rack::Response]
|
92
|
+
def json(data = {})
|
93
|
+
Rack::Response.new(data.to_json, 200, {
|
94
|
+
"Content-Type" => "application/json",
|
95
|
+
})
|
96
|
+
end
|
97
|
+
|
98
|
+
def error404
|
99
|
+
erb "404.erb", layout: "panel/layout.erb", status: 404
|
100
|
+
end
|
101
|
+
|
102
|
+
def _render_erb(template)
|
103
|
+
ERB.new(template).result(binding)
|
104
|
+
end
|
105
|
+
|
106
|
+
def partial(path)
|
107
|
+
return "" if path.nil?
|
108
|
+
ERB.new(read_template(path)).result(binding)
|
109
|
+
end
|
110
|
+
|
111
|
+
def render_collector(path, data)
|
112
|
+
@data = data
|
113
|
+
return "" if path.nil?
|
114
|
+
ERB.new(read_template(path)).result(binding)
|
115
|
+
end
|
116
|
+
|
117
|
+
def read_template(template)
|
118
|
+
unless template.empty?
|
119
|
+
path = ::File.expand_path("../../templates/#{template}", __FILE__)
|
120
|
+
return ::File.read(path) if ::File.exist?(path)
|
121
|
+
end
|
122
|
+
template
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Rack
|
2
|
+
#
|
3
|
+
class WebProfiler::Engine
|
4
|
+
class << self
|
5
|
+
# Process
|
6
|
+
#
|
7
|
+
# @param request [Rack::WebProfiler::Request]
|
8
|
+
# @param body
|
9
|
+
# @param status
|
10
|
+
# @param headers
|
11
|
+
#
|
12
|
+
# @return [Rack::Response]
|
13
|
+
def process(request, body, status, headers)
|
14
|
+
response = Rack::Response.new(body, status, headers)
|
15
|
+
record = collect!(request, response)
|
16
|
+
|
17
|
+
return response if !headers[CONTENT_TYPE].nil? and !headers[CONTENT_TYPE].include? "text/html"
|
18
|
+
|
19
|
+
@token = record.token
|
20
|
+
@url = WebProfiler::Router.url_for_toolbar(record.token)
|
21
|
+
|
22
|
+
response = Rack::Response.new([], status, headers)
|
23
|
+
|
24
|
+
response.header["X-RackWebProfiler-Token"] = @token
|
25
|
+
response.header["X-RackWebProfiler-Url"] = @url
|
26
|
+
|
27
|
+
if defined? Rails and body.is_a? ActionDispatch::Response::RackBody
|
28
|
+
body = body.body
|
29
|
+
end
|
30
|
+
|
31
|
+
if body.is_a? Array
|
32
|
+
body.each { |fragment| response.write inject(fragment) }
|
33
|
+
elsif body.is_a? String
|
34
|
+
response.write inject(body)
|
35
|
+
end
|
36
|
+
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Collect
|
43
|
+
#
|
44
|
+
# @param request
|
45
|
+
# @param response
|
46
|
+
def collect!(request, response)
|
47
|
+
processor = Processor.new(request, response)
|
48
|
+
processor.save!
|
49
|
+
|
50
|
+
processor.record
|
51
|
+
end
|
52
|
+
|
53
|
+
# Inject the webprofiler
|
54
|
+
#
|
55
|
+
# @param body [String]
|
56
|
+
def inject(body)
|
57
|
+
body.gsub(%r{</body>}, template.result(binding) + "</body>")
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the javascript code template to inject.
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
def template
|
64
|
+
@template ||= ERB.new(::File.read(::File.expand_path("../../templates/async.erb", __FILE__)))
|
65
|
+
end
|
66
|
+
|
67
|
+
class Processor
|
68
|
+
attr_reader :record
|
69
|
+
|
70
|
+
def initialize(request, response)
|
71
|
+
@collectors = {}
|
72
|
+
@request = request.clone.freeze
|
73
|
+
@response = response.clone.freeze
|
74
|
+
@record = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def save!
|
78
|
+
create_record!
|
79
|
+
save_collected_datas!
|
80
|
+
|
81
|
+
@record.save
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def create_record!
|
87
|
+
@record ||= WebProfiler::Model::CollectionRecord.create({
|
88
|
+
url: @request.path,
|
89
|
+
ip: @request.ip,
|
90
|
+
http_method: @request.request_method,
|
91
|
+
http_status: @response.status,
|
92
|
+
content_type: @response.content_type,
|
93
|
+
datas: {},
|
94
|
+
})
|
95
|
+
end
|
96
|
+
|
97
|
+
def save_collected_datas!
|
98
|
+
datas = {}
|
99
|
+
|
100
|
+
Rack::WebProfiler.config.collectors.all.each do |name, definition|
|
101
|
+
datas[name.to_sym] = definition.collect!(@request, @response).to_h
|
102
|
+
end
|
103
|
+
|
104
|
+
@record.datas = datas
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rack
|
2
|
+
#
|
3
|
+
class WebProfiler::Model::CollectionRecord < Sequel::Model(Rack::WebProfiler::Model.database)
|
4
|
+
# Plugins
|
5
|
+
plugin :schema
|
6
|
+
plugin :hook_class_methods
|
7
|
+
plugin :serialization
|
8
|
+
plugin :timestamps
|
9
|
+
|
10
|
+
# Attributes:
|
11
|
+
# - id
|
12
|
+
# - token
|
13
|
+
# - ip
|
14
|
+
# - url
|
15
|
+
# - http_method
|
16
|
+
# - http_status
|
17
|
+
# - content_type
|
18
|
+
# - datas
|
19
|
+
# - created_at
|
20
|
+
set_schema do
|
21
|
+
primary_key :id
|
22
|
+
|
23
|
+
varchar :token, unique: true, empty: false
|
24
|
+
varchar :url, empty: false
|
25
|
+
varchar :ip, empty: false
|
26
|
+
varchar :http_method, empty: false
|
27
|
+
integer :http_status
|
28
|
+
varchar :content_type
|
29
|
+
string :datas, text: true, empty: false
|
30
|
+
datetime :created_at
|
31
|
+
end
|
32
|
+
create_table unless table_exists?
|
33
|
+
|
34
|
+
# Serialization
|
35
|
+
serialize_attributes :marshal, :datas
|
36
|
+
|
37
|
+
# Hooks
|
38
|
+
before_create :before_create
|
39
|
+
|
40
|
+
def before_create
|
41
|
+
token = Time.now.to_f.to_s.delete(".").to_i
|
42
|
+
self.token = token.to_s(36)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "sequel"
|
2
|
+
|
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
|
15
|
+
|
16
|
+
# Remove the database content.
|
17
|
+
def clean
|
18
|
+
return unless ::File.exist?(db_file_path)
|
19
|
+
|
20
|
+
::File.delete(db_file_path)
|
21
|
+
@db = nil
|
22
|
+
@db_file_path = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
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")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Rack
|
2
|
+
# Router
|
3
|
+
#
|
4
|
+
# Show WebProfiler page if the request path match with one
|
5
|
+
# of the webprofiler routes.
|
6
|
+
class WebProfiler::Router
|
7
|
+
BASE_PATH = "/_rwpt".freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Get response for given request.
|
11
|
+
#
|
12
|
+
# @param request [Rack::WebProfiler::Request]
|
13
|
+
#
|
14
|
+
# @return [Rack::Reponse, false]
|
15
|
+
def response_for(request)
|
16
|
+
@request = request
|
17
|
+
path = Rack::Utils.unescape(request.path_info)
|
18
|
+
|
19
|
+
# Stop process if the request path does not start
|
20
|
+
# by the BASE_PATH
|
21
|
+
return false unless path.start_with?(BASE_PATH)
|
22
|
+
|
23
|
+
path.slice!(BASE_PATH)
|
24
|
+
|
25
|
+
route(request, path)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Route!
|
29
|
+
def route(request, path)
|
30
|
+
controller = WebProfiler::Controller.new(request)
|
31
|
+
|
32
|
+
if request.get? && path =~ %r{^\/toolbar\/([a-z0-9]*)(\/)?$}
|
33
|
+
controller.show_toolbar(Regexp.last_match(1))
|
34
|
+
elsif request.get? && path =~ %r{^\/clean(\/)?$}
|
35
|
+
controller.delete
|
36
|
+
elsif request.get? && path =~ %r{^(\/)?$}
|
37
|
+
controller.index
|
38
|
+
elsif request.get? && path =~ %r{^\/([a-z0-9]*)(\/)?$}
|
39
|
+
controller.show(Regexp.last_match(1))
|
40
|
+
else
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get url for toobar.
|
46
|
+
#
|
47
|
+
# @param token [String]
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
def url_for_toolbar(token)
|
51
|
+
"#{@request.script_name}#{BASE_PATH}/toolbar/#{token}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get url for the webprofiler.
|
55
|
+
#
|
56
|
+
# @param token [String, nil]
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
def url_for_profiler(token = nil)
|
60
|
+
"#{@request.script_name}#{BASE_PATH}/#{token}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get url to clean webprofiler.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
def url_for_clean_profiler
|
67
|
+
"#{@request.script_name}#{BASE_PATH}/clean"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|