require 'ostruct' require 'erb' require 'rack/request' require 'rack/utils' module Rack # Rack::ShowExceptions catches all exceptions raised from the app it # wraps. It shows a useful backtrace with the sourcefile and # clickable context, the whole Rack environment and the request # data. # # Be careful when you use this on public-facing sites as it could # reveal information helpful to attackers. class ShowExceptions CONTEXT = 7 def initialize(app) @app = app @template = ERB.new(TEMPLATE) end def call(env) @app.call(env) rescue StandardError, LoadError, SyntaxError => e exception_string = dump_exception(e) env["rack.errors"].puts(exception_string) env["rack.errors"].flush if prefers_plain_text?(env) content_type = "text/plain" body = [exception_string] else content_type = "text/html" body = pretty(env, e) end [500, {"Content-Type" => content_type, "Content-Length" => Rack::Utils.bytesize(body.join).to_s}, body] end def prefers_plain_text?(env) env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" && (!env["HTTP_ACCEPT"] || !env["HTTP_ACCEPT"].include?("text/html")) end def dump_exception(exception) string = "#{exception.class}: #{exception.message}\n" string << exception.backtrace.map { |l| "\t#{l}" }.join("\n") string end def pretty(env, exception) req = Rack::Request.new(env) # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. path = path = (req.script_name + req.path_info).squeeze("/") # This double assignment is to prevent an "unused variable" warning on # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me. frames = frames = exception.backtrace.map { |line| frame = OpenStruct.new if line =~ /(.*?):(\d+)(:in `(.*)')?/ frame.filename = $1 frame.lineno = $2.to_i frame.function = $4 begin lineno = frame.lineno-1 lines = ::File.readlines(frame.filename) frame.pre_context_lineno = [lineno-CONTEXT, 0].max frame.pre_context = lines[frame.pre_context_lineno...lineno] frame.context_line = lines[lineno].chomp frame.post_context_lineno = [lineno+CONTEXT, lines.size].min frame.post_context = lines[lineno+1..frame.post_context_lineno] rescue end frame else nil end }.compact [@template.result(binding)] end def h(obj) # :nodoc: case obj when String Utils.escape_html(obj) else Utils.escape_html(obj.inspect) end end # :stopdoc: # adapted from Django # Copyright (c) 2005, the Lawrence Journal-World # Used under the modified BSD license: # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 TEMPLATE = <<'HTML' <%=h exception.class %> at <%=h path %>

<%=h exception.class %> at <%=h path %>

<%=h exception.message %>

Ruby <% if first = frames.first %> <%=h first.filename %>: in <%=h first.function %>, line <%=h frames.first.lineno %> <% else %> unknown location <% end %>
Web <%=h req.request_method %> <%=h(req.host + path)%>

Jump to:

Traceback (innermost first)

Request information

GET

<% if req.GET and not req.GET.empty? %> <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No GET data.

<% end %>

POST

<% if req.POST and not req.POST.empty? %> <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No POST data.

<% end %> <% unless req.cookies.empty? %> <% req.cookies.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No cookie data.

<% end %>

Rack ENV

<% env.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val %>

You're seeing this error because you use Rack::ShowExceptions.

HTML # :startdoc: end end