rack-webprofiler 0.1.0.pre.alpha1
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 +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
|