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,198 @@
|
|
|
1
|
+
require 'rack'
|
|
2
|
+
require "digest/sha1"
|
|
3
|
+
require "rack/insight/config"
|
|
4
|
+
require "rack/insight/logger"
|
|
5
|
+
require "rack/insight/filtered_backtrace"
|
|
6
|
+
require "rack/insight/options"
|
|
7
|
+
require "rack/insight/panel"
|
|
8
|
+
require "rack/insight/panel_app"
|
|
9
|
+
require "rack/insight/params_signature"
|
|
10
|
+
require "rack/insight/rack_static_bug_avoider"
|
|
11
|
+
require "rack/insight/redirect_interceptor"
|
|
12
|
+
require "rack/insight/render"
|
|
13
|
+
require "rack/insight/toolbar"
|
|
14
|
+
require "rack/insight/enable-button"
|
|
15
|
+
require "rack/insight/path-filter"
|
|
16
|
+
require 'rack/insight/request-recorder'
|
|
17
|
+
require 'rack/insight/instrumentation/setup'
|
|
18
|
+
require 'rack/insight/panels-content'
|
|
19
|
+
require 'rack/insight/panels-header'
|
|
20
|
+
|
|
21
|
+
module Rack::Insight
|
|
22
|
+
class App
|
|
23
|
+
include Options
|
|
24
|
+
INSIGHT_ROOT = "/__insight__"
|
|
25
|
+
INSIGHT_REGEX = %r{^#{INSIGHT_ROOT}}
|
|
26
|
+
|
|
27
|
+
VERSION = "0.4.4"
|
|
28
|
+
|
|
29
|
+
class SecurityError < StandardError
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(app, options = {}, &block)
|
|
33
|
+
initialize_options options
|
|
34
|
+
@base_app = app
|
|
35
|
+
@panels = []
|
|
36
|
+
instance_eval(&block) if block_given?
|
|
37
|
+
|
|
38
|
+
@logger = Logger.new(read_option(:log_level), read_option(:log_path))
|
|
39
|
+
Thread.current['rack-insight.logger'] = @logger
|
|
40
|
+
build_normal_stack
|
|
41
|
+
build_debug_stack
|
|
42
|
+
if options[:on_initialize]
|
|
43
|
+
options[:on_initialize].call(self)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
attr_reader :logger
|
|
47
|
+
attr_accessor :panels
|
|
48
|
+
|
|
49
|
+
def call(env)
|
|
50
|
+
@original_request = Rack::Request.new(env)
|
|
51
|
+
if insight_active?
|
|
52
|
+
@env = env
|
|
53
|
+
self.options = @default_options
|
|
54
|
+
|
|
55
|
+
env['rack-insight.logger'] = @logger
|
|
56
|
+
Thread.current['rack-insight.logger'] = @logger
|
|
57
|
+
|
|
58
|
+
Rack::Insight.enable
|
|
59
|
+
env["rack-insight.panels"] = []
|
|
60
|
+
@debug_stack.call(env)
|
|
61
|
+
else
|
|
62
|
+
@normal_stack.call(env)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def reset(new_options=nil)
|
|
68
|
+
@env = nil
|
|
69
|
+
initialize_options(new_options)
|
|
70
|
+
|
|
71
|
+
Rack::Insight::Instrumentation::ClassProbe::all_probes.each do |probe|
|
|
72
|
+
probe.clear_collectors
|
|
73
|
+
end
|
|
74
|
+
Rack::Insight::Instrumentation::InstanceProbe::all_probes.each do |probe|
|
|
75
|
+
probe.clear_collectors
|
|
76
|
+
end
|
|
77
|
+
Rack::Insight::Instrumentation::PackageDefinition.clear_collectors
|
|
78
|
+
|
|
79
|
+
build_debug_stack
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def insight_active?
|
|
85
|
+
return (toolbar_requested? && ip_authorized? && password_authorized?)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def build_normal_stack
|
|
89
|
+
builder = Rack::Builder.new
|
|
90
|
+
builder.use EnableButton, self
|
|
91
|
+
builder.run Rack::Cascade.new([ asset_mapped(Rack::Builder.new), @base_app ])
|
|
92
|
+
@normal_stack = builder.to_app
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_debug_stack
|
|
96
|
+
@panels.clear
|
|
97
|
+
builder = Rack::Builder.new
|
|
98
|
+
builder.use Toolbar, self
|
|
99
|
+
builder.run Rack::Cascade.new([panel_mappings, shortcut_stack(@base_app), collection_stack(@base_app)])
|
|
100
|
+
|
|
101
|
+
@debug_stack = builder.to_app
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def panel_mappings
|
|
105
|
+
classes = read_option(:panel_classes)
|
|
106
|
+
root = INSIGHT_ROOT
|
|
107
|
+
insight = self
|
|
108
|
+
builder = Rack::Builder.new do
|
|
109
|
+
classes.each do |panel_class|
|
|
110
|
+
panel_class.panel_mappings.each do |path, app|
|
|
111
|
+
map [root, path].join("/") do
|
|
112
|
+
run app
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
map root + "/panels_content" do
|
|
117
|
+
run PanelsContent.new(insight)
|
|
118
|
+
end
|
|
119
|
+
map root + "/panels_header" do
|
|
120
|
+
run PanelsHeader.new(insight)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
return asset_mapped(builder)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def shortcut_stack(app)
|
|
127
|
+
Rack::Builder.app do
|
|
128
|
+
use PathFilter
|
|
129
|
+
run app
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def collection_stack(app)
|
|
134
|
+
classes = read_option(:panel_classes)
|
|
135
|
+
insight_id = self.object_id
|
|
136
|
+
panels = self.panels
|
|
137
|
+
|
|
138
|
+
#Builder makes it impossible to access the panels
|
|
139
|
+
|
|
140
|
+
app = Instrumentation::Setup.new(app)
|
|
141
|
+
app = RedirectInterceptor.new(app)
|
|
142
|
+
#Reversed? Does it matter?
|
|
143
|
+
app = classes.inject(app) do |app, panel_class|
|
|
144
|
+
panel = panel_class.new(app)
|
|
145
|
+
panels << panel
|
|
146
|
+
panel
|
|
147
|
+
end
|
|
148
|
+
app = RequestRecorder.new(app)
|
|
149
|
+
return app
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def asset_mapped(builder)
|
|
153
|
+
path = public_path
|
|
154
|
+
builder.map INSIGHT_ROOT do
|
|
155
|
+
run Rack::File.new(path)
|
|
156
|
+
end
|
|
157
|
+
builder.to_app
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def public_path
|
|
161
|
+
::File.expand_path("../../insight/public/__insight__", __FILE__)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def toolbar_requested?
|
|
165
|
+
@original_request.cookies["rack-insight_enabled"]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def ip_authorized?
|
|
169
|
+
return true unless options["rack-insight.ip_masks"]
|
|
170
|
+
|
|
171
|
+
logger.debug{ "Checking #{@original_request.ip} against ip_masks" }
|
|
172
|
+
ip = IPAddr.new(@original_request.ip)
|
|
173
|
+
|
|
174
|
+
mask = options["rack-insight.ip_masks"].find do |ip_mask|
|
|
175
|
+
ip_mask.include?(ip)
|
|
176
|
+
end
|
|
177
|
+
if mask
|
|
178
|
+
logger.debug{ "Matched #{mask}" }
|
|
179
|
+
return true
|
|
180
|
+
else
|
|
181
|
+
logger.debug{ "Matched no masks" }
|
|
182
|
+
return false
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def password_authorized?
|
|
187
|
+
return true unless options["rack-insight.password"]
|
|
188
|
+
|
|
189
|
+
logger.debug{"Checking password"}
|
|
190
|
+
|
|
191
|
+
expected_sha = Digest::SHA1.hexdigest ["rack-insight", options["rack-insight.password"]].join(":")
|
|
192
|
+
actual_sha = @original_request.cookies["rack-insight_password"]
|
|
193
|
+
|
|
194
|
+
logger.debug{"Password result: #{actual_sha == expected_sha}"}
|
|
195
|
+
actual_sha == expected_sha
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
class Config
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :config
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
DEFAULTS = {
|
|
8
|
+
# You can augment or replace the default set of panel load paths.
|
|
9
|
+
# These are the paths where rack-insight will look for panels.
|
|
10
|
+
# A rack-insight extension gem could place panels in:
|
|
11
|
+
# lib/foo/bar/
|
|
12
|
+
# Since gems' lib/ is automatically shifted onto Ruby load path, this will make the custom panels discoverable:
|
|
13
|
+
# Rack::Insight::Config.configure do |config|
|
|
14
|
+
# config[:panel_load_paths] << File::join('foo', 'bar')
|
|
15
|
+
# end
|
|
16
|
+
:panel_load_paths => [File::join('rack', 'insight', 'panels')]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#cattr_reader :config
|
|
20
|
+
#cattr_writer :config
|
|
21
|
+
|
|
22
|
+
@config ||= DEFAULTS
|
|
23
|
+
def self.configure &block
|
|
24
|
+
yield @config
|
|
25
|
+
unless config[:panel_load_paths].kind_of?(Array)
|
|
26
|
+
raise "Rack::Insight::Config.config[:panel_load_paths] is invalid: Expected kind of Array but got #{config[:panel_load_paths].class}"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#require 'rack-insight'
|
|
2
|
+
require 'sqlite3'
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Rack::Insight
|
|
6
|
+
class Database
|
|
7
|
+
module RequestDataClient
|
|
8
|
+
def key_sql_template(sql)
|
|
9
|
+
@key_sql_template = sql
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def table_setup(name, *keys)
|
|
13
|
+
@table = DataTable.new(name, *keys)
|
|
14
|
+
if keys.empty?
|
|
15
|
+
@key_sql_template = ""
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def store(env, *keys_and_value)
|
|
20
|
+
return if env.nil?
|
|
21
|
+
request_id = env["rack-insight.request-id"]
|
|
22
|
+
return if request_id.nil?
|
|
23
|
+
|
|
24
|
+
value = keys_and_value[-1]
|
|
25
|
+
keys = keys_and_value[0...-1]
|
|
26
|
+
|
|
27
|
+
@table.store(request_id, value, @key_sql_template % keys)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def retrieve(request_id)
|
|
31
|
+
@table.for_request(request_id)
|
|
32
|
+
end
|
|
33
|
+
alias retreive retrieve #JDL cannot spell
|
|
34
|
+
|
|
35
|
+
def table_length
|
|
36
|
+
@table.length
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
include Logging
|
|
42
|
+
|
|
43
|
+
def database_path=(value)
|
|
44
|
+
@database_path = value || "rack-insight.sqlite"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def database_path
|
|
48
|
+
@database_path
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def db
|
|
52
|
+
@db ||= open_database
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def reset
|
|
56
|
+
@db = nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def open_database
|
|
60
|
+
@db = SQLite3::Database.new(database_path)
|
|
61
|
+
@db.execute("pragma foreign_keys = on")
|
|
62
|
+
@db
|
|
63
|
+
rescue Object => ex
|
|
64
|
+
msg = "Issue while loading SQLite DB:" + [ex.class, ex.message, ex.backtrace[0..4]].inspect
|
|
65
|
+
logger.error{ msg }
|
|
66
|
+
|
|
67
|
+
return {}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if defined?(PhusionPassenger)
|
|
71
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
|
72
|
+
Rack::Insight::Database::open_database if forked
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Table
|
|
78
|
+
include Logging
|
|
79
|
+
|
|
80
|
+
def db
|
|
81
|
+
Rack::Insight::Database.db
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def create_keys_clause
|
|
85
|
+
"#{@keys.map{|key| "#{key} varchar"}.join(", ")}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def create_sql
|
|
89
|
+
"create table #@table_name ( id integer primary key, #{create_keys_clause} )"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def execute(*args)
|
|
93
|
+
logger.info{ ins_args = args.inspect; "(#{[ins_args.length,120].min}/#{ins_args.length})" + ins_args[0..120] }
|
|
94
|
+
db.execute(*args)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def initialize(table_name, *keys)
|
|
98
|
+
@table_name = table_name
|
|
99
|
+
@keys = keys
|
|
100
|
+
if(execute("select * from sqlite_master where name = ?", table_name).empty?)
|
|
101
|
+
logger.warn{ "Initializing a table called #{table_name}" }
|
|
102
|
+
execute(create_sql)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def select(which_sql, condition_sql)
|
|
107
|
+
execute("select #{which_sql} from #@table_name where #{condition_sql}")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def fields_sql
|
|
111
|
+
"#{@keys.join(",")}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def insert(values_sql)
|
|
115
|
+
execute("insert into #@table_name(#{fields_sql}) values (#{values_sql})")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def keys(name)
|
|
119
|
+
execute("select #{name} from #@table_name").flatten
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def length(where = "1 = 1")
|
|
123
|
+
execute("select count(1) from #@table_name where #{where}").first.first
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def to_a
|
|
127
|
+
execute("select * from #@table_name")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class RequestTable < Table
|
|
132
|
+
def initialize()
|
|
133
|
+
super("requests", "method", "url", "date")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def store(method, url)
|
|
137
|
+
result = insert("'#{method}', '#{url}', #{Time.now.to_i}")
|
|
138
|
+
db.last_insert_row_id
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def last_request_id
|
|
142
|
+
execute("select max(id) from #@table_name").first.first
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def sweep
|
|
146
|
+
execute("delete from #@table_name where date < #{Time.now.to_i - (60 * 60 * 12)}")
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
require 'yaml'
|
|
151
|
+
class DataTable < Table
|
|
152
|
+
def initialize(name, *keys)
|
|
153
|
+
super(name, *(%w{request_id} + keys + %w{value}))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def create_keys_clause
|
|
157
|
+
non_request_keys = @keys - %w"request_id"
|
|
158
|
+
sql = non_request_keys.map{|key| "#{key} varchar"}.join(", ")
|
|
159
|
+
sql += ", request_id references requests(id) on delete cascade"
|
|
160
|
+
sql
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def store(request_id, value, keys_sql = "")
|
|
164
|
+
sql = "'#{encode_value(value)}'"
|
|
165
|
+
sql = keys_sql + ", " + sql unless keys_sql.empty?
|
|
166
|
+
sql = "#{request_id}, #{sql}"
|
|
167
|
+
insert(sql)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def encode_value(value)
|
|
171
|
+
Base64.encode64(YAML.dump(value))
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def decode_value(value)
|
|
175
|
+
YAML.load(Base64.decode64(value))
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def retrieve(key_sql)
|
|
179
|
+
select("value", key_sql).map{|value| decode_value(value.first)}
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def for_request(id)
|
|
183
|
+
retrieve("request_id = #{id}")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def to_a
|
|
187
|
+
super.map do |row|
|
|
188
|
+
row[-1] = decode_value(row[-1])
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Rack::Insight
|
|
2
|
+
class EnableButton
|
|
3
|
+
include Render
|
|
4
|
+
|
|
5
|
+
MIME_TYPES = ["text/html", "application/xhtml+xml"]
|
|
6
|
+
|
|
7
|
+
def initialize(app, insight)
|
|
8
|
+
@app = app
|
|
9
|
+
@insight = insight
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
@env = env
|
|
14
|
+
status, headers, body = @app.call(@env)
|
|
15
|
+
|
|
16
|
+
response = Rack::Response.new(body, status, headers)
|
|
17
|
+
|
|
18
|
+
inject_button(response) if okay_to_modify?(env, response)
|
|
19
|
+
|
|
20
|
+
return response.to_a
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def okay_to_modify?(env, response)
|
|
24
|
+
req = Rack::Request.new(env)
|
|
25
|
+
content_type, charset = response.content_type.split(";")
|
|
26
|
+
|
|
27
|
+
response.ok? && MIME_TYPES.include?(content_type) && !req.xhr?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def inject_button(response)
|
|
31
|
+
full_body = response.body.join
|
|
32
|
+
full_body.sub! /<\/body>/, render + "</body>"
|
|
33
|
+
|
|
34
|
+
response["Content-Length"] = full_body.bytesize.to_s
|
|
35
|
+
|
|
36
|
+
response.body = [full_body]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def render
|
|
40
|
+
render_template("enable-button")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|