| blib/lib/Plack/Middleware/InteractiveDebugger/HTML.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % | 
| statement | 15 | 56 | 26.7 | 
| branch | 0 | 12 | 0.0 | 
| condition | 0 | 2 | 0.0 | 
| subroutine | 5 | 13 | 38.4 | 
| pod | 0 | 8 | 0.0 | 
| total | 20 | 91 | 21.9 | 
| line | stmt | bran | cond | sub | pod | time | code | |
|---|---|---|---|---|---|---|---|---|
| 1 | package Plack::Middleware::InteractiveDebugger::HTML; | |||||||
| 2 | 1 | 1 | 7 | use strict; | ||||
| 1 | 1 | |||||||
| 1 | 32 | |||||||
| 3 | 1 | 1 | 11 | use warnings; | ||||
| 1 | 4 | |||||||
| 1 | 62 | |||||||
| 4 | ||||||||
| 5 | 1 | 1 | 6 | use parent qw(Exporter); | ||||
| 1 | 1 | |||||||
| 1 | 8 | |||||||
| 6 | our @EXPORT = qw( render_full render_source encode_html ); | |||||||
| 7 | ||||||||
| 8 | 1 | 1 | 79 | use Scalar::Util qw(refaddr); | ||||
| 1 | 1 | |||||||
| 1 | 224 | |||||||
| 9 | ||||||||
| 10 |  my $header = < | |||||||
| 11 | ||||||||
| 12 | "http://www.w3.org/TR/html4/loose.dtd"> | |||||||
| 13 | ||||||||
| 14 | ||||||||
| 15 |       | 
|||||||
| 16 | ||||||||
| 17 | ||||||||
| 18 | ||||||||
| 19 | ||||||||
| 25 | ||||||||
| 26 | ||||||||
| 27 |       | 
|||||||
| 28 | EOF | |||||||
| 29 | ||||||||
| 30 |  my $footer = < | |||||||
| 31 | ||||||||
| 32 | Brought to you by DON'T PANIC, your | |||||||
| 33 | friendly Plack powered stacktrace interpreter, inspired by Werkzeug. | |||||||
| 34 | ||||||||
| 35 | ||||||||
| 36 | ||||||||
| 37 | ||||||||
| 38 | EOF | |||||||
| 39 | ||||||||
| 40 |  my $page_html = $header . < | |||||||
| 41 |  %(exception_type) | 
|||||||
| 42 |   | 
|||||||
| 43 |     %(exception)  | 
|||||||
| 44 | ||||||||
| 45 |  StackTrace (most recent call first) | 
|||||||
| 46 | %(summary) | |||||||
| 47 |   | 
|||||||
| 48 |       
  | 
|||||||
| 49 | This is the Copy/Paste friendly version of the stacktrace. | |||||||
| 50 | ||||||||
| 51 | ||||||||
| 52 | ||||||||
| 53 | ||||||||
| 54 |   | 
|||||||
| 55 | The debugger caught an exception in your PSGI application. You can now | |||||||
| 56 | look at the stacktrace which led to the error. | |||||||
| 57 | If you enable JavaScript you can also use additional features such as code | |||||||
| 58 | execution and much more. | |||||||
| 59 | ||||||||
| 60 | EOF | |||||||
| 61 | ||||||||
| 62 |  my $console_html = $header . < | |||||||
| 63 |  Interactive Console | 
|||||||
| 64 |   | 
|||||||
| 65 | In this console you can execute Perl expressions in the context of the | |||||||
| 66 | application. | |||||||
| 67 | ||||||||
| 68 |   The Console requires JavaScript.  | 
|||||||
| 69 | EOF | |||||||
| 70 | ||||||||
| 71 |  my $summary_html = < | |||||||
| 72 |   | 
|||||||
| 73 | StackTrace (most recent call first) | |||||||
| 74 |    
  | 
