rack-insight 0.5.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.
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.simplecov +4 -0
- data/.travis.yml +8 -0
- data/CHANGELOG +58 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +24 -0
- data/README.md +189 -0
- data/Rakefile +27 -0
- data/TODO +7 -0
- data/lib/rack-insight.rb +1 -0
- data/lib/rack/insight.rb +19 -0
- data/lib/rack/insight/app.rb +198 -0
- data/lib/rack/insight/config.rb +30 -0
- data/lib/rack/insight/database.rb +193 -0
- data/lib/rack/insight/enable-button.rb +43 -0
- data/lib/rack/insight/filtered_backtrace.rb +45 -0
- data/lib/rack/insight/instrumentation.rb +9 -0
- data/lib/rack/insight/instrumentation/backstage.rb +10 -0
- data/lib/rack/insight/instrumentation/client.rb +20 -0
- data/lib/rack/insight/instrumentation/instrument.rb +109 -0
- data/lib/rack/insight/instrumentation/package-definition.rb +58 -0
- data/lib/rack/insight/instrumentation/probe-definition.rb +20 -0
- data/lib/rack/insight/instrumentation/probe.rb +196 -0
- data/lib/rack/insight/instrumentation/setup.rb +32 -0
- data/lib/rack/insight/logger.rb +53 -0
- data/lib/rack/insight/options.rb +116 -0
- data/lib/rack/insight/panel.rb +135 -0
- data/lib/rack/insight/panel_app.rb +31 -0
- data/lib/rack/insight/panels-content.rb +22 -0
- data/lib/rack/insight/panels-header.rb +18 -0
- data/lib/rack/insight/panels/active_record_panel.rb +46 -0
- data/lib/rack/insight/panels/active_resource_panel.rb +48 -0
- data/lib/rack/insight/panels/active_resource_panel/query.rb +27 -0
- data/lib/rack/insight/panels/cache_panel.rb +68 -0
- data/lib/rack/insight/panels/cache_panel/panel_app.rb +46 -0
- data/lib/rack/insight/panels/cache_panel/stats.rb +90 -0
- data/lib/rack/insight/panels/log_panel.rb +53 -0
- data/lib/rack/insight/panels/memory_panel.rb +36 -0
- data/lib/rack/insight/panels/mongo_panel.rb +41 -0
- data/lib/rack/insight/panels/mongo_panel/mongo_extension.rb +24 -0
- data/lib/rack/insight/panels/mongo_panel/stats.rb +46 -0
- data/lib/rack/insight/panels/rails_info_panel.rb +19 -0
- data/lib/rack/insight/panels/redis_panel.rb +42 -0
- data/lib/rack/insight/panels/redis_panel/redis_extension.rb +23 -0
- data/lib/rack/insight/panels/redis_panel/stats.rb +50 -0
- data/lib/rack/insight/panels/request_variables_panel.rb +70 -0
- data/lib/rack/insight/panels/speedtracer_panel.rb +89 -0
- data/lib/rack/insight/panels/speedtracer_panel/profiling.rb +29 -0
- data/lib/rack/insight/panels/speedtracer_panel/trace-app.rb +52 -0
- data/lib/rack/insight/panels/speedtracer_panel/tracer.rb +213 -0
- data/lib/rack/insight/panels/sphinx_panel.rb +41 -0
- data/lib/rack/insight/panels/sphinx_panel/stats.rb +94 -0
- data/lib/rack/insight/panels/sql_panel.rb +53 -0
- data/lib/rack/insight/panels/sql_panel/panel_app.rb +37 -0
- data/lib/rack/insight/panels/sql_panel/query.rb +94 -0
- data/lib/rack/insight/panels/templates_panel.rb +58 -0
- data/lib/rack/insight/panels/templates_panel/rendering.rb +81 -0
- data/lib/rack/insight/panels/timer_panel.rb +40 -0
- data/lib/rack/insight/params_signature.rb +61 -0
- data/lib/rack/insight/path-filter.rb +23 -0
- data/lib/rack/insight/public/__insight__/bookmarklet.html +10 -0
- data/lib/rack/insight/public/__insight__/bookmarklet.js +223 -0
- data/lib/rack/insight/public/__insight__/insight.css +235 -0
- data/lib/rack/insight/public/__insight__/insight.js +127 -0
- data/lib/rack/insight/public/__insight__/jquery-1.3.2.js +4376 -0
- data/lib/rack/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
- data/lib/rack/insight/public/__insight__/spinner.gif +0 -0
- data/lib/rack/insight/rack_static_bug_avoider.rb +16 -0
- data/lib/rack/insight/redirect_interceptor.rb +25 -0
- data/lib/rack/insight/render.rb +72 -0
- data/lib/rack/insight/request-recorder.rb +22 -0
- data/lib/rack/insight/rspec_matchers.rb +33 -0
- data/lib/rack/insight/toolbar.rb +69 -0
- data/lib/rack/insight/version.rb +7 -0
- data/lib/rack/insight/views/enable-button.html.erb +21 -0
- data/lib/rack/insight/views/error.html.erb +17 -0
- data/lib/rack/insight/views/headers_fragment.html.erb +20 -0
- data/lib/rack/insight/views/panels/active_record.html.erb +17 -0
- data/lib/rack/insight/views/panels/active_resource.html.erb +47 -0
- data/lib/rack/insight/views/panels/cache.html.erb +93 -0
- data/lib/rack/insight/views/panels/execute_sql.html.erb +32 -0
- data/lib/rack/insight/views/panels/explain_sql.html.erb +32 -0
- data/lib/rack/insight/views/panels/log.html.erb +21 -0
- data/lib/rack/insight/views/panels/mongo.html.erb +32 -0
- data/lib/rack/insight/views/panels/profile_sql.html.erb +32 -0
- data/lib/rack/insight/views/panels/rails_info.html.erb +19 -0
- data/lib/rack/insight/views/panels/redis.html.erb +46 -0
- data/lib/rack/insight/views/panels/request_variables.html.erb +25 -0
- data/lib/rack/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
- data/lib/rack/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
- data/lib/rack/insight/views/panels/speedtracer/traces.html.erb +18 -0
- data/lib/rack/insight/views/panels/sphinx.html.erb +32 -0
- data/lib/rack/insight/views/panels/sql.html.erb +43 -0
- data/lib/rack/insight/views/panels/templates.html.erb +6 -0
- data/lib/rack/insight/views/panels/timer.html.erb +19 -0
- data/lib/rack/insight/views/panels/view_cache.html.erb +19 -0
- data/lib/rack/insight/views/redirect.html.erb +16 -0
- data/lib/rack/insight/views/request_fragment.html.erb +25 -0
- data/lib/rack/insight/views/toolbar.html.erb +29 -0
- data/rack-insight.gemspec +40 -0
- data/spec/custom_matchers.rb +0 -0
- data/spec/fixtures/config.ru +8 -0
- data/spec/fixtures/dummy_panel.rb +2 -0
- data/spec/fixtures/sample_app.rb +72 -0
- data/spec/fixtures/star_trek_panel.rb +1 -0
- data/spec/insight_spec.rb +163 -0
- data/spec/instrumentation_spec.rb +188 -0
- data/spec/rack/insight/config_spec.rb +20 -0
- data/spec/rack/insight/panels/active_record_panel_spec.rb +43 -0
- data/spec/rack/insight/panels/active_resource_panel_spec.rb +40 -0
- data/spec/rack/insight/panels/cache_panel_spec.rb +178 -0
- data/spec/rack/insight/panels/log_panel_spec.rb +44 -0
- data/spec/rack/insight/panels/memory_panel_spec.rb +21 -0
- data/spec/rack/insight/panels/mongo_panel_spec_pending.rb +52 -0
- data/spec/rack/insight/panels/rails_info_panel_spec.rb +29 -0
- data/spec/rack/insight/panels/redis_panel_spec.rb +67 -0
- data/spec/rack/insight/panels/speedtracer_panel_spec.rb +86 -0
- data/spec/rack/insight/panels/sql_panel_spec.rb +146 -0
- data/spec/rack/insight/panels/templates_panel_spec.rb +86 -0
- data/spec/rack/insight/panels/timer_panel_spec.rb +38 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +111 -0
- metadata +380 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
|
|
3
|
+
class SphinxPanel < Panel
|
|
4
|
+
require "rack/insight/panels/sphinx_panel/sphinx_extension"
|
|
5
|
+
require "rack/insight/panels/sphinx_panel/stats"
|
|
6
|
+
|
|
7
|
+
def self.record(*sphinx_command_args, &block)
|
|
8
|
+
return block.call unless Rack::Insight.enabled?
|
|
9
|
+
|
|
10
|
+
start_time = Time.now
|
|
11
|
+
result = block.call
|
|
12
|
+
total_time = Time.now - start_time
|
|
13
|
+
stats.record_call(total_time * 1_000, sphinx_command_args)
|
|
14
|
+
return result
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.reset
|
|
18
|
+
Thread.current["rack-insight.sphinx"] = Stats.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.stats
|
|
22
|
+
Thread.current["rack-insight.sphinx"] ||= Stats.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def name
|
|
26
|
+
"sphinx"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def heading
|
|
30
|
+
"Sphinx: %.2fms (#{self.class.stats.queries.size} calls)" % self.class.stats.time
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def content
|
|
34
|
+
result = render_template "panels/sphinx", :stats => self.class.stats
|
|
35
|
+
self.class.reset
|
|
36
|
+
return result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
class SphinxPanel
|
|
3
|
+
|
|
4
|
+
class Stats
|
|
5
|
+
class Query
|
|
6
|
+
attr_reader :time
|
|
7
|
+
attr_reader :command
|
|
8
|
+
|
|
9
|
+
def initialize(time, *command_args)
|
|
10
|
+
@time = time
|
|
11
|
+
if command_args.flatten.first == :search
|
|
12
|
+
@command = "search: " + decode_message(command_args.first.flatten.last).collect{|k,v| "#{k} => #{v}"}.join(", ")
|
|
13
|
+
else
|
|
14
|
+
@command = command_args.flatten.first.to_s + ": No more info is available for this Sphinx request type"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def display_time
|
|
19
|
+
"%.2fms" % time
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def decode_message(m)
|
|
23
|
+
@m = m.clone
|
|
24
|
+
params = ActiveSupport::OrderedHash.new
|
|
25
|
+
|
|
26
|
+
params[:offset] = consume_int
|
|
27
|
+
params[:limit] = consume_int
|
|
28
|
+
params[:match_mode] = consume_int
|
|
29
|
+
params[:rank_mode] = consume_int
|
|
30
|
+
params[:sort_mode] = consume_int
|
|
31
|
+
params[:sort_by] = consume_string
|
|
32
|
+
params[:query] = consume_string
|
|
33
|
+
wl = consume_int
|
|
34
|
+
weights = []
|
|
35
|
+
wl.times do weights << consume_int end
|
|
36
|
+
params[:weights] = weights
|
|
37
|
+
|
|
38
|
+
params[:index] = consume_string
|
|
39
|
+
|
|
40
|
+
consume_string
|
|
41
|
+
|
|
42
|
+
params[:id_range] = [consume_64int, consume_64int]
|
|
43
|
+
params
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def consume_int
|
|
47
|
+
i = @m.unpack("N").first
|
|
48
|
+
@m = @m.slice(4, @m.length - 4)
|
|
49
|
+
i
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def consume_64int
|
|
53
|
+
i = @m.unpack("NN").first
|
|
54
|
+
@m = @m.slice(8, @m.length - 8)
|
|
55
|
+
i
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def consume_string
|
|
59
|
+
len = consume_int
|
|
60
|
+
s = @m.slice(0, len)
|
|
61
|
+
@m = @m.slice(len, @m.length - len)
|
|
62
|
+
s
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
attr_reader :calls
|
|
67
|
+
attr_reader :queries
|
|
68
|
+
|
|
69
|
+
def initialize
|
|
70
|
+
@queries = []
|
|
71
|
+
@calls = 0
|
|
72
|
+
@time = 0.0
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def record_call(time, *command_args)
|
|
76
|
+
|
|
77
|
+
@queries << Query.new(time, command_args)
|
|
78
|
+
@calls += 1
|
|
79
|
+
@time += time
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def display_time
|
|
83
|
+
"%.2fms" % time
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def time
|
|
87
|
+
@queries.inject(0) do |memo, query|
|
|
88
|
+
memo + query.time
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
|
|
3
|
+
module Rack::Insight
|
|
4
|
+
|
|
5
|
+
class SQLPanel < Panel
|
|
6
|
+
|
|
7
|
+
require "rack/insight/panels/sql_panel/panel_app"
|
|
8
|
+
require "rack/insight/panels/sql_panel/query"
|
|
9
|
+
|
|
10
|
+
def initialize(app)
|
|
11
|
+
super
|
|
12
|
+
probe(self) do
|
|
13
|
+
%w{ PostgreSQLAdapter MysqlAdapter SQLiteAdapter
|
|
14
|
+
Mysql2Adapter OracleEnhancedAdapter }.each do |adapter|
|
|
15
|
+
instrument "ActiveRecord::ConnectionAdapters::#{adapter}" do
|
|
16
|
+
instance_probe :execute
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
table_setup("sql_queries")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.panel_mappings
|
|
24
|
+
{ "sql" => PanelApp.new }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def after_detect(method_call, timing, arguments, results)
|
|
28
|
+
store(@env, QueryResult.new(arguments.first, timing.duration, method_call.backtrace, results))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def total_time(queries)
|
|
32
|
+
(queries.inject(0) do |memo, query|
|
|
33
|
+
memo + query.time
|
|
34
|
+
end)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def name
|
|
38
|
+
"sql"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def heading_for_request(number)
|
|
42
|
+
queries = retrieve(number)
|
|
43
|
+
"#{queries.size} Queries (%.2fms)" % total_time(queries)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def content_for_request(number)
|
|
47
|
+
queries = retrieve(number)
|
|
48
|
+
render_template "panels/sql", :queries => queries
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'rack/insight/panels/sql_panel/query'
|
|
2
|
+
|
|
3
|
+
module Rack::Insight
|
|
4
|
+
class SQLPanel
|
|
5
|
+
|
|
6
|
+
class PanelApp < ::Rack::Insight::PanelApp
|
|
7
|
+
|
|
8
|
+
def dispatch
|
|
9
|
+
case request.path_info
|
|
10
|
+
when "/explain" then explain_sql
|
|
11
|
+
when "/profile" then profile_sql
|
|
12
|
+
when "/execute" then execute_sql
|
|
13
|
+
else not_found
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def explain_sql
|
|
18
|
+
validate_params
|
|
19
|
+
query = ExplainResult.new(params["query"], params["time"].to_f)
|
|
20
|
+
render_template "panels/explain_sql", :query => query
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def profile_sql
|
|
24
|
+
validate_params
|
|
25
|
+
query = ProfileResult.new(params["query"], params["time"].to_f)
|
|
26
|
+
render_template "panels/profile_sql", :query => query
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def execute_sql
|
|
30
|
+
validate_params
|
|
31
|
+
query = QueryResult.new(params["query"], params["time"].to_f)
|
|
32
|
+
render_template "panels/execute_sql", :query => query
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
class SQLPanel
|
|
3
|
+
|
|
4
|
+
class QueryResult
|
|
5
|
+
include Rack::Insight::FilteredBacktrace
|
|
6
|
+
|
|
7
|
+
attr_reader :sql
|
|
8
|
+
attr_reader :time
|
|
9
|
+
|
|
10
|
+
def initialize(sql, time, backtrace = [], result=nil)
|
|
11
|
+
@sql = sql
|
|
12
|
+
@time = time
|
|
13
|
+
@backtrace = backtrace
|
|
14
|
+
@result = result
|
|
15
|
+
@results = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def result
|
|
19
|
+
@results ||= execute
|
|
20
|
+
return @results
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def column_names
|
|
24
|
+
if result.respond_to?(:fields)
|
|
25
|
+
return result.fields
|
|
26
|
+
else
|
|
27
|
+
return result.fetch_fields.map{|col| col.name}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def rows
|
|
32
|
+
if result.respond_to?(:values)
|
|
33
|
+
result.values
|
|
34
|
+
else
|
|
35
|
+
result.map do |row|
|
|
36
|
+
row
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def human_time
|
|
42
|
+
"%.2fms" % (@time)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def inspectable?
|
|
46
|
+
sql.strip =~ /^SELECT /i
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Downside is: we re-execute the SQL...
|
|
50
|
+
def self.execute(sql)
|
|
51
|
+
ActiveRecord::Base.connection.execute(sql)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute
|
|
55
|
+
self.class.execute(@sql)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def valid_hash?(secret_key, possible_hash)
|
|
59
|
+
hash = Digest::SHA1.hexdigest [secret_key, @sql].join(":")
|
|
60
|
+
possible_hash == hash
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class ExplainResult < QueryResult
|
|
65
|
+
def execute
|
|
66
|
+
self.class.execute "EXPLAIN #{@sql}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class ProfileResult < QueryResult
|
|
71
|
+
def with_profiling
|
|
72
|
+
result = nil
|
|
73
|
+
begin
|
|
74
|
+
self.class.execute("SET PROFILING=1")
|
|
75
|
+
result = yield
|
|
76
|
+
ensure
|
|
77
|
+
self.class.execute("SET PROFILING=0")
|
|
78
|
+
end
|
|
79
|
+
return result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def execute
|
|
83
|
+
with_profiling do
|
|
84
|
+
super
|
|
85
|
+
self.class.execute <<-SQL
|
|
86
|
+
SELECT *
|
|
87
|
+
FROM information_schema.profiling
|
|
88
|
+
WHERE query_id = (SELECT query_id FROM information_schema.profiling ORDER BY query_id DESC LIMIT 1)
|
|
89
|
+
SQL
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
|
|
3
|
+
class TemplatesPanel < Panel
|
|
4
|
+
require "rack/insight/panels/templates_panel/rendering"
|
|
5
|
+
|
|
6
|
+
def initialize(app)
|
|
7
|
+
super
|
|
8
|
+
|
|
9
|
+
probe(self) do
|
|
10
|
+
instrument "ActionView::Template" do
|
|
11
|
+
instance_probe :render
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
table_setup("templates")
|
|
16
|
+
|
|
17
|
+
@current = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def request_start(env, start)
|
|
22
|
+
@current = Rendering.new("root")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def request_finish(env, status, headers, body, timing)
|
|
26
|
+
store(env, @current)
|
|
27
|
+
@current = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def before_detect(method_call, args)
|
|
31
|
+
template_name = method_call.object.virtual_path
|
|
32
|
+
|
|
33
|
+
rendering = Rendering.new(template_name)
|
|
34
|
+
@current.add(rendering)
|
|
35
|
+
@current = rendering
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def after_detect(method_call, timing, args, result)
|
|
39
|
+
@current.timing = timing
|
|
40
|
+
@current = @current.parent
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def name
|
|
44
|
+
"templates"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def heading_for_request(number)
|
|
48
|
+
"Templates: %.2fms" % (retrieve(number).inject(0.0){|memo, rendering| memo + rendering.duration})
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def content_for_request(number)
|
|
52
|
+
result = render_template "panels/templates", :root_rendering => retrieve(number).first
|
|
53
|
+
return result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
class TemplatesPanel
|
|
3
|
+
|
|
4
|
+
class Rendering
|
|
5
|
+
attr_accessor :name
|
|
6
|
+
attr_accessor :parent
|
|
7
|
+
attr_accessor :timing
|
|
8
|
+
attr_reader :children
|
|
9
|
+
|
|
10
|
+
def initialize(name, timing = nil)
|
|
11
|
+
@name = name
|
|
12
|
+
@timing = timing
|
|
13
|
+
@children = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def start_time
|
|
17
|
+
@timing.start
|
|
18
|
+
end
|
|
19
|
+
alias_method :time, :start_time
|
|
20
|
+
|
|
21
|
+
def end_time
|
|
22
|
+
@timing.end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add(rendering)
|
|
26
|
+
@children << rendering
|
|
27
|
+
rendering.parent = self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def delete(rendering)
|
|
31
|
+
@children.delete(rendering)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def duration
|
|
35
|
+
if @timing
|
|
36
|
+
@timing.duration
|
|
37
|
+
else
|
|
38
|
+
child_duration
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def exclusive_duration
|
|
43
|
+
duration - child_duration
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def child_duration
|
|
47
|
+
children.inject(0.0) { |memo, c| memo + c.duration }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def duration_summary
|
|
51
|
+
if children.any?
|
|
52
|
+
"%.2fms, %.2f exclusive" % [duration, exclusive_duration]
|
|
53
|
+
else
|
|
54
|
+
"%.2fms" % (duration)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
def html
|
|
58
|
+
<<-HTML
|
|
59
|
+
<li>
|
|
60
|
+
<p>#{name} (#{duration_summary})</p>
|
|
61
|
+
|
|
62
|
+
#{children_html}
|
|
63
|
+
</li>
|
|
64
|
+
HTML
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def children_html
|
|
68
|
+
return "" unless children.any?
|
|
69
|
+
|
|
70
|
+
<<-HTML
|
|
71
|
+
<ul>#{joined_children_html}</ul>
|
|
72
|
+
HTML
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def joined_children_html
|
|
76
|
+
children.map { |c| c.html }.join
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|