gentooboontoo-rack-bug 0.3.0.edge
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.
- data/.gitignore +3 -0
- data/History.txt +46 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.md +120 -0
- data/Rakefile +23 -0
- data/Thorfile +113 -0
- data/lib/rack/bug/autoloading.rb +25 -0
- data/lib/rack/bug/filtered_backtrace.rb +38 -0
- data/lib/rack/bug/options.rb +89 -0
- data/lib/rack/bug/panel.rb +50 -0
- data/lib/rack/bug/panel_app.rb +33 -0
- data/lib/rack/bug/panels/active_record_panel/activerecord_extensions.rb +18 -0
- data/lib/rack/bug/panels/active_record_panel.rb +45 -0
- data/lib/rack/bug/panels/cache_panel/dalli_extension.rb +16 -0
- data/lib/rack/bug/panels/cache_panel/memcache_extension.rb +129 -0
- data/lib/rack/bug/panels/cache_panel/panel_app.rb +48 -0
- data/lib/rack/bug/panels/cache_panel/stats.rb +97 -0
- data/lib/rack/bug/panels/cache_panel.rb +51 -0
- data/lib/rack/bug/panels/log_panel/logger_extension.rb +24 -0
- data/lib/rack/bug/panels/log_panel.rb +56 -0
- data/lib/rack/bug/panels/memory_panel.rb +27 -0
- data/lib/rack/bug/panels/mongo_panel/mongo_extension.rb +27 -0
- data/lib/rack/bug/panels/mongo_panel/stats.rb +48 -0
- data/lib/rack/bug/panels/mongo_panel.rb +44 -0
- data/lib/rack/bug/panels/rails_info_panel.rb +23 -0
- data/lib/rack/bug/panels/redis_panel/redis_extension.rb +28 -0
- data/lib/rack/bug/panels/redis_panel/stats.rb +52 -0
- data/lib/rack/bug/panels/redis_panel.rb +44 -0
- data/lib/rack/bug/panels/request_variables_panel.rb +52 -0
- data/lib/rack/bug/panels/sphinx_panel/sphinx_extension.rb +25 -0
- data/lib/rack/bug/panels/sphinx_panel/stats.rb +96 -0
- data/lib/rack/bug/panels/sphinx_panel.rb +44 -0
- data/lib/rack/bug/panels/sql_panel/panel_app.rb +37 -0
- data/lib/rack/bug/panels/sql_panel/query.rb +63 -0
- data/lib/rack/bug/panels/sql_panel/sql_extension.rb +11 -0
- data/lib/rack/bug/panels/sql_panel.rb +55 -0
- data/lib/rack/bug/panels/templates_panel/actionview_extension.rb +12 -0
- data/lib/rack/bug/panels/templates_panel/rendering.rb +67 -0
- data/lib/rack/bug/panels/templates_panel/trace.rb +34 -0
- data/lib/rack/bug/panels/templates_panel.rb +47 -0
- data/lib/rack/bug/panels/timer_panel.rb +40 -0
- data/lib/rack/bug/params_signature.rb +63 -0
- data/lib/rack/bug/public/__rack_bug__/bookmarklet.html +10 -0
- data/lib/rack/bug/public/__rack_bug__/bookmarklet.js +217 -0
- data/lib/rack/bug/public/__rack_bug__/bug.css +220 -0
- data/lib/rack/bug/public/__rack_bug__/bug.js +84 -0
- data/lib/rack/bug/public/__rack_bug__/jquery-1.3.2.js +4376 -0
- data/lib/rack/bug/public/__rack_bug__/jquery.tablesorter.min.js +1 -0
- data/lib/rack/bug/public/__rack_bug__/spinner.gif +0 -0
- data/lib/rack/bug/rack_static_bug_avoider.rb +16 -0
- data/lib/rack/bug/redirect_interceptor.rb +27 -0
- data/lib/rack/bug/render.rb +67 -0
- data/lib/rack/bug/toolbar.rb +64 -0
- data/lib/rack/bug/views/error.html.erb +16 -0
- data/lib/rack/bug/views/panels/active_record.html.erb +17 -0
- data/lib/rack/bug/views/panels/cache.html.erb +93 -0
- data/lib/rack/bug/views/panels/execute_sql.html.erb +38 -0
- data/lib/rack/bug/views/panels/explain_sql.html.erb +38 -0
- data/lib/rack/bug/views/panels/log.html.erb +21 -0
- data/lib/rack/bug/views/panels/mongo.html.erb +32 -0
- data/lib/rack/bug/views/panels/profile_sql.html.erb +38 -0
- data/lib/rack/bug/views/panels/rails_info.html.erb +19 -0
- data/lib/rack/bug/views/panels/redis.html.erb +46 -0
- data/lib/rack/bug/views/panels/request_variables.html.erb +29 -0
- data/lib/rack/bug/views/panels/sphinx.html.erb +32 -0
- data/lib/rack/bug/views/panels/sql.html.erb +41 -0
- data/lib/rack/bug/views/panels/templates.html.erb +7 -0
- data/lib/rack/bug/views/panels/timer.html.erb +19 -0
- data/lib/rack/bug/views/panels/view_cache.html.erb +19 -0
- data/lib/rack/bug/views/redirect.html.erb +16 -0
- data/lib/rack/bug/views/toolbar.html.erb +42 -0
- data/lib/rack/bug.rb +82 -0
- data/rack-bug.gemspec +155 -0
- data/spec/custom_matchers.rb +21 -0
- data/spec/fixtures/config.ru +8 -0
- data/spec/fixtures/dummy_panel.rb +2 -0
- data/spec/fixtures/sample_app.rb +46 -0
- data/spec/rack/bug/panels/active_record_panel_spec.rb +30 -0
- data/spec/rack/bug/panels/cache_panel_spec.rb +167 -0
- data/spec/rack/bug/panels/log_panel_spec.rb +43 -0
- data/spec/rack/bug/panels/memory_panel_spec.rb +22 -0
- data/spec/rack/bug/panels/mongo_panel_spec.rb +51 -0
- data/spec/rack/bug/panels/rails_info_panel_spec.rb +40 -0
- data/spec/rack/bug/panels/redis_panel_spec.rb +69 -0
- data/spec/rack/bug/panels/sql_panel_spec.rb +146 -0
- data/spec/rack/bug/panels/templates_panel_spec.rb +71 -0
- data/spec/rack/bug/panels/timer_panel_spec.rb +38 -0
- data/spec/rack/bug_spec.rb +137 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +44 -0
- metadata +245 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
|
4
|
+
class RequestVariablesPanel < Panel
|
5
|
+
|
6
|
+
def name
|
7
|
+
"request_variables"
|
8
|
+
end
|
9
|
+
|
10
|
+
def before(env)
|
11
|
+
@env = env
|
12
|
+
end
|
13
|
+
|
14
|
+
def heading
|
15
|
+
"Rack Env"
|
16
|
+
end
|
17
|
+
|
18
|
+
def content
|
19
|
+
sections = {}
|
20
|
+
sections["GET"] = sort(@request.GET) if @request.GET.any?
|
21
|
+
sections["POST"] = sort(@request.GET) if @request.POST.any?
|
22
|
+
sections["Session"] = sort(@request.env["rack.session"]) if @request.env["rack.session"] && @request.env["rack.session"].any?
|
23
|
+
sections["Cookies"] = sort(@request.env["rack.request.cookie_hash"]) if @request.env["rack.request.cookie_hash"] && @request.env["rack.request.cookie_hash"].any?
|
24
|
+
server, rack = split_and_filter_env(@env)
|
25
|
+
sections["SERVER VARIABLES"] = sort(server)
|
26
|
+
sections["Rack ENV"] = sort(rack)
|
27
|
+
render_template "panels/request_variables", :sections => sections
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def sort(hash)
|
32
|
+
hash.sort_by { |k, v| k.to_s }
|
33
|
+
end
|
34
|
+
|
35
|
+
def split_and_filter_env(env)
|
36
|
+
server, rack = {}, {}
|
37
|
+
env.each do |k,v|
|
38
|
+
if k.index("rack.") == 0
|
39
|
+
rack[k] = v
|
40
|
+
elsif k.index("rack-bug.") == 0
|
41
|
+
#don't output the rack-bug variables - especially secret_key
|
42
|
+
else
|
43
|
+
server[k] = v
|
44
|
+
end
|
45
|
+
end
|
46
|
+
return server, rack
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'riddle'
|
2
|
+
|
3
|
+
if defined?(Riddle)
|
4
|
+
Riddle::Client.class_eval do
|
5
|
+
def request_with_rack_bug(command, messages)
|
6
|
+
Rack::Bug::SphinxPanel.record(command, messages) do
|
7
|
+
request_without_rack_bug(command, messages)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :request, :rack_bug
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if defined?(Sphinx::Client)
|
16
|
+
Sphinx::Client.class_eval do
|
17
|
+
def PerformRequest_with_rack_bug(command, request, additional = nil)
|
18
|
+
Rack::Bug::SphinxPanel.record(command, request) do
|
19
|
+
PerformRequest_without_rack_bug(command, request, additional)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method_chain :PerformRequest, :rack_bug
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
class SphinxPanel
|
4
|
+
|
5
|
+
class Stats
|
6
|
+
class Query
|
7
|
+
attr_reader :time
|
8
|
+
attr_reader :command
|
9
|
+
|
10
|
+
def initialize(time, *command_args)
|
11
|
+
@time = time
|
12
|
+
if command_args.flatten.first == :search
|
13
|
+
@command = "search: " + decode_message(command_args.first.flatten.last).collect{|k,v| "#{k} => #{v}"}.join(", ")
|
14
|
+
else
|
15
|
+
@command = command_args.flatten.first.to_s + ": No more info is available for this Sphinx request type"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def display_time
|
20
|
+
"%.2fms" % time
|
21
|
+
end
|
22
|
+
|
23
|
+
def decode_message(m)
|
24
|
+
@m = m.clone
|
25
|
+
params = ActiveSupport::OrderedHash.new
|
26
|
+
|
27
|
+
params[:offset] = consume_int
|
28
|
+
params[:limit] = consume_int
|
29
|
+
params[:match_mode] = consume_int
|
30
|
+
params[:rank_mode] = consume_int
|
31
|
+
params[:sort_mode] = consume_int
|
32
|
+
params[:sort_by] = consume_string
|
33
|
+
params[:query] = consume_string
|
34
|
+
wl = consume_int
|
35
|
+
weights = []
|
36
|
+
wl.times do weights << consume_int end
|
37
|
+
params[:weights] = weights
|
38
|
+
|
39
|
+
params[:index] = consume_string
|
40
|
+
|
41
|
+
consume_string
|
42
|
+
|
43
|
+
params[:id_range] = [consume_64int, consume_64int]
|
44
|
+
params
|
45
|
+
end
|
46
|
+
|
47
|
+
def consume_int
|
48
|
+
i = @m.unpack("N").first
|
49
|
+
@m = @m.slice(4, @m.length - 4)
|
50
|
+
i
|
51
|
+
end
|
52
|
+
|
53
|
+
def consume_64int
|
54
|
+
i = @m.unpack("NN").first
|
55
|
+
@m = @m.slice(8, @m.length - 8)
|
56
|
+
i
|
57
|
+
end
|
58
|
+
|
59
|
+
def consume_string
|
60
|
+
len = consume_int
|
61
|
+
s = @m.slice(0, len)
|
62
|
+
@m = @m.slice(len, @m.length - len)
|
63
|
+
s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_reader :calls
|
68
|
+
attr_reader :queries
|
69
|
+
|
70
|
+
def initialize
|
71
|
+
@queries = []
|
72
|
+
@calls = 0
|
73
|
+
@time = 0.0
|
74
|
+
end
|
75
|
+
|
76
|
+
def record_call(time, *command_args)
|
77
|
+
|
78
|
+
@queries << Query.new(time, command_args)
|
79
|
+
@calls += 1
|
80
|
+
@time += time
|
81
|
+
end
|
82
|
+
|
83
|
+
def display_time
|
84
|
+
"%.2fms" % time
|
85
|
+
end
|
86
|
+
|
87
|
+
def time
|
88
|
+
@queries.inject(0) do |memo, query|
|
89
|
+
memo + query.time
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
|
4
|
+
class SphinxPanel < Panel
|
5
|
+
require "rack/bug/panels/sphinx_panel/sphinx_extension"
|
6
|
+
|
7
|
+
autoload :Stats, "rack/bug/panels/sphinx_panel/stats"
|
8
|
+
|
9
|
+
def self.record(*sphinx_command_args, &block)
|
10
|
+
return block.call unless Rack::Bug.enabled?
|
11
|
+
|
12
|
+
start_time = Time.now
|
13
|
+
result = block.call
|
14
|
+
total_time = Time.now - start_time
|
15
|
+
stats.record_call(total_time * 1_000, sphinx_command_args)
|
16
|
+
return result
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reset
|
20
|
+
Thread.current["rack.bug.sphinx"] = Stats.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.stats
|
24
|
+
Thread.current["rack.bug.sphinx"] ||= Stats.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
"sphinx"
|
29
|
+
end
|
30
|
+
|
31
|
+
def heading
|
32
|
+
"Sphinx: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
|
33
|
+
end
|
34
|
+
|
35
|
+
def content
|
36
|
+
result = render_template "panels/sphinx", :stats => self.class.stats
|
37
|
+
self.class.reset
|
38
|
+
return result
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
class SQLPanel
|
4
|
+
|
5
|
+
class PanelApp < ::Rack::Bug::PanelApp
|
6
|
+
|
7
|
+
def dispatch
|
8
|
+
case request.path_info
|
9
|
+
when "/__rack_bug__/explain_sql" then explain_sql
|
10
|
+
when "/__rack_bug__/profile_sql" then profile_sql
|
11
|
+
when "/__rack_bug__/execute_sql" then execute_sql
|
12
|
+
else not_found
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def explain_sql
|
17
|
+
validate_params
|
18
|
+
query = Query.new(params["query"], params["time"].to_f)
|
19
|
+
render_template "panels/explain_sql", :result => query.explain, :query => query.sql, :time => query.time
|
20
|
+
end
|
21
|
+
|
22
|
+
def profile_sql
|
23
|
+
validate_params
|
24
|
+
query = Query.new(params["query"], params["time"].to_f)
|
25
|
+
render_template "panels/profile_sql", :result => query.profile, :query => query.sql, :time => query.time
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute_sql
|
29
|
+
validate_params
|
30
|
+
query = Query.new(params["query"], params["time"].to_f)
|
31
|
+
render_template "panels/execute_sql", :result => query.execute, :query => query.sql, :time => query.time
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
class SQLPanel
|
4
|
+
|
5
|
+
class Query
|
6
|
+
include Rack::Bug::FilteredBacktrace
|
7
|
+
|
8
|
+
attr_reader :sql
|
9
|
+
attr_reader :time
|
10
|
+
|
11
|
+
def initialize(sql, time, backtrace = [])
|
12
|
+
@sql = sql
|
13
|
+
@time = time
|
14
|
+
@backtrace = backtrace
|
15
|
+
end
|
16
|
+
|
17
|
+
def human_time
|
18
|
+
"%.2fms" % (@time * 1_000)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspectable?
|
22
|
+
sql.strip =~ /^SELECT /i
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_profiling
|
26
|
+
self.class.execute("SET PROFILING=1")
|
27
|
+
result = yield
|
28
|
+
self.class.execute("SET PROFILING=0")
|
29
|
+
return result
|
30
|
+
end
|
31
|
+
|
32
|
+
def explain
|
33
|
+
self.class.execute "EXPLAIN #{@sql}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def profile
|
37
|
+
with_profiling do
|
38
|
+
execute
|
39
|
+
self.class.execute <<-SQL
|
40
|
+
SELECT *
|
41
|
+
FROM information_schema.profiling
|
42
|
+
WHERE query_id = (SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)
|
43
|
+
SQL
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute
|
48
|
+
self.class.execute(@sql)
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid_hash?(secret_key, possible_hash)
|
52
|
+
hash = Digest::SHA1.hexdigest [secret_key, @sql].join(":")
|
53
|
+
possible_hash == hash
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.execute(sql)
|
57
|
+
ActiveRecord::Base.connection.execute(sql)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
if defined?(ActiveRecord) && defined?(ActiveRecord::ConnectionAdapters)
|
2
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
|
3
|
+
def log_with_rack_bug(sql, name, &block)
|
4
|
+
Rack::Bug::SQLPanel.record(sql, Kernel.caller) do
|
5
|
+
log_without_rack_bug(sql, name, &block)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
alias_method_chain :log, :rack_bug
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Bug
|
5
|
+
|
6
|
+
class SQLPanel < Panel
|
7
|
+
require "rack/bug/panels/sql_panel/sql_extension"
|
8
|
+
|
9
|
+
autoload :PanelApp, "rack/bug/panels/sql_panel/panel_app"
|
10
|
+
autoload :Query, "rack/bug/panels/sql_panel/query"
|
11
|
+
|
12
|
+
def panel_app
|
13
|
+
PanelApp.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.record(sql, backtrace = [], &block)
|
17
|
+
return block.call unless Rack::Bug.enabled?
|
18
|
+
|
19
|
+
start_time = Time.now
|
20
|
+
result = block.call
|
21
|
+
queries << Query.new(sql, Time.now - start_time, backtrace)
|
22
|
+
|
23
|
+
return result
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reset
|
27
|
+
Thread.current["rack.test.queries"] = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.queries
|
31
|
+
Thread.current["rack.test.queries"] ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.total_time
|
35
|
+
(queries.inject(0) { |memo, query| memo + query.time}) * 1_000
|
36
|
+
end
|
37
|
+
|
38
|
+
def name
|
39
|
+
"sql"
|
40
|
+
end
|
41
|
+
|
42
|
+
def heading
|
43
|
+
"#{self.class.queries.size} Queries (%.2fms)" % self.class.total_time
|
44
|
+
end
|
45
|
+
|
46
|
+
def content
|
47
|
+
result = render_template "panels/sql", :queries => self.class.queries
|
48
|
+
self.class.reset
|
49
|
+
return result
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
if defined?(ActionView) && defined?(ActionView::Template)
|
2
|
+
ActionView::Template.class_eval do
|
3
|
+
|
4
|
+
def render_template_with_rack_bug(*args, &block)
|
5
|
+
Rack::Bug::TemplatesPanel.record(path_without_format_and_extension) do
|
6
|
+
render_template_without_rack_bug(*args, &block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
alias_method_chain :render_template, :rack_bug
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
class TemplatesPanel
|
4
|
+
|
5
|
+
class Rendering
|
6
|
+
attr_accessor :name
|
7
|
+
attr_accessor :start_time
|
8
|
+
attr_accessor :end_time
|
9
|
+
attr_accessor :parent
|
10
|
+
attr_reader :children
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
@children = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(rendering)
|
19
|
+
@children << rendering
|
20
|
+
rendering.parent = self
|
21
|
+
end
|
22
|
+
|
23
|
+
def time
|
24
|
+
@end_time - @start_time
|
25
|
+
end
|
26
|
+
|
27
|
+
def exclusive_time
|
28
|
+
time - child_time
|
29
|
+
end
|
30
|
+
|
31
|
+
def child_time
|
32
|
+
children.inject(0.0) { |memo, c| memo + c.time }
|
33
|
+
end
|
34
|
+
|
35
|
+
def time_summary
|
36
|
+
if children.any?
|
37
|
+
"%.2fms, %.2f exclusive" % [time * 1_000, exclusive_time * 1_000]
|
38
|
+
else
|
39
|
+
"%.2fms" % (time * 1_000)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
def html
|
43
|
+
<<-HTML
|
44
|
+
<li>
|
45
|
+
<p>#{name} (#{time_summary})</p>
|
46
|
+
|
47
|
+
#{children_html}
|
48
|
+
</li>
|
49
|
+
HTML
|
50
|
+
end
|
51
|
+
|
52
|
+
def children_html
|
53
|
+
return "" unless children.any?
|
54
|
+
|
55
|
+
<<-HTML
|
56
|
+
<ul>#{joined_children_html}</ul>
|
57
|
+
HTML
|
58
|
+
end
|
59
|
+
|
60
|
+
def joined_children_html
|
61
|
+
children.map { |c| c.html }.join
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
class TemplatesPanel
|
4
|
+
|
5
|
+
class Trace
|
6
|
+
|
7
|
+
def start(template_name)
|
8
|
+
rendering = Rendering.new(template_name)
|
9
|
+
rendering.start_time = Time.now
|
10
|
+
@current.add(rendering)
|
11
|
+
@current = rendering
|
12
|
+
end
|
13
|
+
|
14
|
+
def finished(template_name)
|
15
|
+
@current.end_time = Time.now
|
16
|
+
@current = @current.parent
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@current = root
|
21
|
+
end
|
22
|
+
|
23
|
+
def total_time
|
24
|
+
root.child_time
|
25
|
+
end
|
26
|
+
|
27
|
+
def root
|
28
|
+
@root ||= Rendering.new("root")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Rack
|
2
|
+
class Bug
|
3
|
+
|
4
|
+
class TemplatesPanel < Panel
|
5
|
+
require "rack/bug/panels/templates_panel/actionview_extension"
|
6
|
+
|
7
|
+
autoload :Trace, "rack/bug/panels/templates_panel/trace"
|
8
|
+
autoload :Rendering, "rack/bug/panels/templates_panel/rendering"
|
9
|
+
|
10
|
+
def self.record(template)
|
11
|
+
return yield unless Rack::Bug.enabled?
|
12
|
+
|
13
|
+
template_trace.start(template)
|
14
|
+
begin
|
15
|
+
result = yield
|
16
|
+
ensure
|
17
|
+
template_trace.finished(template)
|
18
|
+
end
|
19
|
+
return result
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset
|
23
|
+
Thread.current["rack.bug.template_trace"] = Trace.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.template_trace
|
27
|
+
Thread.current["rack.bug.template_trace"] ||= Trace.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
"templates"
|
32
|
+
end
|
33
|
+
|
34
|
+
def heading
|
35
|
+
"Templates: %.2fms" % (self.class.template_trace.total_time * 1_000)
|
36
|
+
end
|
37
|
+
|
38
|
+
def content
|
39
|
+
result = render_template "panels/templates", :template_trace => self.class.template_trace
|
40
|
+
self.class.reset
|
41
|
+
return result
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Bug
|
5
|
+
|
6
|
+
class TimerPanel < Panel
|
7
|
+
|
8
|
+
def name
|
9
|
+
"timer"
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
status, headers, body = nil
|
14
|
+
@times = Benchmark.measure do
|
15
|
+
status, headers, body = @app.call(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
@measurements = [
|
19
|
+
["User CPU time", "%.2fms" % (@times.utime * 1_000)],
|
20
|
+
["System CPU time", "%.2fms" % (@times.stime * 1_000)],
|
21
|
+
["Total CPU time", "%.2fms" % (@times.total * 1_000)],
|
22
|
+
["Elapsed time", "%.2fms" % (@times.real * 1_000)]
|
23
|
+
]
|
24
|
+
|
25
|
+
env["rack-bug.panels"] << self
|
26
|
+
return [status, headers, body]
|
27
|
+
end
|
28
|
+
|
29
|
+
def heading
|
30
|
+
"%.2fms" % (@times.real * 1_000)
|
31
|
+
end
|
32
|
+
|
33
|
+
def content
|
34
|
+
render_template "panels/timer", :measurements => @measurements
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Bug
|
5
|
+
|
6
|
+
class ParamsSignature
|
7
|
+
extend ERB::Util
|
8
|
+
|
9
|
+
def self.sign(request, hash)
|
10
|
+
parts = []
|
11
|
+
|
12
|
+
hash.keys.sort.each do |key|
|
13
|
+
parts << "#{key}=#{u(hash[key])}"
|
14
|
+
end
|
15
|
+
|
16
|
+
signature = new(request).signature(hash)
|
17
|
+
parts << "hash=#{u(signature)}"
|
18
|
+
|
19
|
+
parts.join("&")
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :request
|
23
|
+
|
24
|
+
def initialize(request)
|
25
|
+
@request = request
|
26
|
+
end
|
27
|
+
|
28
|
+
def secret_key
|
29
|
+
@request.env['rack-bug.secret_key']
|
30
|
+
end
|
31
|
+
|
32
|
+
def secret_key_blank?
|
33
|
+
secret_key.nil? || secret_key == ""
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate!
|
37
|
+
if secret_key_blank?
|
38
|
+
raise SecurityError.new("Missing secret key")
|
39
|
+
elsif request.params["hash"] != signature(request.params)
|
40
|
+
raise SecurityError.new("Invalid query hash.")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def signature(params)
|
45
|
+
Digest::SHA1.hexdigest(signature_base(params))
|
46
|
+
end
|
47
|
+
|
48
|
+
def signature_base(params)
|
49
|
+
signature = []
|
50
|
+
signature << secret_key
|
51
|
+
|
52
|
+
params.keys.sort.each do |key|
|
53
|
+
next if key == "hash"
|
54
|
+
signature << params[key].to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
signature.join(":")
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
</head>
|
4
|
+
<body>
|
5
|
+
<br/><br/><br/>
|
6
|
+
<a href="javascript: (function(){var script=document.createElement('script'); script.src='/__rack_bug__/bookmarklet.js'; document.getElementsByTagName('head')[0].appendChild(script);})()">
|
7
|
+
Toggle Rack::Bug
|
8
|
+
</a>
|
9
|
+
</body>
|
10
|
+
</html>
|