logical-insight 0.4.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/History.txt +45 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.md +123 -0
- data/Rakefile +24 -0
- data/Thorfile +113 -0
- data/lib/insight.rb +17 -0
- data/lib/insight/app.rb +189 -0
- data/lib/insight/database.rb +186 -0
- data/lib/insight/enable-button.rb +43 -0
- data/lib/insight/filtered_backtrace.rb +45 -0
- data/lib/insight/instrumentation.rb +9 -0
- data/lib/insight/instrumentation/backstage.rb +10 -0
- data/lib/insight/instrumentation/client.rb +20 -0
- data/lib/insight/instrumentation/instrument.rb +109 -0
- data/lib/insight/instrumentation/package-definition.rb +58 -0
- data/lib/insight/instrumentation/probe-definition.rb +20 -0
- data/lib/insight/instrumentation/probe.rb +199 -0
- data/lib/insight/instrumentation/setup.rb +32 -0
- data/lib/insight/logger.rb +55 -0
- data/lib/insight/options.rb +102 -0
- data/lib/insight/panel.rb +119 -0
- data/lib/insight/panel_app.rb +31 -0
- data/lib/insight/panels-content.rb +22 -0
- data/lib/insight/panels-header.rb +18 -0
- data/lib/insight/panels/active_record_panel.rb +46 -0
- data/lib/insight/panels/cache_panel.rb +69 -0
- data/lib/insight/panels/cache_panel/panel_app.rb +46 -0
- data/lib/insight/panels/cache_panel/stats.rb +98 -0
- data/lib/insight/panels/log_panel.rb +54 -0
- data/lib/insight/panels/memory_panel.rb +32 -0
- data/lib/insight/panels/rails_info_panel.rb +19 -0
- data/lib/insight/panels/redis_panel.rb +42 -0
- data/lib/insight/panels/redis_panel/redis_extension.rb +23 -0
- data/lib/insight/panels/redis_panel/stats.rb +50 -0
- data/lib/insight/panels/request_variables_panel.rb +70 -0
- data/lib/insight/panels/speedtracer_panel.rb +89 -0
- data/lib/insight/panels/speedtracer_panel/trace-app.rb +52 -0
- data/lib/insight/panels/speedtracer_panel/tracer.rb +212 -0
- data/lib/insight/panels/sql_panel.rb +53 -0
- data/lib/insight/panels/sql_panel/panel_app.rb +37 -0
- data/lib/insight/panels/sql_panel/query.rb +94 -0
- data/lib/insight/panels/templates_panel.rb +58 -0
- data/lib/insight/panels/templates_panel/rendering.rb +81 -0
- data/lib/insight/panels/timer_panel.rb +40 -0
- data/lib/insight/params_signature.rb +61 -0
- data/lib/insight/public/__insight__/bookmarklet.html +10 -0
- data/lib/insight/public/__insight__/bookmarklet.js +223 -0
- data/lib/insight/public/__insight__/insight.css +235 -0
- data/lib/insight/public/__insight__/insight.js +123 -0
- data/lib/insight/public/__insight__/jquery-1.3.2.js +4376 -0
- data/lib/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
- data/lib/insight/public/__insight__/spinner.gif +0 -0
- data/lib/insight/rack_static_bug_avoider.rb +16 -0
- data/lib/insight/redirect_interceptor.rb +25 -0
- data/lib/insight/render.rb +72 -0
- data/lib/insight/request-recorder.rb +23 -0
- data/lib/insight/toolbar.rb +63 -0
- data/lib/insight/views/enable-button.html.erb +1 -0
- data/lib/insight/views/error.html.erb +17 -0
- data/lib/insight/views/headers_fragment.html.erb +20 -0
- data/lib/insight/views/panels/active_record.html.erb +17 -0
- data/lib/insight/views/panels/cache.html.erb +93 -0
- data/lib/insight/views/panels/execute_sql.html.erb +32 -0
- data/lib/insight/views/panels/explain_sql.html.erb +32 -0
- data/lib/insight/views/panels/log.html.erb +21 -0
- data/lib/insight/views/panels/profile_sql.html.erb +32 -0
- data/lib/insight/views/panels/rails_info.html.erb +19 -0
- data/lib/insight/views/panels/redis.html.erb +46 -0
- data/lib/insight/views/panels/request_variables.html.erb +25 -0
- data/lib/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
- data/lib/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
- data/lib/insight/views/panels/speedtracer/traces.html.erb +18 -0
- data/lib/insight/views/panels/sql.html.erb +43 -0
- data/lib/insight/views/panels/templates.html.erb +6 -0
- data/lib/insight/views/panels/timer.html.erb +19 -0
- data/lib/insight/views/panels/view_cache.html.erb +19 -0
- data/lib/insight/views/redirect.html.erb +16 -0
- data/lib/insight/views/request_fragment.html.erb +25 -0
- data/lib/insight/views/toolbar.html.erb +29 -0
- data/lib/logical-insight.rb +1 -0
- data/spec/custom_matchers.rb +31 -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/insight/panels/active_record_panel_spec.rb +42 -0
- data/spec/insight/panels/cache_panel_spec.rb +176 -0
- data/spec/insight/panels/log_panel_spec.rb +44 -0
- data/spec/insight/panels/memory_panel_spec.rb +19 -0
- data/spec/insight/panels/mongo_panel_spec_pending.rb +50 -0
- data/spec/insight/panels/rails_info_panel_spec.rb +27 -0
- data/spec/insight/panels/redis_panel_spec.rb +66 -0
- data/spec/insight/panels/sql_panel_spec.rb +145 -0
- data/spec/insight/panels/templates_panel_spec.rb +84 -0
- data/spec/insight/panels/timer_panel_spec.rb +36 -0
- data/spec/insight_spec.rb +141 -0
- data/spec/instrumentation_spec.rb +188 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +93 -0
- metadata +187 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Insight::Instrumentation
|
|
2
|
+
class ProbeDefinition
|
|
3
|
+
def initialize(package, target_name)
|
|
4
|
+
@package = package
|
|
5
|
+
@target_name = target_name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def instance_probe(*method_names)
|
|
9
|
+
if probes = @package.get_instance_probe(@target_name)
|
|
10
|
+
probes.probe(@package.collector, *method_names)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def class_probe(*method_names)
|
|
15
|
+
if probes = @package.get_class_probe(@target_name)
|
|
16
|
+
probes.probe(@package.collector, *method_names)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
module Instrumentation
|
|
3
|
+
class Probe
|
|
4
|
+
module Interpose
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
@@class_list = nil
|
|
8
|
+
|
|
9
|
+
module ProbeRunner
|
|
10
|
+
include Backstage
|
|
11
|
+
include Logging
|
|
12
|
+
|
|
13
|
+
def probe_run(object, context = "::", kind=:instance, args=[], called_at=caller[1], method_name = nil)
|
|
14
|
+
if Thread.current['instrumented_backstage']
|
|
15
|
+
#warn "probe_run while backstage: #{context}, #{kind},
|
|
16
|
+
##{method_name}" unless method_name.to_sym == :add
|
|
17
|
+
return yield
|
|
18
|
+
end
|
|
19
|
+
instrument = Thread.current['insight.instrument']
|
|
20
|
+
result = nil
|
|
21
|
+
if instrument.nil?
|
|
22
|
+
backstage do
|
|
23
|
+
# Rails.logger.debug{"No instrument in thread - #{context} /
|
|
24
|
+
# #{called_at}"}
|
|
25
|
+
result = yield
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
instrument.run(object, context, kind, called_at, method_name, args){ result = yield }
|
|
29
|
+
end
|
|
30
|
+
result
|
|
31
|
+
end
|
|
32
|
+
extend self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
include Logging
|
|
37
|
+
|
|
38
|
+
def class_list
|
|
39
|
+
@@class_list ||= begin
|
|
40
|
+
classes = []
|
|
41
|
+
ObjectSpace.each_object(Class) do |klass|
|
|
42
|
+
classes << klass
|
|
43
|
+
end
|
|
44
|
+
classes
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_probe_chain(name)
|
|
49
|
+
const = const_from_name(name)
|
|
50
|
+
chain = []
|
|
51
|
+
const.ancestors.each do |mod|
|
|
52
|
+
if probes.has_key?(mod.name)
|
|
53
|
+
chain << probes[mod.name]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
chain
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def const_from_name(name)
|
|
60
|
+
parts = name.split("::")
|
|
61
|
+
const = parts.inject(Kernel) do |namespace, part|
|
|
62
|
+
namespace.const_get(part)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def probes
|
|
67
|
+
@probes ||= Hash.new do |h,k|
|
|
68
|
+
begin
|
|
69
|
+
h[k] = self.new(const_from_name(k))
|
|
70
|
+
rescue NameError
|
|
71
|
+
logger.info{ "Cannot find constant: #{k}" }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def all_probes
|
|
77
|
+
probes.values
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def probe_for(const)
|
|
81
|
+
probes[const]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def initialize(const)
|
|
86
|
+
@const = const
|
|
87
|
+
@probed = {}
|
|
88
|
+
@collectors = Hash.new{|h,k| h[k] = []}
|
|
89
|
+
@probe_orders = []
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def collectors(key)
|
|
93
|
+
@collectors[key.to_sym]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def all_collectors
|
|
97
|
+
@collectors.values
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def clear_collectors
|
|
101
|
+
@collectors.clear
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def probe(collector, *methods)
|
|
105
|
+
methods.each do |name|
|
|
106
|
+
unless @collectors[name.to_sym].include?(collector)
|
|
107
|
+
@collectors[name.to_sym] << collector
|
|
108
|
+
end
|
|
109
|
+
@probe_orders << name
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def descendants
|
|
114
|
+
@descendants ||= self.class.class_list.find_all do |klass|
|
|
115
|
+
klass.ancestors.include?(@const)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def local_method_defs(klass)
|
|
120
|
+
klass.instance_methods(false)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def descendants_that_define(method_name)
|
|
124
|
+
log{{ :descendants => descendants }}
|
|
125
|
+
descendants.find_all do |klass|
|
|
126
|
+
(@const != klass and local_method_defs(klass).include?(method_name))
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
include Logging
|
|
131
|
+
def log &block
|
|
132
|
+
logger.debug &block
|
|
133
|
+
#$stderr.puts block.call.inspect
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def fulfill_probe_orders
|
|
137
|
+
log{{:probes_for => @const.name, :type => self.class}}
|
|
138
|
+
@probe_orders.each do |method_name|
|
|
139
|
+
log{{ :method => method_name }}
|
|
140
|
+
build_tracing_wrappers(@const, method_name)
|
|
141
|
+
descendants_that_define(method_name).each do |klass|
|
|
142
|
+
log{{ :subclass => klass.name }}
|
|
143
|
+
build_tracing_wrappers(klass, method_name)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
@probe_orders.clear
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def get_method(klass, method_name)
|
|
150
|
+
klass.instance_method(method_name)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def define_trace_method(target, method)
|
|
154
|
+
target.class_exec() do
|
|
155
|
+
define_method(method.name) do |*args, &block|
|
|
156
|
+
ProbeRunner::probe_run(self, method.owner.name, :instance, args, caller(0)[0], method.name) do
|
|
157
|
+
method.bind(self).call(*args, &block)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def build_tracing_wrappers(target, method_name)
|
|
164
|
+
return if @probed.has_key?([target,method_name])
|
|
165
|
+
@probed[[target,method_name]] = true
|
|
166
|
+
|
|
167
|
+
meth = get_method(target, method_name)
|
|
168
|
+
|
|
169
|
+
log{ {:tracing => meth } }
|
|
170
|
+
define_trace_method(target, meth)
|
|
171
|
+
rescue NameError => ne
|
|
172
|
+
log{ {:not_tracing => NameError } }
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
class ClassProbe < Probe
|
|
177
|
+
def local_method_defs(klass)
|
|
178
|
+
klass.singleton_methods(false)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def get_method(klass, method_name)
|
|
182
|
+
(class << klass; self; end).instance_method(method_name)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def define_trace_method(target, method)
|
|
186
|
+
(class << target; self; end).class_exec() do
|
|
187
|
+
define_method(method.name) do |*args, &block|
|
|
188
|
+
ProbeRunner::probe_run(self, target.name, :class, args, caller(0)[0], method.name) do
|
|
189
|
+
method.bind(self).call(*args, &block)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
class InstanceProbe < Probe
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Insight::Instrumentation
|
|
2
|
+
class Setup
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def setup(env)
|
|
8
|
+
instrument = Instrument.new
|
|
9
|
+
|
|
10
|
+
PackageDefinition.start
|
|
11
|
+
instrument.start(env)
|
|
12
|
+
|
|
13
|
+
env["insight.instrument"] = instrument
|
|
14
|
+
Thread::current["insight.instrument"] = instrument
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def teardown(env, status, headers, body)
|
|
18
|
+
instrument, env["insight.instrument"] = env["insight.instrument"], nil
|
|
19
|
+
instrument.finish(env, status, headers, body)
|
|
20
|
+
Thread::current["insight.instrument"] = nil
|
|
21
|
+
|
|
22
|
+
env["insight.duration"] = instrument.duration
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call(env)
|
|
26
|
+
setup(env)
|
|
27
|
+
status, headers, body = @app.call(env)
|
|
28
|
+
teardown(env, status, headers, body)
|
|
29
|
+
return [status, headers, body]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
class Logger
|
|
3
|
+
def initialize(level, path)
|
|
4
|
+
@level = level
|
|
5
|
+
@log_path = path
|
|
6
|
+
@logfile = nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_accessor :level
|
|
10
|
+
|
|
11
|
+
DEBUG = 0
|
|
12
|
+
INFO = 1
|
|
13
|
+
WARN = 2
|
|
14
|
+
ERROR = 3
|
|
15
|
+
FATAL = 4
|
|
16
|
+
UNKNOWN = 5
|
|
17
|
+
|
|
18
|
+
def log(severity, message)
|
|
19
|
+
message = message.inspect unless String === message
|
|
20
|
+
return unless severity >= @level
|
|
21
|
+
|
|
22
|
+
if defined? Rails and
|
|
23
|
+
Rails.respond_to? :logger
|
|
24
|
+
not Rails.logger.nil?
|
|
25
|
+
Rails.logger.add(severity, "[Insight]: " + message)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
logfile.puts(message)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def logfile
|
|
32
|
+
@logfile ||= File::open(@log_path, "a+")
|
|
33
|
+
rescue
|
|
34
|
+
$stderr
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def debug; log(DEBUG, yield) end
|
|
38
|
+
def info; log(INFO, yield) end
|
|
39
|
+
def warn; log(WARN, yield) end
|
|
40
|
+
def error; log(ERROR, yield) end
|
|
41
|
+
def fatal; log(FATAL, yield) end
|
|
42
|
+
def unknown; log(UNKNOWN, yield) end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Logging
|
|
46
|
+
def logger(env = nil)
|
|
47
|
+
if env.nil?
|
|
48
|
+
Thread.current['insight.logger'] ||= Logger.new(Logger::DEBUG, "")
|
|
49
|
+
else
|
|
50
|
+
env["insight.logger"]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
module_function :logger
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require 'ipaddr'
|
|
2
|
+
module Insight
|
|
3
|
+
module Options
|
|
4
|
+
class << self
|
|
5
|
+
private
|
|
6
|
+
def option_accessor(key)
|
|
7
|
+
define_method(key) { || read_option(key) }
|
|
8
|
+
define_method("#{key}=") { |value| write_option(key, value) }
|
|
9
|
+
define_method("#{key}?") { || !! read_option(key) }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
option_accessor :secret_key
|
|
14
|
+
option_accessor :ip_masks
|
|
15
|
+
option_accessor :password
|
|
16
|
+
option_accessor :panel_classes
|
|
17
|
+
option_accessor :intercept_redirects
|
|
18
|
+
|
|
19
|
+
# The underlying options Hash. During initialization (or outside of a
|
|
20
|
+
# request), this is a default values Hash. During a request, this is the
|
|
21
|
+
# Rack environment Hash. The default values Hash is merged in underneath
|
|
22
|
+
# the Rack environment before each request is processed.
|
|
23
|
+
def options
|
|
24
|
+
@env || @default_options
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Set multiple options.
|
|
28
|
+
def options=(hash={})
|
|
29
|
+
hash.each { |key,value| write_option(key, value) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Set an option. When +option+ is a Symbol, it is set in the Rack
|
|
33
|
+
# Environment as "rack-cache.option". When +option+ is a String, it
|
|
34
|
+
# exactly as specified. The +option+ argument may also be a Hash in
|
|
35
|
+
# which case each key/value pair is merged into the environment as if
|
|
36
|
+
# the #set method were called on each.
|
|
37
|
+
def set(option, value=self, &block)
|
|
38
|
+
if block_given?
|
|
39
|
+
write_option option, block
|
|
40
|
+
elsif value == self
|
|
41
|
+
self.options = option.to_hash
|
|
42
|
+
else
|
|
43
|
+
write_option option, value
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def read_option(key)
|
|
50
|
+
options[option_name(key)]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def write_option(key, value)
|
|
54
|
+
options[option_name(key)] = value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def option_name(key)
|
|
58
|
+
case key
|
|
59
|
+
when Symbol ; "insight.#{key}"
|
|
60
|
+
when String ; key
|
|
61
|
+
else raise ArgumentError
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def process_options
|
|
66
|
+
if(file_list = read_option('insight.panel_files'))
|
|
67
|
+
class_list = read_option('insight.panel_classes') || []
|
|
68
|
+
file_list.each do |file|
|
|
69
|
+
class_list |= Insight::Panel.from_file(file)
|
|
70
|
+
end
|
|
71
|
+
write_option('insight.panel_classes', class_list)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def initialize_options(options=nil)
|
|
76
|
+
@default_options = {
|
|
77
|
+
'insight.ip_masks' => [IPAddr.new("127.0.0.1")],
|
|
78
|
+
'insight.password' => nil,
|
|
79
|
+
'insight.verbose' => nil,
|
|
80
|
+
'insight.secret_key' => nil,
|
|
81
|
+
'insight.intercept_redirects' => false,
|
|
82
|
+
'insight.panels' => [],
|
|
83
|
+
'insight.log_level' => Logger::INFO,
|
|
84
|
+
'insight.log_path' => "log/insight.log",
|
|
85
|
+
'insight.panel_files' => %w{
|
|
86
|
+
rails_info_panel
|
|
87
|
+
timer_panel
|
|
88
|
+
request_variables_panel
|
|
89
|
+
sql_panel
|
|
90
|
+
active_record_panel
|
|
91
|
+
cache_panel
|
|
92
|
+
templates_panel
|
|
93
|
+
log_panel
|
|
94
|
+
memory_panel
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
self.options = options || {}
|
|
98
|
+
process_options
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require 'insight/database'
|
|
3
|
+
require 'insight/instrumentation'
|
|
4
|
+
require 'insight/render'
|
|
5
|
+
|
|
6
|
+
module Insight
|
|
7
|
+
|
|
8
|
+
# Panels are also Rack middleware
|
|
9
|
+
class Panel
|
|
10
|
+
include Render
|
|
11
|
+
include ERB::Util
|
|
12
|
+
include Database::RequestDataClient
|
|
13
|
+
include Logging
|
|
14
|
+
include Instrumentation::Client
|
|
15
|
+
|
|
16
|
+
attr_reader :request
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def file_index
|
|
20
|
+
return @file_index ||= Hash.new do |h,k|
|
|
21
|
+
h[k] = []
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def panel_exclusion
|
|
26
|
+
return @panel_exclusion ||= []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def from_file(rel_path)
|
|
30
|
+
old_rel, Thread::current['panel_file'] = Thread::current['panel_file'], rel_path
|
|
31
|
+
require File::join('insight', 'panels', rel_path)
|
|
32
|
+
return (file_index[rel_path] - panel_exclusion)
|
|
33
|
+
ensure
|
|
34
|
+
Thread::current['panel_file'] = old_rel
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def current_panel_file
|
|
38
|
+
return Thread::current['panel_file'] ||
|
|
39
|
+
begin
|
|
40
|
+
file_name = nil
|
|
41
|
+
caller.each do |line|
|
|
42
|
+
md = %r{^[^:]*insight/panels/([^:]*)\.rb:}.match line
|
|
43
|
+
unless md.nil?
|
|
44
|
+
file_name = md[1]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
file_name
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inherited(sub)
|
|
52
|
+
if filename = current_panel_file
|
|
53
|
+
Panel::file_index[current_panel_file] << sub
|
|
54
|
+
else
|
|
55
|
+
warn "Insight::Panel inherited by #{sub.name} outside of an insight/panels/* file. Discarded"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def excluded(klass = nil)
|
|
60
|
+
Panel::panel_exclusion << klass || self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def initialize(app)
|
|
66
|
+
if panel_app
|
|
67
|
+
#XXX use mappings
|
|
68
|
+
@app = Rack::Cascade.new([panel_app, app])
|
|
69
|
+
else
|
|
70
|
+
@app = app
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def call(env)
|
|
75
|
+
@env = env
|
|
76
|
+
before(env)
|
|
77
|
+
status, headers, body = @app.call(env)
|
|
78
|
+
@request = Rack::Request.new(env)
|
|
79
|
+
after(env, status, headers, body)
|
|
80
|
+
env["insight.panels"] << self
|
|
81
|
+
return [status, headers, body]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def panel_app
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.panel_mappings
|
|
89
|
+
{}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def has_content?
|
|
93
|
+
true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def name
|
|
97
|
+
"Unnamed panel: #{__FILE__}" #for shame
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def heading_for_request(number)
|
|
101
|
+
heading rescue "xxx" #XXX: no panel should need this
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def content_for_request(number)
|
|
105
|
+
content rescue "" #XXX: no panel should need this
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def before(env)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def after(env, status, headers, body)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def render(template)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|