roda-debug_bar 0.1.1
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/lib/roda/debug_bar/current.rb +110 -0
- data/lib/roda/debug_bar/instance.rb +186 -0
- data/lib/roda/debug_bar/views/debug_bar/models.erb +37 -0
- data/lib/roda/debug_bar/views/debug_bar/queries.erb +30 -0
- data/lib/roda/debug_bar/views/debug_bar/request.erb +39 -0
- data/lib/roda/debug_bar/views/debug_bar/right_bar.erb +4 -0
- data/lib/roda/debug_bar/views/debug_bar/route.erb +17 -0
- data/lib/roda/debug_bar/views/debug_bar/views.erb +19 -0
- data/lib/roda/debug_bar/views/debug_bar.erb +196 -0
- data/lib/roda/plugins/debug_bar.rb +299 -0
- data/lib/sequel/extensions/debug_bar.rb +39 -0
- data/lib/sequel/main.rb +36 -0
- data/lib/sequel/plugins/debug_bar.rb +50 -0
- data/lib/version.rb +3 -0
- data/public/out.css +1000 -0
- data/public/ruby.png +0 -0
- metadata +153 -0
@@ -0,0 +1,299 @@
|
|
1
|
+
require "tty-logger"
|
2
|
+
require "pathname"
|
3
|
+
require_relative "../debug_bar/current"
|
4
|
+
require_relative "../debug_bar/instance"
|
5
|
+
require_relative "../../sequel/extensions/debug_bar"
|
6
|
+
require_relative "../../sequel/plugins/debug_bar"
|
7
|
+
require 'rouge'
|
8
|
+
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
class Roda
|
12
|
+
module RodaPlugins
|
13
|
+
module DebugBar
|
14
|
+
|
15
|
+
DEFAULTS = {
|
16
|
+
db: nil,
|
17
|
+
log_time: false,
|
18
|
+
trace_missed: true,
|
19
|
+
##### PUT TRACE ALL TO TRUE AGAIN
|
20
|
+
trace_all: false,
|
21
|
+
# trace_all: true,
|
22
|
+
filtered_params: %w[password password_confirmation _csrf],
|
23
|
+
handlers: [:console]
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def self.load_dependencies(app, opts = {})
|
27
|
+
app.plugin :render
|
28
|
+
|
29
|
+
# just to display sessions
|
30
|
+
# app.plugin :sessions, secret: 'very-secret'*20
|
31
|
+
|
32
|
+
app.plugin :hooks
|
33
|
+
app.plugin :match_hook
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.configure(app, opts = {})
|
37
|
+
# app.opts[:debug_bar] = opts
|
38
|
+
|
39
|
+
@@data_store = []
|
40
|
+
|
41
|
+
options = DEFAULTS.merge(opts)
|
42
|
+
|
43
|
+
logger = TTY::Logger.new { |config|
|
44
|
+
config.handlers = options[:handlers]
|
45
|
+
config.output = options.fetch(:output) { File.open('/dev/null', 'w') }
|
46
|
+
config.metadata.push(:time, :date) if options[:log_time]
|
47
|
+
config.filters.data = options[:filtered_params]
|
48
|
+
config.filters.mask = "<FILTERED>"
|
49
|
+
}
|
50
|
+
|
51
|
+
root = Pathname(app.opts[:root] || Dir.pwd)
|
52
|
+
|
53
|
+
db = options[:db] || (defined?(DB) && DB)
|
54
|
+
db&.extension :debug_bar
|
55
|
+
|
56
|
+
::Sequel::Model.plugin :debug_bar
|
57
|
+
|
58
|
+
app.match_hook do
|
59
|
+
callee = caller_locations.find { |location|
|
60
|
+
location.path.start_with?(root.to_s)
|
61
|
+
}
|
62
|
+
|
63
|
+
@_debug_bar_instance.add_match(callee)
|
64
|
+
end
|
65
|
+
|
66
|
+
app.before do
|
67
|
+
@_debug_bar_instance = Roda::DebugBar::Instance.new(logger, env, object_id, root, options[:filter])
|
68
|
+
|
69
|
+
@halted = false
|
70
|
+
|
71
|
+
if request.path == '/debug_bar/ruby.png'
|
72
|
+
@halted = true
|
73
|
+
response.status = 200
|
74
|
+
response['content-type'] = 'image/png'
|
75
|
+
response.write(File.open(File.join(__dir__, '../../../public/ruby.png')).read)
|
76
|
+
request.halt
|
77
|
+
end
|
78
|
+
|
79
|
+
if request.path == '/debug_bar/out.css'
|
80
|
+
@halted = true
|
81
|
+
response.status = 200
|
82
|
+
response['content-type'] = 'text/css'
|
83
|
+
response.write(File.open(File.join(__dir__, '../../../public/out.css')).read)
|
84
|
+
request.halt
|
85
|
+
end
|
86
|
+
|
87
|
+
if request.path.start_with? '/debug_bar/last/'
|
88
|
+
@halted = true
|
89
|
+
id = request.path.split('/').last.to_i
|
90
|
+
|
91
|
+
if id > @@data_store.size
|
92
|
+
response.status = 404
|
93
|
+
response['content-type'] = 'text/plain'
|
94
|
+
response.write "Log ##{id} either doesn't exist yet, or wasn't stored. The current max store is 5."
|
95
|
+
request.halt
|
96
|
+
end
|
97
|
+
|
98
|
+
response.status = 200
|
99
|
+
response['content-type'] = 'application/json'
|
100
|
+
response.write(@@data_store[-id].to_json)
|
101
|
+
request.halt
|
102
|
+
end
|
103
|
+
|
104
|
+
# request.env['PATH_INFO'] is Rack's raw path
|
105
|
+
# request.path is a Roda abstraction over it, but can't be modified
|
106
|
+
@debug_catch = false
|
107
|
+
if request.env['PATH_INFO'].start_with? '/debug_bar/catch/'
|
108
|
+
@debug_catch = true
|
109
|
+
request.env['PATH_INFO'].sub!('/debug_bar/catch', '')
|
110
|
+
end
|
111
|
+
if request.env['PATH_INFO'].start_with? '/debug_bar/c/'
|
112
|
+
@debug_catch = true
|
113
|
+
request.env['PATH_INFO'].sub!('/debug_bar/c', '')
|
114
|
+
end
|
115
|
+
if request.env['PATH_INFO'].start_with? '/c/'
|
116
|
+
@debug_catch = true
|
117
|
+
request.env['PATH_INFO'].sub!('/c', '')
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
app.after do |res|
|
123
|
+
|
124
|
+
break if @halted
|
125
|
+
|
126
|
+
status, headers, content = res
|
127
|
+
@_debug_bar_instance.add(
|
128
|
+
status,
|
129
|
+
request,
|
130
|
+
(options[:trace_missed] && status == 404) || options[:trace_all]
|
131
|
+
)
|
132
|
+
|
133
|
+
# This @data is available to views
|
134
|
+
@data = @_debug_bar_instance.debug_data
|
135
|
+
|
136
|
+
if @debug_catch
|
137
|
+
response = @data.to_json
|
138
|
+
res[1]['content-type'] = 'application/json'
|
139
|
+
res[1]['Content-Length'] = response.bytesize.to_s
|
140
|
+
res[2] = [response]
|
141
|
+
@_debug_bar_instance.drain
|
142
|
+
@_debug_bar_instance.reset
|
143
|
+
break
|
144
|
+
end
|
145
|
+
|
146
|
+
if headers['content-type'] == 'text/html'
|
147
|
+
#####
|
148
|
+
|
149
|
+
# Should be a method, but I'm having trouble accessing the class variable from within an instance method
|
150
|
+
# def add_data(data)
|
151
|
+
if @@data_store.size < 5
|
152
|
+
@@data_store << @data
|
153
|
+
else
|
154
|
+
@@data_store.shift
|
155
|
+
@@data_store.push @data
|
156
|
+
end
|
157
|
+
# end
|
158
|
+
|
159
|
+
#####
|
160
|
+
|
161
|
+
# add_data(@data)
|
162
|
+
|
163
|
+
puts @@data_store.size
|
164
|
+
end
|
165
|
+
|
166
|
+
imports = <<-HTML
|
167
|
+
<script src="https://unpkg.com/alpinejs@3.14.8/dist/cdn.min.js" defer></script>
|
168
|
+
<!-- <script src="https://raw.githubusercontent.com/caldwell/renderjson/refs/heads/master/renderjson.js"></script>
|
169
|
+
<script src="https://cdn.jsdelivr.net/gh/caldwell/renderjson@master/renderjson.js"></script>
|
170
|
+
<script defer src="https://unpkg.com/pretty-json-custom-element/index.js"></script>
|
171
|
+
<script src="https://unpkg.com/@alenaksu/json-viewer@2.1.0/dist/json-viewer.bundle.js"></script> -->
|
172
|
+
HTML
|
173
|
+
|
174
|
+
if headers['content-type'] == 'text/html'
|
175
|
+
|
176
|
+
res[2] = res[2].map do |body_part|
|
177
|
+
if body_part.include?("<head>")
|
178
|
+
body_part.sub("<head>", "<head>#{imports}")
|
179
|
+
else
|
180
|
+
"#{imports}#{body_part}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
debug_bar = relative_render('debug_bar')
|
185
|
+
|
186
|
+
res[2] = res[2].map do |body_part|
|
187
|
+
if body_part.include?("</body>")
|
188
|
+
body_part.sub("</body>", "#{debug_bar}</body>")
|
189
|
+
else
|
190
|
+
"#{body_part}#{debug_bar}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
res[1]["Content-Length"] = res[2].reduce(0) { |memo, chunk| memo + chunk.bytesize }.to_s
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
# if request.path == '/baz'
|
199
|
+
# puts 'hi hi hi'
|
200
|
+
# end
|
201
|
+
|
202
|
+
@_debug_bar_instance.drain
|
203
|
+
@_debug_bar_instance.reset
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
#########
|
208
|
+
|
209
|
+
module InstanceMethods
|
210
|
+
|
211
|
+
# def add_data(data)
|
212
|
+
# if @@data_store.size < 5
|
213
|
+
# @@data_store << data
|
214
|
+
# else
|
215
|
+
# @@data_store.unshift
|
216
|
+
# @@data_store.push data
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
|
220
|
+
def render(path, opts = {})
|
221
|
+
unless opts[:ignore]
|
222
|
+
# puts "rendering #{path}"
|
223
|
+
Roda::DebugBar::Current.add_view(path)
|
224
|
+
end
|
225
|
+
super
|
226
|
+
end
|
227
|
+
|
228
|
+
def view(path, opts = {})
|
229
|
+
unless opts[:ignore]
|
230
|
+
# puts "view #{path}"
|
231
|
+
Roda::DebugBar::Current.add_view(path)
|
232
|
+
end
|
233
|
+
super
|
234
|
+
end
|
235
|
+
|
236
|
+
# def grid_component(opts)
|
237
|
+
# end
|
238
|
+
|
239
|
+
def relative_render view
|
240
|
+
render('', path: File.join(__dir__, "../debug_bar/views/#{view}.erb"), ignore: true)
|
241
|
+
end
|
242
|
+
|
243
|
+
def sql_highlight query
|
244
|
+
formatter = Rouge::Formatters::HTML.new
|
245
|
+
lexer = Rouge::Lexers::SQL.new
|
246
|
+
sql = formatter.format(lexer.lex(query))
|
247
|
+
sql.gsub('`', '`')
|
248
|
+
end
|
249
|
+
|
250
|
+
def highlight_ruby_hash query
|
251
|
+
formatter = Rouge::Formatters::HTML.new
|
252
|
+
lexer = Rouge::Lexers::Ruby.new
|
253
|
+
formatter.format(lexer.lex(query))
|
254
|
+
end
|
255
|
+
|
256
|
+
def format_time time
|
257
|
+
|
258
|
+
# case time
|
259
|
+
# in 1e-6...1e-3
|
260
|
+
# "#{time*1e6}μs"
|
261
|
+
# in 1e-3...
|
262
|
+
# end
|
263
|
+
|
264
|
+
if time < 1e-3
|
265
|
+
"#{(time*1e6).round}μs"
|
266
|
+
elsif time < 1
|
267
|
+
"#{(time*1e3).round(3)}ms"
|
268
|
+
else
|
269
|
+
"#{time}s"
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
# # def _roda_run_main_route
|
275
|
+
# def call
|
276
|
+
# status, headers, content = super
|
277
|
+
|
278
|
+
# debug_bar = relative_render('debug-bar')
|
279
|
+
|
280
|
+
# content = content.map do |body_part|
|
281
|
+
# if body_part.include?("</body>")
|
282
|
+
# body_part.sub("</body>", "#{debug_bar}</body>")
|
283
|
+
# else
|
284
|
+
# "#{body_part}#{debug_bar}"
|
285
|
+
# end
|
286
|
+
# end
|
287
|
+
|
288
|
+
# headers["Content-Length"] = content.reduce(0) { |memo, chunk| memo + chunk.bytesize }.to_s
|
289
|
+
|
290
|
+
# [status, headers, content]
|
291
|
+
# end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
register_plugin(:debug_bar, RodaPlugins::DebugBar)
|
298
|
+
end
|
299
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "sequel"
|
4
|
+
|
5
|
+
module DebugBar
|
6
|
+
module Sequel
|
7
|
+
|
8
|
+
if ::Sequel::VERSION_NUMBER >= 50240
|
9
|
+
def skip_logging?
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.extended(base)
|
15
|
+
if ::Sequel::VERSION_NUMBER < 50240
|
16
|
+
return if base.loggers.any?
|
17
|
+
|
18
|
+
require "logger"
|
19
|
+
base.loggers = [Logger.new("/dev/null")]
|
20
|
+
end
|
21
|
+
|
22
|
+
# ::Sequel::Model.plugin :after_initialize
|
23
|
+
end
|
24
|
+
|
25
|
+
# def self.configure(model, opts = {})
|
26
|
+
# model.include InstanceMethods
|
27
|
+
# end
|
28
|
+
|
29
|
+
def log_duration(duration, message)
|
30
|
+
Roda::DebugBar::Current.increment_accrued_database_time(duration)
|
31
|
+
Roda::DebugBar::Current.increment_database_query_count
|
32
|
+
Roda::DebugBar::Current.add_database_query(message, duration.round(6))
|
33
|
+
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
::Sequel::Database.register_extension :debug_bar, DebugBar::Sequel
|
39
|
+
end
|
data/lib/sequel/main.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require_relative 'plugins/debug_bar'
|
3
|
+
|
4
|
+
# require 'sequel/plugins/after_initialize'
|
5
|
+
|
6
|
+
DB = Sequel.sqlite('/Users/avi/code/2025/jan/roda-30day-clone/database/database.sqlite')
|
7
|
+
|
8
|
+
|
9
|
+
Sequel::Model.plugin :debug_bar
|
10
|
+
|
11
|
+
class Employer < Sequel::Model
|
12
|
+
end
|
13
|
+
|
14
|
+
# puts Employer.first.inspect
|
15
|
+
|
16
|
+
# puts Employer.plugins.include?(:after_initialize)
|
17
|
+
|
18
|
+
|
19
|
+
# Sequel::Model.plugin :after_initialize
|
20
|
+
# Sequel::Model.plugin :debug_bar
|
21
|
+
|
22
|
+
# class Employer < Sequel::Model
|
23
|
+
# plugin :debug_bar
|
24
|
+
# end
|
25
|
+
|
26
|
+
# Employer.plugin :after_initialize
|
27
|
+
# Employer.plugin :debug_bar
|
28
|
+
|
29
|
+
# puts Employer.first.inspect
|
30
|
+
|
31
|
+
# a = Employer.create(name: 'alksgj')
|
32
|
+
|
33
|
+
# # puts Employer.plugins.include?(:after_initialization)
|
34
|
+
# puts Employer.plugins.include?(:debug_bar)
|
35
|
+
|
36
|
+
# # puts Sequel::Model.plugins.include?(:debug_bar)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "sequel"
|
4
|
+
|
5
|
+
module Sequel
|
6
|
+
module Plugins
|
7
|
+
module DebugBar
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def call(_)
|
12
|
+
v = super
|
13
|
+
v.after_initialize
|
14
|
+
v
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
|
21
|
+
def initialize(h={})
|
22
|
+
super
|
23
|
+
after_initialize
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_initialize
|
27
|
+
Roda::DebugBar::Current.add_model(self.class, self.values)
|
28
|
+
# puts "created #{self.inspect}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.extended(base)
|
33
|
+
# ::Sequel::Model.plugin :after_initialize
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.apply(model, opts = {})
|
37
|
+
model.include InstanceMethods
|
38
|
+
model.include ClassMethods
|
39
|
+
end
|
40
|
+
|
41
|
+
# def self.configure(model, opts = {})
|
42
|
+
# model.include InstanceMethods
|
43
|
+
# model.include ClassMethods
|
44
|
+
# end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Model.plugin :debug_bar, Sequel::Plugins::DebugBar
|
50
|
+
end
|
data/lib/version.rb
ADDED