|||||||
| 75 | ||||||||
| 76 | EOF | |||||||
| 77 | ||||||||
| 78 |  my $frame_html = < | |||||||
| 79 |   | 
|||||||
| 80 |    File "%(filename)", | 
|||||||
| 81 | line %(lineno), | |||||||
| 82 |        in %(function_name)  | 
|||||||
| 83 |    %(current_line)  | 
|||||||
| 84 | ||||||||
| 85 | EOF | |||||||
| 86 | ||||||||
| 87 |  my $source_table_html = ' | 
|||||||
| 88 | ||||||||
| 89 |  my $source_line_html = < | |||||||
| 90 | ||||||||
| 91 | %(lineno) | |||||||
| 92 | %(code) | |||||||
| 93 | ||||||||
| 94 | EOF | |||||||
| 95 | ||||||||
| 96 | 1 | 1 | 7 | no warnings 'qw'; | ||||
| 1 | 2 | |||||||
| 1 | 773 | |||||||
| 97 | my %enc = qw( & & > > < < " " ' ' ); | |||||||
| 98 | ||||||||
| 99 | sub encode_html { | |||||||
| 100 | 0 | 0 | 0 | my $str = shift; | ||||
| 101 | 0 | 0 | $str =~ s/([^\x00-\x21\x23-\x25\x28-\x3b\x3d\x3f-\xff])/$enc{$1} || '' . ord($1) . ';' /ge; | |||||
| 0 | ||||||||
| 102 | 0 | utf8::downgrade($str); | ||||||
| 103 | 0 | $str; | ||||||
| 104 | } | |||||||
| 105 | ||||||||
| 106 | sub render { | |||||||
| 107 | 0 | 0 | 0 | my($html, $vars) = @_; | ||||
| 108 | 0 | $html =~ s/%\((.*?)\)/$vars->{$1}/g; | ||||||
| 109 | 0 | $html; | ||||||
| 110 | } | |||||||
| 111 | ||||||||
| 112 | sub current_line { | |||||||
| 113 | 0 | 0 | 0 | my $frame = shift; | ||||
| 114 | ||||||||
| 115 | 0 | 0 | open my $fh, "<", $frame->filename or return ''; | |||||
| 116 | 0 | my @lines = <$fh>; | ||||||
| 117 | ||||||||
| 118 | 0 | my $line = $lines[$frame->line-1]; | ||||||
| 119 | 0 | $line =~ s/^\s+//; | ||||||
| 120 | 0 | $line; | ||||||
| 121 | } | |||||||
| 122 | ||||||||
| 123 | sub render_frame { | |||||||
| 124 | 0 | 0 | 0 | my($trace, $idx) = @_; | ||||
| 125 | ||||||||
| 126 | 0 | my $frame = $trace->frame($idx); | ||||||
| 127 | ||||||||
| 128 | 0 | 0 | render $frame_html, { | |||||
| 129 | id => refaddr($trace) . "-" . $idx, | |||||||
| 130 | filename => encode_html($frame->filename), | |||||||
| 131 | lineno => $frame->line, | |||||||
| 132 | function_name => $frame->subroutine ? encode_html($frame->subroutine) : '', | |||||||
| 133 | current_line => current_line($frame), | |||||||
| 134 | }; | |||||||
| 135 | } | |||||||
| 136 | ||||||||
| 137 | sub render_line { | |||||||
| 138 | 0 | 0 | 0 | my($frame, $line, $lineno) = @_; | ||||
| 139 | ||||||||
| 140 | 0 | my @classes = ('line'); | ||||||
| 141 | 0 | 0 | push @classes, 'current' if $frame->line == $lineno; | |||||
| 142 | ||||||||
| 143 | 0 | render $source_line_html, { | ||||||
| 144 | classes => join(" ", @classes), | |||||||
| 145 | lineno => $lineno, | |||||||
| 146 | code => encode_html($line), | |||||||
| 147 | }; | |||||||
| 148 | } | |||||||
| 149 | ||||||||
| 150 | sub render_source { | |||||||
| 151 | 0 | 0 | 0 | my $frame = shift; | ||||
| 152 | ||||||||
| 153 | 0 | my $source; | ||||||
| 154 | ||||||||
| 155 | 0 | 0 | open my $fh, "<", $frame->filename or return ''; | |||||
| 156 | 0 | my @lines = <$fh>; | ||||||
| 157 | ||||||||
| 158 | 0 | my $lineno = 1; | ||||||
| 159 | 0 | for my $line (@lines) { | ||||||
| 160 | 0 | $source .= render_line $frame, $line, $lineno++; | ||||||
| 161 | 0 | $source .= "\n"; | ||||||
| 162 | } | |||||||
| 163 | ||||||||
| 164 | 0 | render $source_table_html, { source => $source }; | ||||||
| 165 | } | |||||||
| 166 | ||||||||
| 167 | sub render_summary { | |||||||
| 168 | 0 | 0 | 0 | my $trace = shift; | ||||
| 169 | ||||||||
| 170 | 0 | my @classes = ('traceback'); | ||||||
| 171 | 0 | 0 | unless ($trace->frames) { | |||||
| 172 | 0 | push @classes, 'noframe-traceback'; | ||||||
| 173 | } | |||||||
| 174 | ||||||||
| 175 | 0 | my $out; | ||||||
| 176 | 0 | for my $idx (0..$trace->frame_count-1) { | ||||||
| 177 | 0 |          $out .= ' | 
||||||
| 178 | } | |||||||
| 179 | ||||||||
| 180 | 0 | render $summary_html, { | ||||||
| 181 | classes => join(" ", @classes), | |||||||
| 182 | frames => $out, | |||||||
| 183 | }; | |||||||
| 184 | } | |||||||
| 185 | ||||||||
| 186 | sub render_full { | |||||||
| 187 | 0 | 0 | 0 | my($env, $trace) = @_; | ||||
| 188 | 0 | my $msg = encode_html($trace->frame(0)->as_string(1)); | ||||||
| 189 | 0 | 0 | render $page_html, { | |||||
| 190 | script_name => $env->{SCRIPT_NAME}, | |||||||
| 191 | evalex => 'true', | |||||||
| 192 | console => 'false', | |||||||
| 193 | title => $msg, | |||||||
| 194 | exception => $msg, | |||||||
| 195 | exception_type => ref(($trace->frame(0)->args)[0]) || "Error", | |||||||
| 196 | summary => render_summary($trace), | |||||||
| 197 | plaintext => $trace->as_string, | |||||||
| 198 | traceback_id => refaddr($trace), | |||||||
| 199 | }; | |||||||
| 200 | } | |||||||
| 201 | ||||||||
| 202 | 1; |