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.
@@ -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('`', '&#96;')
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module DebugBar
2
+ VERSION = "0.1.1"
3
+ end