class MetaRuby::GUI::ExceptionRendering

Functionality to render exceptions in an HTML view

On top of properly formatting the exception, it introduces backtrace filtering and javascript-based buttons to enable backtraces on or off.

It is usually not used directly, but through {HTML::Page}

@see HTML::Page#enable_exception_rendering @see HTML::Page#push_exception

Constants

EXCEPTION_TEMPLATE_WITHOUT_BACKTRACE

Template used to render an exception that does not have backtrace

EXCEPTION_TEMPLATE_WITH_BACKTRACE

Template used to render an exception that does have a backtrace

Necessary header content

RESSOURCES_DIR

The directory relative to which ressources (such as css or javascript files) are resolved by default

SCRIPTS

The scripts that are used by the other exception templates

Attributes

linker[R]

@return [#link_to] an object that allows to render a link to an

object
user_file_filter[R]

@return [#[]] an object that can be used to determine whether a

file is a user or framework file. It is used in backtrace
filtering and rendering. The default returns true for any file.

Public Class Methods

new(linker) click to toggle source

Create an exception rendering object using the given linker object

@param [#link_to] linker

# File lib/metaruby/gui/exception_rendering.rb, line 36
def initialize(linker)
    @linker = linker
    self.user_file_filter = nil
end
parse_backtrace(backtrace) click to toggle source

Parse a backtrace into its file, line and method consistuents

@return [Array<(String,Integer,String)>]

# File lib/metaruby/gui/exception_rendering.rb, line 87
def self.parse_backtrace(backtrace)
    BacktraceParser.new(backtrace).parse
end

Public Instance Methods

allocate_exception_id() click to toggle source

Automatically generate an exception ID

# File lib/metaruby/gui/exception_rendering.rb, line 176
def allocate_exception_id
    @@exception_id += 1
end
each_exception_from(e) { |e| ... } click to toggle source

Method used by {#render} to discover all exception objects that are linked to another exception, in cases where exceptions cause one another

The default implementation only yields 'e', reimplement in subclasses

@yieldparam [Exception] exception an exception

# File lib/metaruby/gui/exception_rendering.rb, line 211
def each_exception_from(e)
    return enum_for(__method__) if !block_given?
    yield(e)
end
filter_backtrace(parsed_backtrace, raw_backtrace) click to toggle source

Filters the backtrace to remove framework parts that are not relevant

@param [Array<(String,Integer,Symbol)>] parsed_backtrace the parsed backtrace @param [Array<(String,Integer,Symbol)>] raw_backtrace the raw backtrace

# File lib/metaruby/gui/exception_rendering.rb, line 156
def filter_backtrace(parsed_backtrace, raw_backtrace)
    head = parsed_backtrace.take_while { |file, _| !user_file?(file) }
    tail = parsed_backtrace[head.size..-1].find_all { |file, _| user_file?(file) }
    head + tail
end
head() click to toggle source

Contents necessary in the <head> … </head> section

It is used when enabling the renderer on a [Page] by calling {HTML::Page#add_to_setup}

# File lib/metaruby/gui/exception_rendering.rb, line 72
def head
    HEADER
end
parse_and_filter_backtrace(backtrace) click to toggle source

@api private

Parses the exception backtrace, and generate a parsed raw and parsed filtered version of it

@return [(Array<(String,Integer,String)>,Array<(String,Integer,String))>

the full and filtered backtraces, as list of tuples
(file,line,method)
# File lib/metaruby/gui/exception_rendering.rb, line 224
def parse_and_filter_backtrace(backtrace)
    full_backtrace = ExceptionRendering.parse_backtrace(backtrace)
    filtered_backtrace = filter_backtrace(full_backtrace, backtrace)
    if filtered_backtrace.first.respond_to?(:to_str)
        filtered_backtrace = ExceptionRendering.parse_backtrace(filtered_backtrace)
    end
    return full_backtrace, filtered_backtrace
end
render(e, reason = nil, id = allocate_exception_id) click to toggle source

Render an exception into HTML

@param [Exception] e the exception to be rendered @param [String] reason additional string that describes the

exception reason

@param [String] id the ID that should be used to identify the

exception. Since a given exception can "contain" more than one
(see {#each_exception_from}), a -#counter pattern is added to
the ID.

@return [String]

# File lib/metaruby/gui/exception_rendering.rb, line 190
def render(e, reason = nil, id = allocate_exception_id)
    counter = 0
    html = []
    seen = Set.new
    each_exception_from(e) do |exception|
        if !seen.include?(exception)
            seen << exception
            html << render_single_exception(exception, "#{id}-#{counter += 1}")
        end
    end
    html.join("\n")
end
render_backtrace(backtrace) click to toggle source

@api private

Render a backtrace

It uses {#linker} to generate links, and {#user_file?} to change the style of the backtrace line.

# File lib/metaruby/gui/exception_rendering.rb, line 266
def render_backtrace(backtrace)
    result = []
    backtrace.each do |file, line, method|
        file_link = linker.link_to(Pathname.new(file), file, lineno: line)
        if user_file?(file)
            result << "  <span class=\"user_file\">#{file_link}:#{line}:in #{HTML.escape_html(method.to_s)}</span><br/>"
        else
            result << "  #{file_link}:#{line}:in #{HTML.escape_html(method.to_s)}<br/>"
        end
    end
    result.join("\n")
end
render_single_exception(e, id) click to toggle source

@api private

Render a single exception object into a HTML block

@param [Exception] e the exception @param [String] id the block ID @return [String]

# File lib/metaruby/gui/exception_rendering.rb, line 240
def render_single_exception(e, id)
    message = PP.pp(e, "").split("\n").
        map { |line| HTML.escape_html(line) }

    full_backtrace, filtered_backtrace =
        parse_and_filter_backtrace(e.backtrace || Array.new)

    if !full_backtrace.empty?
        origin_file, origin_line, origin_method =
            filtered_backtrace.find { |file, _| user_file?(file) } ||
            filtered_backtrace.first ||
            full_backtrace.first

        origin_file = linker.link_to(Pathname.new(origin_file), origin_file, lineno: origin_line)
        ERB.new(EXCEPTION_TEMPLATE_WITH_BACKTRACE).result(binding)
    else
        ERB.new(EXCEPTION_TEMPLATE_WITHOUT_BACKTRACE).result(binding)
    end
end
scripts() click to toggle source

Scripts block to be added to the HTML document

It is used when enabling the renderer on a [Page] by calling {HTML::Page#add_to_setup}

# File lib/metaruby/gui/exception_rendering.rb, line 80
def scripts
    SCRIPTS
end
user_file?(file) click to toggle source

Return true if the given file is a user file or a framework file

An object used to determine this can be set with {#user_file_filter=}

This is used by {#render_backtrace} to choose the style of a backtrace line

# File lib/metaruby/gui/exception_rendering.rb, line 169
def user_file?(file)
    user_file_filter[file]
end
user_file_filter=(filter) click to toggle source

Sets {#user_file_filter} or resets it to the default

@param [nil,# filter

# File lib/metaruby/gui/exception_rendering.rb, line 29
def user_file_filter=(filter)
    @user_file_filter = filter || Hash.new(true)
end