Class: MetaRuby::GUI::ExceptionRendering

Inherits:
Object
  • Object
show all
Defined in:
lib/metaruby/gui/exception_rendering.rb

Overview

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

Defined Under Namespace

Classes: BacktraceParser

Constant Summary collapse

RESSOURCES_DIR =

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

File.expand_path('html', File.dirname(__FILE__))
HEADER =

Necessary header content

<<-EOD
<link rel="stylesheet" href="file://#{File.join(RESSOURCES_DIR, 'exception_view.css')}" type="text/css" />
<script type="text/javascript" src="file://#{File.join(RESSOURCES_DIR, 'jquery.min.js')}"></script>
EOD
SCRIPTS =

The scripts that are used by the other exception templates

<<-EOD
<script type="text/javascript">
$(document).ready(function () {
    $(".backtrace").hide()
    $("a.backtrace_toggle_filtered").click(function (event) {
            var eventId = $(this).attr("id");
            $("#backtrace_full_" + eventId).hide();
            $("#backtrace_filtered_" + eventId).toggle();
            event.preventDefault();
            });
    $("a.backtrace_toggle_full").click(function (event) {
            var eventId = $(this).attr("id");
            $("#backtrace_full_" + eventId).toggle();
            $("#backtrace_filtered_" + eventId).hide();
            event.preventDefault();
            });
});
</script>
EOD
EXCEPTION_TEMPLATE_WITHOUT_BACKTRACE =

Template used to render an exception that does not have backtrace

<<-EOF
<div class="message" id="<%= id %>"><pre><%= message.join("\n") %></pre></div>
EOF
EXCEPTION_TEMPLATE_WITH_BACKTRACE =

Template used to render an exception that does have a backtrace

<<-EOF
<div class="message" id="<%= id %>">
    <pre><%= message.join("\n") %></pre>
    <span class="backtrace_links">
        (show: <a class="backtrace_toggle_filtered" id="<%= id %>">filtered backtrace</a>,
               <a class=\"backtrace_toggle_full\" id="<%= id %>">full backtrace</a>)
    </span>
</div>
<div class="backtrace_summary">
    from <%= origin_file %>:<%= origin_line %>:in <%= HTML.escape_html(origin_method.to_s) %>
</div>
<div class="backtrace" id="backtrace_filtered_<%= id %>">
    <%= render_backtrace(filtered_backtrace) %>
</div>
<div class="backtrace" id="backtrace_full_<%= id %>">
    <%= render_backtrace(full_backtrace) %>
</div>
EOF
@@exception_id =
0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(linker) ⇒ ExceptionRendering

Create an exception rendering object using the given linker object

Parameters:

  • linker (#link_to)


36
37
38
39
# File 'lib/metaruby/gui/exception_rendering.rb', line 36

def initialize(linker)
    @linker = linker
    self.user_file_filter = nil
end

Instance Attribute Details

#linker#link_to (readonly)

Returns an object that allows to render a link to an object.

Returns:

  • (#link_to)

    an object that allows to render a link to an object



19
20
21
# File 'lib/metaruby/gui/exception_rendering.rb', line 19

def linker
  @linker
end

#user_file_filter#[]

Returns 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.

Returns:

  • (#[])

    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.



24
25
26
# File 'lib/metaruby/gui/exception_rendering.rb', line 24

def user_file_filter
  @user_file_filter
end

Class Method Details

.parse_backtrace(backtrace) ⇒ Array<(String,Integer,String)>

Parse a backtrace into its file, line and method consistuents

Returns:

  • (Array<(String,Integer,String)>)


87
88
89
# File 'lib/metaruby/gui/exception_rendering.rb', line 87

def self.parse_backtrace(backtrace)
    BacktraceParser.new(backtrace).parse
end

Instance Method Details

#allocate_exception_idObject

Automatically generate an exception ID



176
177
178
# File 'lib/metaruby/gui/exception_rendering.rb', line 176

def allocate_exception_id
    @@exception_id += 1
end

#each_exception_from(e) {|exception| ... } ⇒ Object

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

Yield Parameters:

  • exception (Exception)

    an exception



211
212
213
214
# 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) ⇒ Object

Filters the backtrace to remove framework parts that are not relevant

Parameters:

  • parsed_backtrace (Array<(String,Integer,Symbol)>)

    the parsed backtrace

  • raw_backtrace (Array<(String,Integer,Symbol)>)

    the raw backtrace



156
157
158
159
160
# 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

#headObject

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

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



72
73
74
# File 'lib/metaruby/gui/exception_rendering.rb', line 72

def head
    HEADER
end

#parse_and_filter_backtrace(backtrace) ⇒ (Array<(String,Integer,String)>,Array<(String,Integer,String))> the full and filtered backtraces, as list of tuples (file,line,method)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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

Returns:

  • ((Array<(String,Integer,String)>,Array<(String,Integer,String))> the full and filtered backtraces, as list of tuples (file,line,method))

    (Array<(String,Integer,String)>,Array<(String,Integer,String))> the full and filtered backtraces, as list of tuples (file,line,method)



224
225
226
227
228
229
230
231
# 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) ⇒ String

Render an exception into HTML

Parameters:

  • e (Exception)

    the exception to be rendered

  • reason (String) (defaults to: nil)

    additional string that describes the exception reason

  • id (String) (defaults to: allocate_exception_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.

Returns:

  • (String)


190
191
192
193
194
195
196
197
198
199
200
201
# 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) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Render a backtrace

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



266
267
268
269
270
271
272
273
274
275
276
277
# 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) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Render a single exception object into a HTML block

Parameters:

  • e (Exception)

    the exception

  • id (String)

    the block ID

Returns:

  • (String)


240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# 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

#scriptsObject

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



80
81
82
# File 'lib/metaruby/gui/exception_rendering.rb', line 80

def scripts
    SCRIPTS
end

#user_file?(file) ⇒ Boolean

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

Returns:

  • (Boolean)


169
170
171
# File 'lib/metaruby/gui/exception_rendering.rb', line 169

def user_file?(file)
    user_file_filter[file]
end