| Mojolicious documentation | Contained in the Mojolicious distribution. |
Mojo::Exception - Exceptions With Context
use Mojo::Exception; my $e = Mojo::Exception->new;
Mojo::Exception is a container for exceptions with context information.
Mojo::Exception implements the following attributes.
framesmy $frames = $e->frames; $e = $e->frames($frames);
Stacktrace.
linemy $line = $e->line; $e = $e->line([3, 'foo']);
The line where the exception occured.
lines_aftermy $lines = $e->lines_after; $e = $e->lines_after([[1, 'bar'], [2, 'baz']]);
Lines after the line where the exception occured.
lines_beforemy $lines = $e->lines_before; $e = $e->lines_before([[4, 'bar'], [5, 'baz']]);
Lines before the line where the exception occured.
message my $message = $e->message;
$e = $e->message('Oops!');
Exception message.
raw_message my $message = $e->raw_message;
$e = $e->raw_message('Oops!');
Raw unprocessed exception message.
verbosemy $verbose = $e->verbose; $e = $e->verbose(1);
Activate verbose rendering.
Mojo::Exception inherits all methods from Mojo::Base and implements the following new ones.
new my $e = Mojo::Exception->new('Oops!');
my $e = Mojo::Exception->new('Oops!', $files, $name);
Construct a new Mojo::Exception object.
throw Mojo::Exception->throw('Oops!');
Mojo::Exception->throw('Oops!', $files, $name);
Throw exception with stacktrace.
trace$e = $e->trace;
Perform stacktrace.
to_stringmy $string = $e->to_string; my $string = "$e";
Render exception with context.
Mojolicious, Mojolicious::Guides, http://mojolicio.us.
| Mojolicious documentation | Contained in the Mojolicious distribution. |
package Mojo::Exception; use Mojo::Base -base; use overload 'bool' => sub {1}, '""' => sub { shift->to_string }, fallback => 1; use IO::File; use Scalar::Util 'blessed'; has [qw/frames line lines_before lines_after/] => sub { [] }; has [qw/message raw_message/] => 'Exception!'; has verbose => sub { $ENV{MOJO_EXCEPTION_VERBOSE} || 0 }; # "Attempted murder? Now honestly, what is that? # Do they give a Nobel Prize for attempted chemistry?" sub new { my $self = shift->SUPER::new(); # Message return $self unless @_; # Detect $self->_detect(@_); } sub throw { my $self = shift; my $e = Mojo::Exception->new; $e->trace(2); die $e->_detect(@_); } sub trace { my ($e, $start) = @_; $start = 1 unless defined $start; # Trace my @frames; while (my ($p, $f, $l) = caller($start++)) { push @frames, [$p, $f, $l]; # Line if (-r $f) { next unless my $handle = IO::File->new("< $f"); my @lines = <$handle>; push @{$frames[-1]}, $lines[$l - 1]; } } $e->frames(\@frames); $e; } sub _detect { my $self = shift; # Message my $message = shift; return $message if blessed $message && $message->isa('Mojo::Exception'); $self->message($message); $self->raw_message($message); # Trace name and line my @trace; while ($message =~ /at\s+(.+?)\s+line\s+(\d+)/g) { push @trace, {file => $1, line => $2}; } # Stacktrace if (my $first = $self->frames->[0]) { unshift @trace, {file => $first->[1], line => $first->[2]} if $first->[1]; } # Frames foreach my $frame (reverse @trace) { my $file = $frame->{file}; my $line = $frame->{line}; # Readable if (-r $file) { # Slurp my $handle = IO::File->new($file, '<:utf8'); my @lines = <$handle>; # Line $self->_parse_context($line, [\@lines]); return $self; } } # Parse specific file return $self unless my $files = shift; my @lines; for my $lines (@$files) { push @lines, [split /\n/, $lines] } # Cleanup plain messages return $self unless my $name = shift; unless (ref $message) { my $filter = sub { my $num = shift; my $new = "$name line $num"; my $line = $lines[0]->[$num]; $new .= qq/, near "$line"/ if defined $line; $new .= '.'; return $new; }; $message =~ s/\(eval\s+\d+\) line (\d+).*/$filter->($1)/ge; $self->message($message); } # Parse message $name = quotemeta $name; my $line; $line = $1 if $self->message =~ /at\s+$name\s+line\s+(\d+)/; # Stacktrace unless ($line) { for my $frame (@{$self->frames}) { if ($frame->[1] =~ /^\(eval\ \d+\)$/) { $line = $frame->[2]; last; } } } # Context $self->_parse_context($line, \@lines) if $line; $self; } # "You killed zombie Flanders! # He was a zombie?" sub to_string { my $self = shift; # Message only return $self->message unless $self->verbose; # Start with message my $string = ''; $string .= $self->message if $self->message; # Before for my $line (@{$self->lines_before}) { $string .= $line->[0] . ': ' . $line->[1] . "\n"; } # Line $string .= ($self->line->[0] . ': ' . $self->line->[1] . "\n") if $self->line->[0]; # After for my $line (@{$self->lines_after}) { $string .= $line->[0] . ': ' . $line->[1] . "\n"; } $string; } sub _parse_context { my ($self, $line, $lines) = @_; # Wrong file return unless defined $lines->[0]->[$line - 1]; # Context $self->line([$line]); for my $l (@$lines) { my $code = $l->[$line - 1]; chomp $code; push @{$self->line}, $code; } # Cleanup $self->lines_before([]); $self->lines_after([]); # Before for my $i (2 .. 6) { my $previous = $line - $i; last if $previous < 0; if (defined($lines->[0]->[$previous])) { unshift @{$self->lines_before}, [$previous + 1]; for my $l (@$lines) { my $code = $l->[$previous]; chomp $code; push @{$self->lines_before->[0]}, $code; } } } # After for my $i (0 .. 4) { my $next = $line + $i; next if $next < 0; if (defined($lines->[0]->[$next])) { push @{$self->lines_after}, [$next + 1]; for my $l (@$lines) { next unless defined(my $code = $l->[$next]); chomp $code; push @{$self->lines_after->[-1]}, $code; } } } $self; } 1; __END__