Skip to content

Commit

Permalink
By default, exceptions now eliminate most of the Moose internals from…
Browse files Browse the repository at this point in the history
… stack traces
  • Loading branch information
autarch committed Oct 21, 2014
1 parent d1b1426 commit 610fcf3
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 17 deletions.
8 changes: 8 additions & 0 deletions Changes
Expand Up @@ -3,6 +3,14 @@ for, noteworthy changes.

{{$NEXT}}

[ENHANCEMENTS]

- By default, exceptions thrown from inside Moose now remove most of the
Moose internals from their stack trace when stringifying. This makes for
much more readable error messages in most cases. Set the
MOOSE_FULL_EXCEPTION env var to true to get the complete stack trace.


2.1304 2014-09-25 (TRIAL RELEASE)

[BUG FIXES]
Expand Down
65 changes: 48 additions & 17 deletions lib/Moose/Exception.pm
Expand Up @@ -21,13 +21,10 @@ has 'message' => (
"It is lazy and has a default value 'Error'."
);

use overload
'""' => sub {
my $self = shift;
return $self->trace->as_string,
},
use overload(
q{""} => 'as_string',
fallback => 1,
;
);

sub _build_trace {
my $self = shift;
Expand Down Expand Up @@ -57,6 +54,39 @@ sub BUILD {
$self->trace;
}

sub as_string {
my $self = shift;

if ( $ENV{MOOSE_FULL_EXCEPTION} ) {
return $self->trace->as_string;
}

my @frames;
my $last_frame;
my $in_moose = 1;
for my $frame ( $self->trace->frames ) {
if ($in_moose & $frame->package =~ /^Moose(?::|$)/) {
$last_frame = $frame;
next;
}
elsif ($last_frame) {
push @frames, $last_frame;
undef $last_frame;
}

$in_moose = 0;
push @frames, $frame;
}

# This would be a somewhat pathological case, but who knows
return $self->trace->as_string unless @frames;

my $message = ( shift @frames )->as_string( 1, {} ) . "\n";
$message .= join q{}, map { $_->as_string( 0, {} ) . "\n" } @frames;

return $message;
}

1;

# ABSTRACT: Superclass for Moose internal exceptions
Expand All @@ -80,23 +110,24 @@ for use in user code.
Of course if you're writing metaclass traits, it would then make sense to
subclass the relevant Moose exceptions - but only then.
=head1 ATTRIBUTES
=head1 METHODS
=over 4
This class provides the following methods:
=item B<< $exception->trace >>
=head2 $exception->message
This attribute contains the stack trace for the given exception. It
is read-only and isa L<Devel::StackTrace>. It is lazy & dependent
on $exception->message.
This methods returns the exception message.
=item B<< $exception->message >>
=head2 $exception->trace
This attribute contains the exception message. It is read-only and isa Str.
It is lazy and has a default value 'Error'. Every subclass of L<Moose::Exception>
is expected to override _build_message method.
This method returns the stack trace for the given exception.
=back
=head2 $exception->as_string
This method returns a stringified form of the exception, including a stack
trace. By default, this method skips Moose-internal stack frames until it sees
a caller outside of the Moose core. If the C<MOOSE_FULL_EXCEPTION> env var is
true, these frames are included.
=head1 SEE ALSO
Expand Down
74 changes: 74 additions & 0 deletions t/exceptions/stringify.t
@@ -0,0 +1,74 @@
use strict;
use warnings;

use Test::More;
use Try::Tiny;

{
my $e;
{
package Foo;
use Moose;
use Try::Tiny;

try {
has '+foo' => ( is => 'ro' );
}
catch {
$e = $_;
};
}

ok( $e, q{got an exception from a bad has '+foo' declaration} );
like(
$e->as_string,
qr/\QCould not find an attribute by the name of 'foo' to inherit from in Foo/,
'stringification includes the error message'
);
like(
$e->as_string,
qr/\s+Moose::has/,
'stringification includes the call to Moose::has'
);
unlike(
$e->as_string,
qr/Moose::Meta/,
'stringification does not include internal calls to Moose meta classes'
);
}

local $ENV{MOOSE_FULL_EXCEPTION} = 1;
{
my $e;
{
package Bar;
use Moose;
use Try::Tiny;

try {
has '+foo' => ( is => 'ro' );
}
catch {
$e = $_;
};
}

ok( $e, q{got an exception from a bad has '+foo' declaration} );
like(
$e->as_string,
qr/\QCould not find an attribute by the name of 'foo' to inherit from in Bar/,
'stringification includes the error message'
);
like(
$e->as_string,
qr/\s+Moose::has/,
'stringification includes the call to Moose::has'
);
like(
$e->as_string,
qr/Moose::Meta/,
'stringification includes internal calls to Moose meta classes when MOOSE_FULL_EXCEPTION env var is true'
);
}

done_testing;

0 comments on commit 610fcf3

Please sign in to comment.