Public Instance methods
Return a HTML page showing the exception, allowing a developer more information for debugging. Designed to be called inside an exception handler, passing in the received exception. Sets the Content-Type header in the response, and returns the string used for the body. If the Accept request header is present and text/html is accepted, return an HTML page with the backtrace with the ability to see the context for each backtrace line, as well as the GET, POST, cookie, and rack environment data. If text/html is not accepted, then just show a plain text page with the exception class, message, and backtrace.
Options:
:assets |
If |
:context |
The number of context lines before and after each line in the backtrace (default: 7). |
:css_file |
A path to the external CSS file for the HTML exception page. If false, doesn’t use any CSS. |
:js_file |
A path to the external javascript file for the HTML exception page. If false, doesn’t use any JS. |
:json |
Return a hash of exception information. The hash will have a single key, “exception”, with a value being a hash with three keys, “class”, “message”, and “backtrace”, which contain information derived from the given exception. Designed to be used with the |
# File lib/roda/plugins/exception_page.rb 198 def exception_page(exception, opts=OPTS) 199 message = exception_page_exception_message(exception) 200 if opts[:json] 201 @_response[RodaResponseHeaders::CONTENT_TYPE] = "application/json" 202 { 203 "exception"=>{ 204 "class"=>exception.class.to_s, 205 "message"=>message, 206 "backtrace"=>exception.backtrace.map(&:to_s) 207 } 208 } 209 elsif env['HTTP_ACCEPT'] =~ /text\/html/ 210 @_response[RodaResponseHeaders::CONTENT_TYPE] = "text/html" 211 212 context = opts[:context] || 7 213 css_file = opts[:css_file] 214 js_file = opts[:js_file] 215 216 case prefix = opts[:assets] 217 when false 218 css_file = false if css_file.nil? 219 js_file = false if js_file.nil? 220 when nil 221 # nothing 222 else 223 prefix = '' if prefix == true 224 css_file ||= "#{prefix}/exception_page.css" 225 js_file ||= "#{prefix}/exception_page.js" 226 end 227 228 css = case css_file 229 when nil 230 "<style type=\"text/css\">#{exception_page_css}</style>" 231 when false 232 # :nothing 233 else 234 "<link rel=\"stylesheet\" href=\"#{h css_file}\" />" 235 end 236 237 js = case js_file 238 when nil 239 "<script type=\"text/javascript\">\n//<!--\n#{exception_page_js}\n//-->\n</script>" 240 when false 241 # :nothing 242 else 243 "<script type=\"text/javascript\" src=\"#{h js_file}\"></script>" 244 end 245 246 frames = exception.backtrace.map.with_index do |line, i| 247 frame = {:id=>i} 248 if line =~ /\A(.*?):(\d+)(?::in [`'](.*)')?\Z/ 249 filename = frame[:filename] = $1 250 lineno = frame[:lineno] = $2.to_i 251 frame[:function] = $3 252 253 begin 254 lineno -= 1 255 lines = ::File.readlines(filename) 256 if line = lines[lineno] 257 pre_lineno = [lineno-context, 0].max 258 if (pre_context = lines[pre_lineno...lineno]) && !pre_context.empty? 259 frame[:pre_context_lineno] = pre_lineno 260 frame[:pre_context] = pre_context 261 end 262 263 post_lineno = [lineno+context, lines.size].min 264 if (post_context = lines[lineno+1..post_lineno]) && !post_context.empty? 265 frame[:post_context_lineno] = post_lineno 266 frame[:post_context] = post_context 267 end 268 269 frame[:context_line] = line.chomp 270 end 271 rescue 272 end 273 274 frame 275 end 276 end.compact 277 278 r = @_request 279 begin 280 post_data = r.POST 281 missing_post = "No POST data" 282 rescue 283 missing_post = "Invalid POST data" 284 end 285 info = lambda do |title, id, var, none| 286 <<END 287 <h3 id="#{id}">#{title}</h3> 288 #{(var && !var.empty?) ? (<<END1) : "<p>#{none}</p>" 289 <table class="req"> 290 <thead> 291 <tr> 292 <th>Variable</th> 293 <th>Value</th> 294 </tr> 295 </thead> 296 <tbody> 297 #{var.sort_by{|k, _| k.to_s}.map{|key, val| (<<END2)}.join 298 <tr> 299 <td>#{h key}</td> 300 <td class="code"><div>#{h val.inspect}</div></td> 301 </tr> 302 END2 303 } 304 </tbody> 305 </table> 306 END1 307 } 308 END 309 end 310 311 <<END 312 <!DOCTYPE html> 313 <html lang="en"> 314 <head> 315 <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 316 <title>#{h exception.class} at #{h r.path}</title> 317 #{css} 318 </head> 319 <body> 320 321 <div id="summary"> 322 <h1>#{h exception.class} at #{h r.path}</h1> 323 <h2>#{h message}</h2> 324 <table><tr> 325 <th>Ruby</th> 326 <td> 327 #{(first = frames.first) ? "<code>#{h first[:filename]}</code>: in <code>#{h first[:function]}</code>, line #{first[:lineno]}" : "unknown location"} 328 </td> 329 </tr><tr> 330 <th>Web</th> 331 <td><code>#{r.request_method} #{h r.host}#{h r.path}</code></td> 332 </tr></table> 333 334 <h3>Jump to:</h3> 335 <ul id="quicklinks"> 336 <li><a href="#get-info">GET</a></li> 337 <li><a href="#post-info">POST</a></li> 338 <li><a href="#cookie-info">Cookies</a></li> 339 <li><a href="#env-info">ENV</a></li> 340 </ul> 341 </div> 342 343 <div id="traceback"> 344 <h2>Traceback <span>(innermost first)</span></h2> 345 <ul class="traceback"> 346 #{frames.map{|frame| id = frame[:id]; (<<END1)}.join 347 <li class="frame"> 348 <code>#{h frame[:filename]}:#{frame[:lineno]}</code> in <code>#{h frame[:function]}</code> 349 350 #{frame[:context_line] ? (<<END2) : '</li>' 351 <div class="context" id="c#{id}"> 352 #{frame[:pre_context] ? (<<END3) : '' 353 <ol start="#{frame[:pre_context_lineno]+1}" id="bc#{id}"> 354 #{frame[:pre_context].map{|line| "<li>#{h line}</li>"}.join} 355 </ol> 356 END3 357 } 358 359 <ol start="#{frame[:lineno]}" class="context-line"> 360 <li>#{h frame[:context_line]}<span>...</span></li> 361 </ol> 362 363 #{frame[:post_context] ? (<<END4) : '' 364 <ol start='#{frame[:lineno]+1}' id="ac#{id}"> 365 #{frame[:post_context].map{|line| "<li>#{h line}</li>"}.join} 366 </ol> 367 END4 368 } 369 </div> 370 END2 371 } 372 END1 373 } 374 </ul> 375 </div> 376 377 <div id="requestinfo"> 378 <h2>Request information</h2> 379 380 #{info.call('GET', 'get-info', r.GET, 'No GET data')} 381 #{info.call('POST', 'post-info', post_data, missing_post)} 382 #{info.call('Cookies', 'cookie-info', r.cookies, 'No cookie data')} 383 #{info.call('Rack ENV', 'env-info', r.env, 'No Rack env?')} 384 </div> 385 386 <div id="explanation"> 387 <p> 388 You're seeing this error because you use the Roda exception_page plugin. 389 </p> 390 </div> 391 392 #{js} 393 </body> 394 </html> 395 END 396 else 397 @_response[RodaResponseHeaders::CONTENT_TYPE] = "text/plain" 398 "#{exception.class}: #{message}\n#{exception.backtrace.map{|l| "\t#{l}"}.join("\n")}" 399 end 400 end
The CSS to use on the exception page
# File lib/roda/plugins/exception_page.rb 403 def exception_page_css 404 ExceptionPage.css 405 end
The JavaScript to use on the exception page
# File lib/roda/plugins/exception_page.rb 408 def exception_page_js 409 ExceptionPage.js 410 end