Hello community,

here is the log from the commit of package perl-Mojolicious for 
openSUSE:Factory checked in at 2019-07-17 14:26:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1887 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Mojolicious"

Wed Jul 17 14:26:54 2019 rev:112 rq:715697 version:8.21

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes        
2019-06-30 10:22:23.883703782 +0200
+++ 
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1887/perl-Mojolicious.changes  
    2019-07-17 14:26:55.595407112 +0200
@@ -1,0 +2,30 @@
+Mon Jul 15 16:40:28 UTC 2019 - Oliver Kurz <[email protected]>
+
+- updated to 8.21
+   see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+  8.21  2019-07-13
+    - Undeprecated Mojo::Exception::verbose.
+    - Added support for MOJO_EXCEPTION_VERBOSE environment variable.
+    - Improved built in templates to use the same stack trace format as
+      Mojo::Exception.
+
+  8.20  2019-07-08
+    - Improved check function in Mojo::Exception to match regular expressions
+      against stringified exception objects too.
+    - Improved num check in Mojolicious::Validator to accept negative numbers.
+      (kiwiroy)
+
+  8.19  2019-07-07
+    - Deprecated Mojo::Exception::verbose.
+    - Added EXPERIMENTAL check and raise functions to Mojo::Exception.
+    - Added -f option to generate plugin command.
+    - Improved inspect method in Mojo::Exception to be safe to call more than
+      once.
+    - Fixed various stack trace bugs in Mojo::Exception, so your error messages
+      and stack traces can look slightly different than before.
+    - Fixed bugs in reply->exception helper that could result in exceptions
+      without error message.
+    - Fixed daemonize exception handling in Mojo::Server. (skaji)
+
+-------------------------------------------------------------------

Old:
----
  Mojolicious-8.18.tar.gz

New:
----
  Mojolicious-8.21.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.yUPl9y/_old  2019-07-17 14:26:56.447404465 +0200
+++ /var/tmp/diff_new_pack.yUPl9y/_new  2019-07-17 14:26:56.451404452 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           perl-Mojolicious
-Version:        8.18
+Version:        8.21
 Release:        0
 %define cpan_name Mojolicious
 Summary:        Real-time web framework

++++++ Mojolicious-8.18.tar.gz -> Mojolicious-8.21.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/Changes new/Mojolicious-8.21/Changes
--- old/Mojolicious-8.18/Changes        2019-06-28 23:19:12.000000000 +0200
+++ new/Mojolicious-8.21/Changes        2019-07-13 21:37:00.000000000 +0200
@@ -1,4 +1,28 @@
 
+8.21  2019-07-13
+  - Undeprecated Mojo::Exception::verbose.
+  - Added support for MOJO_EXCEPTION_VERBOSE environment variable.
+  - Improved built in templates to use the same stack trace format as
+    Mojo::Exception.
+
+8.20  2019-07-08
+  - Improved check function in Mojo::Exception to match regular expressions
+    against stringified exception objects too.
+  - Improved num check in Mojolicious::Validator to accept negative numbers.
+    (kiwiroy)
+
+8.19  2019-07-07
+  - Deprecated Mojo::Exception::verbose.
+  - Added EXPERIMENTAL check and raise functions to Mojo::Exception.
+  - Added -f option to generate plugin command.
+  - Improved inspect method in Mojo::Exception to be safe to call more than
+    once.
+  - Fixed various stack trace bugs in Mojo::Exception, so your error messages
+    and stack traces can look slightly different than before.
+  - Fixed bugs in reply->exception helper that could result in exceptions
+    without error message.
+  - Fixed daemonize exception handling in Mojo::Server. (skaji)
+
 8.18  2019-06-28
   - Welcome to the Mojolicious core team CandyAngel, Christopher Rasch-Olsen
     Raa and Dan Book.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/META.json 
new/Mojolicious-8.21/META.json
--- old/Mojolicious-8.18/META.json      2019-06-28 23:28:50.000000000 +0200
+++ new/Mojolicious-8.21/META.json      2019-07-14 19:08:42.000000000 +0200
@@ -19,6 +19,10 @@
          "inc",
          "examples",
          "t"
+      ],
+      "package" : [
+         "Mojo::Exception::_Guard",
+         "Mojo::Server::PSGI::_IO"
       ]
    },
    "prereqs" : {
@@ -58,6 +62,6 @@
       },
       "x_IRC" : "irc://irc.freenode.net/#mojo"
    },
-   "version" : "8.18",
-   "x_serialization_backend" : "JSON::PP version 4.02"
+   "version" : "8.21",
+   "x_serialization_backend" : "JSON::PP version 4.04"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/META.yml 
new/Mojolicious-8.21/META.yml
--- old/Mojolicious-8.18/META.yml       2019-06-28 23:28:50.000000000 +0200
+++ new/Mojolicious-8.21/META.yml       2019-07-14 19:08:42.000000000 +0200
@@ -19,6 +19,9 @@
     - inc
     - examples
     - t
+  package:
+    - Mojo::Exception::_Guard
+    - Mojo::Server::PSGI::_IO
 requires:
   IO::Socket::IP: '0.37'
   JSON::PP: '2.27103'
@@ -31,5 +34,5 @@
   homepage: https://mojolicious.org
   license: http://www.opensource.org/licenses/artistic-license-2.0
   repository: https://github.com/mojolicious/mojo.git
-version: '8.18'
+version: '8.21'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/Makefile.PL 
new/Mojolicious-8.21/Makefile.PL
--- old/Mojolicious-8.18/Makefile.PL    2018-12-10 21:34:06.000000000 +0100
+++ new/Mojolicious-8.21/Makefile.PL    2019-07-09 22:57:59.000000000 +0200
@@ -18,9 +18,12 @@
   META_MERGE   => {
     dynamic_config => 0,
     'meta-spec'    => {version => 2},
-    no_index       => {directory => ['examples', 't']},
-    prereqs        => {runtime => {requires => {perl => '5.010001'}}},
-    resources      => {
+    no_index       => {
+      directory => [qw(examples t)],
+      package   => [qw(Mojo::Exception::_Guard Mojo::Server::PSGI::_IO)],
+    },
+    prereqs   => {runtime => {requires => {perl => '5.010001'}}},
+    resources => {
       bugtracker => {web => 'https://github.com/mojolicious/mojo/issues'},
       homepage   => 'https://mojolicious.org',
       license    => 
['http://www.opensource.org/licenses/artistic-license-2.0'],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/README.md 
new/Mojolicious-8.21/README.md
--- old/Mojolicious-8.18/README.md      2018-10-22 17:34:04.000000000 +0200
+++ new/Mojolicious-8.21/README.md      2019-07-09 18:33:40.000000000 +0200
@@ -7,13 +7,19 @@
 
  # [![Build 
Status](https://travis-ci.com/mojolicious/mojo.svg?branch=master)](https://travis-ci.com/mojoliocus/mojo)
 [![Windows build 
status](https://ci.appveyor.com/api/projects/status/b748ehchfsd4edac?svg=true)](https://ci.appveyor.com/project/kraih73737/mojo)
 
-  Back in the early days of the web, many people learned Perl because of a
-  wonderful Perl library called [CGI](https://metacpan.org/module/CGI). It was
-  simple enough to get started without knowing much about the language and
-  powerful enough to keep you going, learning by doing was much fun. While
-  most of the techniques used are outdated now, the idea behind it is not.
-  Mojolicious is a new endeavor to implement this idea using bleeding edge
-  technologies.
+  Mojolicious is a fresh take on Perl web development, based on years of
+  experience developing the Catalyst framework, and utilizing the latest
+  web standards and technologies. You can get started with your project
+  quickly, with a framework that grows with your needs.
+
+  The Mojo stack provides a consistent set of components that can be used in
+  any project. The guides cover most aspects of using the framework and the
+  components have comprehensive reference documentation. Mojolicious is a
+  real-time web framework, which allows a new class of web applications
+  using WebSockets and having long-running requests without blocking.
+
+  Join us now, and be a part of a friendly and knowledgeable community of
+  developers!
 
 ## Features
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Content/MultiPart.pm 
new/Mojolicious-8.21/lib/Mojo/Content/MultiPart.pm
--- old/Mojolicious-8.18/lib/Mojo/Content/MultiPart.pm  2019-06-21 
22:48:51.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojo/Content/MultiPart.pm  2019-07-09 
20:50:53.000000000 +0200
@@ -304,8 +304,8 @@
   my $multi
     = Mojo::Content::MultiPart->new({parts => [Mojo::Content::Single->new]});
 
-Construct a new L<Mojo::Content::MultiPart> object and subscribe to L</"read">
-event with default content parser.
+Construct a new L<Mojo::Content::MultiPart> object and subscribe to event
+L<Mojo::Content/"read"> with default content parser.
 
 =head1 SEE ALSO
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Content/Single.pm 
new/Mojolicious-8.21/lib/Mojo/Content/Single.pm
--- old/Mojolicious-8.18/lib/Mojo/Content/Single.pm     2019-06-21 
22:48:51.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojo/Content/Single.pm     2019-07-09 
20:50:43.000000000 +0200
@@ -152,8 +152,8 @@
   my $single = Mojo::Content::Single->new(asset => Mojo::Asset::File->new);
   my $single = Mojo::Content::Single->new({asset => Mojo::Asset::File->new});
 
-Construct a new L<Mojo::Content::Single> object and subscribe to L</"read">
-event with default content parser.
+Construct a new L<Mojo::Content::Single> object and subscribe to event
+L<Mojo::Content/"read"> with default content parser.
 
 =head2 parse
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Cookie/Response.pm 
new/Mojolicious-8.21/lib/Mojo/Cookie/Response.pm
--- old/Mojolicious-8.18/lib/Mojo/Cookie/Response.pm    2019-06-21 
22:48:57.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojo/Cookie/Response.pm    2019-07-07 
14:10:25.000000000 +0200
@@ -139,7 +139,7 @@
   my $samesite = $cookie->samesite;
   $cookie      = $cookie->samesite('Lax');
 
-SameSite value. Note that this attribute is EXPERIMENTAL because even though
+SameSite value. Note that this attribute is B<EXPERIMENTAL> because even though
 most commonly used browsers support the feature, there is no specification yet
 besides
 L<this draft|https://tools.ietf.org/html/draft-west-first-party-cookies-07>.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/DOM/CSS.pm 
new/Mojolicious-8.21/lib/Mojo/DOM/CSS.pm
--- old/Mojolicious-8.18/lib/Mojo/DOM/CSS.pm    2019-06-21 22:48:57.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/DOM/CSS.pm    2019-07-07 14:10:59.000000000 
+0200
@@ -387,7 +387,7 @@
 
 An C<E> element whose C<foo> attribute value is exactly equal to any
 (ASCII-range) case-permutation of C<bar>. Note that this selector is
-EXPERIMENTAL and might change without warning!
+B<EXPERIMENTAL> and might change without warning!
 
   my $case_insensitive = $css->select('input[type="hidden" i]');
   my $case_insensitive = $css->select('input[type=hidden i]');
@@ -555,7 +555,7 @@
 =head2 E:not(s1, s2)
 
 An C<E> element that does not match either compound selector C<s1> or compound
-selector C<s2>. Note that support for compound selectors is EXPERIMENTAL and
+selector C<s2>. Note that support for compound selectors is B<EXPERIMENTAL> and
 might change without warning!
 
   my $others = $css->select('div p:not(:first-child, :last-child)');
@@ -567,7 +567,8 @@
 =head2 E:matches(s1, s2)
 
 An C<E> element that matches compound selector C<s1> and/or compound selector
-C<s2>. Note that this selector is EXPERIMENTAL and might change without 
warning!
+C<s2>. Note that this selector is B<EXPERIMENTAL> and might change without
+warning!
 
   my $headers = $css->select(':matches(section, article, aside, nav) h1');
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/DynamicMethods.pm 
new/Mojolicious-8.21/lib/Mojo/DynamicMethods.pm
--- old/Mojolicious-8.18/lib/Mojo/DynamicMethods.pm     2019-06-21 
22:48:50.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojo/DynamicMethods.pm     2019-07-07 
14:11:08.000000000 +0200
@@ -95,7 +95,7 @@
     };
   }
 
-Note that this module is EXPERIMENTAL and might change without warning!
+Note that this module is B<EXPERIMENTAL> and might change without warning!
 
 =head1 FUNCTIONS
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Exception.pm 
new/Mojolicious-8.21/lib/Mojo/Exception.pm
--- old/Mojolicious-8.18/lib/Mojo/Exception.pm  2019-06-21 22:48:46.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/Exception.pm  2019-07-13 21:17:39.000000000 
+0200
@@ -2,19 +2,58 @@
 use Mojo::Base -base;
 use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
 
+use Exporter 'import';
 use Mojo::Util 'decode';
+use Scalar::Util 'blessed';
 
 has [qw(frames line lines_after lines_before)] => sub { [] };
 has message                                    => 'Exception!';
-has 'verbose';
+has verbose => sub { $ENV{MOJO_EXCEPTION_VERBOSE} };
+
+our @EXPORT_OK = qw(check raise);
+
+sub check {
+  my ($err, @spec) = @_ % 2 ? @_ : ($@, @_);
+
+  # Finally (search backwards since it is usually at the end)
+  my $guard;
+  for (my $i = $#spec - 1; $i >= 0; $i -= 2) {
+    ($guard = Mojo::Exception::_Guard->new(finally => $spec[$i + 1])) and last
+      if $spec[$i] eq 'finally';
+  }
+
+  return undef unless $err;
+
+  my ($default, $handler);
+  my ($is_obj, $str) = (!!blessed($err), "$err");
+CHECK: for (my $i = 0; $i < @spec; $i += 2) {
+    my ($checks, $cb) = @spec[$i, $i + 1];
+
+    ($default = $cb) and next if $checks eq 'default';
+
+    for my $c (ref $checks eq 'ARRAY' ? @$checks : $checks) {
+      my $is_re = !!ref $c;
+      ($handler = $cb) and last CHECK if $is_obj && !$is_re && $err->isa($c);
+      ($handler = $cb) and last CHECK if $is_re  && $str =~ $c;
+    }
+  }
+
+  # Rethrow if no handler could be found
+  die $err unless $handler ||= $default;
+  $handler->($_) for $err;
+
+  return 1;
+}
 
 sub inspect {
   my ($self, @sources) = @_;
 
+  return $self if @{$self->line};
+
   # Extract file and line from message
   my @files;
-  my $msg = $self->lines_before([])->line([])->lines_after([])->message;
-  while ($msg =~ /at\s+(.+?)\s+line\s+(\d+)/g) { unshift @files, [$1, $2] }
+  my $msg = $self->message;
+  unshift @files, [$1, $2] while $msg =~ /at\s+(.+?)\s+line\s+(\d+)/g;
 
   # Extract file and line from stack trace
   if (my $zero = $self->frames->[0]) { push @files, [$zero->[1], $zero->[2]] }
@@ -32,24 +71,50 @@
   return $self;
 }
 
-sub new { @_ > 1 ? shift->SUPER::new(message => shift) : shift->SUPER::new }
+sub new {
+  defined $_[1] ? shift->SUPER::new(message => shift) : shift->SUPER::new;
+}
+
+sub raise {
+  my ($class, $err) = @_ > 1 ? (@_) : (__PACKAGE__, shift);
+
+  if (!$class->can('new')) {
+    die $@ unless eval "package $class; use Mojo::Base 'Mojo::Exception'; 1";
+  }
+  elsif (!$class->isa(__PACKAGE__)) {
+    die "$class is not a Mojo::Exception subclass";
+  }
+
+  CORE::die $class->new($err)->trace;
+}
 
 sub to_string {
   my $self = shift;
 
   my $str = $self->message;
-  return $str unless $self->verbose;
-
   $str .= "\n" unless $str =~ /\n$/;
-  $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_before};
-  $str .= $self->line->[0] . ': ' . $self->line->[1] . "\n" if 
$self->line->[0];
-  $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_after};
-  $str .= "$_->[1]:$_->[2] ($_->[0])\n"   for @{$self->frames};
+
+  my $line = $self->line;
+  if (@$line) {
+    $str .= "Context:\n";
+    $str .= "  $_->[0]: $_->[1]\n" for @{$self->lines_before};
+    $str .= "  $line->[0]: $line->[1]\n";
+    $str .= "  $_->[0]: $_->[1]\n" for @{$self->lines_after};
+  }
+
+  my $frames = $self->frames;
+  if (my $max = @$frames) {
+    $str .= "Traceback (most recent call first):\n";
+    my $offset = $self->verbose ? $#$frames : $#$frames <= 4 ? $#$frames : 4;
+    $str .= qq{  File "$_->[1]", line $_->[2], in "$_->[0]"\n}
+      for @$frames[0 .. $offset];
+    $str .= "  ...\n" if $max > ($offset + 1);
+  }
 
   return $str;
 }
 
-sub throw { CORE::die shift->new(shift)->trace(2)->inspect }
+sub throw { CORE::die shift->new(shift)->trace }
 
 sub trace {
   my ($self, $start) = (shift, shift // 1);
@@ -89,33 +154,129 @@
   }
 }
 
+package Mojo::Exception::_Guard;
+use Mojo::Base -base;
+
+sub DESTROY { shift->{finally}->() }
+
 1;
 
 =encoding utf8
 
 =head1 NAME
 
-Mojo::Exception - Exceptions with context
+Mojo::Exception - Exception base class
 
 =head1 SYNOPSIS
 
-  use Mojo::Exception;
+  # Create exception classes
+  package MyApp::X::Foo {
+    use Mojo::Base 'Mojo::Exception';
+  }
+  package MyApp::X::Bar {
+    use Mojo::Base 'Mojo::Exception';
+  }
 
-  # Throw exception and show stack trace
-  eval { Mojo::Exception->throw('Something went wrong!') };
-  say "$_->[1]:$_->[2]" for @{$@->frames};
+  # Throw exceptions and handle them gracefully
+  use Mojo::Exception 'check';
+  eval {
+    MyApp::X::Foo->throw('Something went wrong!');
+  };
+  check(
+    'MyApp::X::Foo' => sub { say "Foo: $_" },
+    'MyApp::X::Bar' => sub { say "Bar: $_" }
+  );
 
-  # Customize exception
+  # Generate exception classes on demand
+  use Mojo::Exception qw(check raise);
   eval {
-    my $e = Mojo::Exception->new('Died at test.pl line 3.');
-    die $e->trace(2)->inspect->verbose(1);
+    raise 'MyApp::X::Name', 'The name Minion is already taken';
   };
-  say $@;
+  check(
+    'MyApp::X::Name' => sub { say "Name error: $_" },
+    default          => sub { say "Error: $_" }
+  );
 
 =head1 DESCRIPTION
 
 L<Mojo::Exception> is a container for exceptions with context information.
 
+=head1 FUNCTIONS
+
+L<Mojo::Exception> implements the following functions, which can be imported
+individually.
+
+=head2 check
+
+  my $bool = check 'MyApp::X::Foo' => sub {...};
+  my $bool = check $err, 'MyApp::X::Foo' => sub {...};
+
+Process exceptions by dispatching them to handlers with one or more matching
+conditions. Exceptions that could not be handled will be rethrown 
automatically.
+By default C<$@> will be used as exception source, so C<check> needs to be
+called right after C<eval>. Note that this function is B<EXPERIMENTAL> and 
might
+change without warning!
+
+  # Handle various types of exceptions
+  eval {
+    dangerous_code();
+  };
+  check(
+    'MyApp::X::Foo'     => sub { say "Foo: $_" },
+    qr/^Could not open/ => sub { say "Open error: $_" },
+    default             => sub { say "Something went wrong: $_" },
+    finally             => sub { say 'Dangerous code is done' }
+  );
+
+Matching conditions can be class names for ISA checks on exception objects, or
+regular expressions to match string exceptions and stringified exception
+objects. The matching exception will be the first argument passed to the
+callback, and is also available as C<$_>.
+
+  # Catch MyApp::X::Foo object or a specific string exception
+  eval {
+    dangerous_code();
+  };
+  check(
+    'MyApp::X::Foo'     => sub { say "Foo: $_" },
+    qr/^Could not open/ => sub { say "Open error: $_" }
+  );
+
+An array reference can be used to share the same handler with multiple
+conditions, of which only one needs to match. And since exception handlers are
+just callbacks, they can also throw their own exceptions.
+
+  # Handle MyApp::X::Foo and MyApp::X::Bar the same
+  eval {
+    dangerous_code();
+  };
+  check(
+    ['MyApp::X::Foo', 'MyApp::X::Bar'] => sub { die "Foo/Bar: $_" }
+  );
+
+There are currently two keywords you can use to set special handlers. The
+C<default> handler is used when no other handler matched. And the C<finally>
+handler runs always, it does not affect normal handlers and even runs if the
+exception was rethrown or if there was no exception to be handled at all.
+
+  # Use "default" to catch everything
+  eval {
+    dangerous_code();
+  };
+  check(
+    default => sub { say "Error: $_" },
+    finally => sub { say 'Dangerous code is done' }
+  );
+
+=head2 raise
+
+  raise 'Something went wrong!';
+  raise 'MyApp::X::Foo', 'Something went wrong!';
+
+Raise a L<Mojo::Exception>, if the class does not exist yet (classes are 
checked
+for a C<new> method), one is created as a L<Mojo::Exception> subclass on 
demand.
+Note that this function is B<EXPERIMENTAL> and might change without warning!
+
 =head1 ATTRIBUTES
 
 L<Mojo::Exception> implements the following attributes.
@@ -164,7 +325,8 @@
   my $bool = $e->verbose;
   $e       = $e->verbose($bool);
 
-Enable context information for L</"to_string">.
+Show more information with L</"to_string">, such as additional L</"frames">,
+defaults to the value of the C<MOJO_EXCEPTION_VERBOSE> environment variable.
 
 =head1 METHODS
 
@@ -190,10 +352,9 @@
 
   my $str = $e->to_string;
 
-Render exception.
-
-  # Render exception with context
-  say $e->verbose(1)->to_string;
+Render exception. Note that the output format may change as more features are
+added, only the error message at the beginning is guaranteed not to be modified
+to allow regex matching.
 
 =head2 throw
 
@@ -202,7 +363,7 @@
 Throw exception from the current execution context.
 
   # Longer version
-  die Mojo::Exception->new('Something went wrong!')->trace->inspect;
+  die Mojo::Exception->new('Something went wrong!')->trace;
 
 =head2 trace
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Headers.pm 
new/Mojolicious-8.21/lib/Mojo/Headers.pm
--- old/Mojolicious-8.18/lib/Mojo/Headers.pm    2019-06-28 18:11:58.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/Headers.pm    2019-07-07 14:11:28.000000000 
+0200
@@ -412,7 +412,7 @@
   $heders = $headers->dehop;
 
 Remove hop-by-hop headers that should not be retransmitted. Note that this
-method is EXPERIMENTAL and might change without warning!
+method is B<EXPERIMENTAL> and might change without warning!
 
 =head2 dnt
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/IOLoop/Stream.pm 
new/Mojolicious-8.21/lib/Mojo/IOLoop/Stream.pm
--- old/Mojolicious-8.18/lib/Mojo/IOLoop/Stream.pm      2019-06-28 
18:11:58.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojo/IOLoop/Stream.pm      2019-07-07 
14:11:59.000000000 +0200
@@ -244,8 +244,8 @@
   $msg     = $msg->high_water_mark(1024);
 
 Maximum size of L</"write"> buffer in bytes before L</"can_write"> returns
-false, defaults to C<1048576> (1MiB). Note that this attribute is EXPERIMENTAL
-and might change without warning!
+false, defaults to C<1048576> (1MiB). Note that this attribute is
+B<EXPERIMENTAL> and might change without warning!
 
 =head2 reactor
 
@@ -271,7 +271,8 @@
   my $num = $stream->bytes_waiting;
 
 Number of bytes that have been enqueued with L</"write"> and are waiting to be
-written. Note that this method is EXPERIMENTAL and might change without 
warning!
+written. Note that this method is B<EXPERIMENTAL> and might change without
+warning!
 
 =head2 bytes_written
 
@@ -284,7 +285,7 @@
   my $bool = $stream->can_write;
 
 Returns true if calling L</"write"> is safe. Note that this method is
-EXPERIMENTAL and might change without warning!
+B<EXPERIMENTAL> and might change without warning!
 
 =head2 close
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Promise.pm 
new/Mojolicious-8.21/lib/Mojo/Promise.pm
--- old/Mojolicious-8.18/lib/Mojo/Promise.pm    2019-06-21 22:48:47.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/Promise.pm    2019-07-07 14:12:31.000000000 
+0200
@@ -342,7 +342,7 @@
 items while optionally limiting concurrency. Returns a L<Mojo::Promise> that
 collects the results in the same manner as L</all>. If any item's promise is
 rejected, any remaining items which have not yet been mapped will not be. Note
-that this method is EXPERIMENTAL and might change without warning!
+that this method is B<EXPERIMENTAL> and might change without warning!
 
   # Perform 3 requests at a time concurrently
   Mojo::Promise->map({concurrency => 3}, sub { $ua->get_p($_) }, @urls)
@@ -449,8 +449,8 @@
 
 Create a new L<Mojo::Promise> object with a timer or attach a timer to an
 existing promise. The promise will be resolved after the given amount of time 
in
-seconds with or without a value. Note that this method is EXPERIMENTAL and 
might
-change without warning!
+seconds with or without a value. Note that this method is B<EXPERIMENTAL> and
+might change without warning!
 
 =head2 timeout
 
@@ -461,7 +461,7 @@
 Create a new L<Mojo::Promise> object with a timeout or attach a timeout to an
 existing promise. The promise will be rejected after the given amount of time 
in
 seconds with a reason, which defaults to C<Promise timeout>. Note that this
-method is EXPERIMENTAL and might change without warning!
+method is B<EXPERIMENTAL> and might change without warning!
 
 =head2 wait
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Server.pm 
new/Mojolicious-8.21/lib/Mojo/Server.pm
--- old/Mojolicious-8.18/lib/Mojo/Server.pm     2019-06-21 22:48:46.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/Server.pm     2019-06-29 19:39:13.000000000 
+0200
@@ -30,7 +30,7 @@
   # Fork and kill parent
   die "Can't fork: $!" unless defined(my $pid = fork);
   exit 0 if $pid;
-  POSIX::setsid or die "Can't start a new session: $!";
+  POSIX::setsid == -1 and die "Can't start a new session: $!";
 
   # Close filehandles
   open STDIN,  '<',  '/dev/null';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojo/Template.pm 
new/Mojolicious-8.21/lib/Mojo/Template.pm
--- old/Mojolicious-8.18/lib/Mojo/Template.pm   2019-06-21 22:48:56.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojo/Template.pm   2019-07-09 20:05:03.000000000 
+0200
@@ -146,8 +146,7 @@
   unless ($compiled) {
     my $code = $self->_compile->code;
     monkey_patch $self->namespace, '_escape', $self->escape;
-    return Mojo::Exception->new($@)->inspect($self->unparsed, $code)
-      ->trace->verbose(1)
+    return Mojo::Exception->new($@)->inspect($self->unparsed, $code)->trace
       unless $compiled = eval $self->_wrap($code, @_);
     $self->compiled($compiled);
   }
@@ -156,7 +155,7 @@
   local $SIG{__DIE__} = sub {
     CORE::die $_[0] if ref $_[0];
     CORE::die Mojo::Exception->new(shift)
-      ->trace->inspect($self->unparsed, $self->code)->verbose(1);
+      ->trace->inspect($self->unparsed, $self->code);
   };
 
   my $output;
@@ -413,14 +412,16 @@
 error messages with context.
 
   Bareword "xx" not allowed while "strict subs" in use at template line 4.
-  2: </head>
-  3: <body>
-  4: % my $i = 2; xx
-  5: %= $i * 2
-  6: </body>
-  template:4 (Mojo::Template::Sandbox)
-  path/to/Mojo/Template.pm:123 (Mojo::Template)
-  path/to/myapp.pl:123 (main)
+  Context:
+    2: </head>
+    3: <body>
+    4: % my $i = 2; xx
+    5: %= $i * 2
+    6: </body>
+  Traceback (most recent call first):
+    File "template", line 4, in "Mojo::Template::Sandbox"
+    File "path/to/Mojo/Template.pm", line 123, in "Mojo::Template"
+    File "path/to/myapp.pl", line 123, in "main"
 
 =head1 ATTRIBUTES
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/app.pm 
new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/app.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/app.pm 
2019-06-21 22:49:08.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/app.pm 
2019-06-29 21:18:51.000000000 +0200
@@ -9,12 +9,6 @@
 sub run {
   my ($self, $class) = (shift, shift || 'MyApp');
 
-  # Prevent bad applications
-  die <<EOF unless $class =~ /^[A-Z](?:\w|::)+$/;
-Your application name has to be a well formed (CamelCase) Perl module name
-like "MyApp".
-EOF
-
   # Script
   my $name = class_to_file $class;
   $self->render_to_rel_file('mojo', "$name/script/$name", {class => $class});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/makefile.pm 
new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/makefile.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/makefile.pm    
2019-06-21 22:49:07.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/makefile.pm    
2019-06-29 21:33:56.000000000 +0200
@@ -1,8 +1,6 @@
 package Mojolicious::Command::Author::generate::makefile;
 use Mojo::Base 'Mojolicious::Command';
 
-use Mojolicious;
-
 has description => 'Generate "Makefile.PL"';
 has usage       => sub { shift->extract_usage };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/plugin.pm 
new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/plugin.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Command/Author/generate/plugin.pm      
2019-06-21 22:49:07.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Command/Author/generate/plugin.pm      
2019-06-29 21:41:03.000000000 +0200
@@ -1,20 +1,21 @@
 package Mojolicious::Command::Author::generate::plugin;
 use Mojo::Base 'Mojolicious::Command';
 
-use Mojo::Util qw(camelize class_to_path);
-use Mojolicious;
+use Mojo::Util qw(camelize class_to_path getopt);
 
 has description => 'Generate Mojolicious plugin directory structure';
 has usage       => sub { shift->extract_usage };
 
 sub run {
-  my ($self, $name) = (shift, shift || 'MyPlugin');
+  my ($self, @args) = @_;
+
+  getopt \@args, 'f|full' => \(my $full);
 
   # Class
-  my $class = $name =~ /^[a-z]/ ? camelize $name : $name;
-  $class = "Mojolicious::Plugin::$class";
-  my $app = class_to_path $class;
-  my $dir = join '-', split('::', $class);
+  my $name  = $args[0] // 'MyPlugin';
+  my $class = $full ? $name : "Mojolicious::Plugin::$name";
+  my $dir   = join '-', split('::', $class);
+  my $app   = class_to_path $class;
   $self->render_to_rel_file('class', "$dir/lib/$app",
     {class => $class, name => $name});
 
@@ -40,8 +41,10 @@
 
     mojo generate plugin
     mojo generate plugin TestPlugin
+    mojo generate plugin -f MyApp::Plugin::AwesomeFeature
 
   Options:
+    -f, --full   Do not prepend "Mojolicious::Plugin::" to the plugin name
     -h, --help   Show this summary of available options
 
 =head1 DESCRIPTION
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-8.18/lib/Mojolicious/Plugin/DefaultHelpers.pm 
new/Mojolicious-8.21/lib/Mojolicious/Plugin/DefaultHelpers.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Plugin/DefaultHelpers.pm       
2019-06-28 18:11:58.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Plugin/DefaultHelpers.pm       
2019-07-07 14:13:07.000000000 +0200
@@ -96,7 +96,7 @@
   my ($page, $c, $e) = @_;
 
   my $app = $c->app;
-  $app->log->error($e = _exception($e) ? $e : 
Mojo::Exception->new($e)->inspect)
+  $app->log->error(($e = _is_e($e) ? $e : Mojo::Exception->new($e))->inspect)
     if $page eq 'exception';
 
   # Filtered stash snapshot
@@ -119,7 +119,6 @@
   return $c;
 }
 
-sub _exception { blessed $_[0] && $_[0]->isa('Mojo::Exception') }
 
 sub _fallbacks {
   my ($c, $options, $template, $bundled) = @_;
@@ -161,6 +160,8 @@
   return $c;
 }
 
+sub _is_e { blessed $_[0] && $_[0]->isa('Mojo::Exception') }
+
 sub _is_fresh {
   my ($c, %options) = @_;
   return $c->app->static->is_fresh($c, \%options);
@@ -529,8 +530,8 @@
 
 Perform non-blocking C<GET> request and forward response as efficiently as
 possible, takes the same arguments as L<Mojo::UserAgent/"get"> and returns a
-L<Mojo::Promise> object. Note that this helper is EXPERIMENTAL and might change
-without warning!
+L<Mojo::Promise> object. Note that this helper is B<EXPERIMENTAL> and might
+change without warning!
 
   # Forward with exception handling
   $c->proxy->get_p('http://mojolicious.org')->catch(sub {
@@ -545,8 +546,8 @@
 
 Perform non-blocking C<POST> request and forward response as efficiently as
 possible, takes the same arguments as L<Mojo::UserAgent/"post"> and returns a
-L<Mojo::Promise> object. Note that this helper is EXPERIMENTAL and might change
-without warning!
+L<Mojo::Promise> object. Note that this helper is B<EXPERIMENTAL> and might
+change without warning!
 
   # Forward with exception handling
   $c->proxy->post_p('example.com' => form => {test => 'pass'})->catch(sub {
@@ -561,7 +562,7 @@
 
 Perform non-blocking request for a custom L<Mojo::Transaction::HTTP> object and
 forward response as efficiently as possible, returns a L<Mojo::Promise> object.
-Note that this helper is EXPERIMENTAL and might change without warning!
+Note that this helper is B<EXPERIMENTAL> and might change without warning!
 
   # Forward with exception handling
   my $tx = $c->ua->build_tx(GET => 'http://mojolicious.org');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojolicious/Renderer.pm 
new/Mojolicious-8.21/lib/Mojolicious/Renderer.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Renderer.pm    2019-06-21 
22:49:04.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Renderer.pm    2019-07-07 
14:13:38.000000000 +0200
@@ -287,7 +287,7 @@
 
 Try to negotiate compression for dynamically generated response content and
 C<gzip> compress it automatically, defaults to false. Note that this attribute
-is EXPERIMENTAL and might change without warning!
+is B<EXPERIMENTAL> and might change without warning!
 
 =head2 default_format
 
@@ -335,7 +335,7 @@
   $renderer = $renderer->min_compress_size(1024);
 
 Minimum output size in bytes required for compression to be used if enabled,
-defaults to C<860>. Note that this attribute is EXPERIMENTAL and might change
+defaults to C<860>. Note that this attribute is B<EXPERIMENTAL> and might 
change
 without warning!
 
 =head2 paths
@@ -427,7 +427,7 @@
     Mojolicious::Controller->new, $output, $format, $status);
 
 Finalize dynamically generated response content and L</"compress"> it if
-possible. Note that this method is EXPERIMENTAL and might change without
+possible. Note that this method is B<EXPERIMENTAL> and might change without
 warning!
 
 =head2 template_for
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojolicious/Sessions.pm 
new/Mojolicious-8.21/lib/Mojolicious/Sessions.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Sessions.pm    2019-06-21 
22:49:02.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Sessions.pm    2019-07-07 
14:13:51.000000000 +0200
@@ -148,8 +148,8 @@
   $sessions    = $sessions->samesite('Strict');
 
 Set the SameSite value on all session cookies, defaults to C<Lax>. Note that
-this attribute is EXPERIMENTAL because even though most commonly used browsers
-support the feature, there is no specification yet besides
+this attribute is B<EXPERIMENTAL> because even though most commonly used
+browsers support the feature, there is no specification yet besides
 L<this draft|https://tools.ietf.org/html/draft-west-first-party-cookies-07>.
 
   # Disable SameSite feature
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojolicious/Types.pm 
new/Mojolicious-8.21/lib/Mojolicious/Types.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Types.pm       2019-06-21 
22:49:06.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Types.pm       2019-07-07 
14:14:09.000000000 +0200
@@ -146,7 +146,7 @@
 Detect MIME type for L<Mojolicious::Controller> object unless a C<Content-Type>
 response header has already been set, defaults to using the MIME type for the
 C<txt> extension if no better alternative could be found. Note that this method
-is EXPERIMENTAL and might change without warning!
+is B<EXPERIMENTAL> and might change without warning!
 
 These options are currently available:
 
@@ -179,7 +179,7 @@
 
   my $type = $types->file_type('foo/bar.png');
 
-Get MIME type for file path. Note that this method is EXPERIMENTAL and might
+Get MIME type for file path. Note that this method is B<EXPERIMENTAL> and might
 change without warning!
 
 =head2 type
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojolicious/Validator.pm 
new/Mojolicious-8.21/lib/Mojolicious/Validator.pm
--- old/Mojolicious-8.18/lib/Mojolicious/Validator.pm   2019-06-21 
22:49:02.000000000 +0200
+++ new/Mojolicious-8.21/lib/Mojolicious/Validator.pm   2019-07-09 
19:25:38.000000000 +0200
@@ -49,7 +49,7 @@
 
 sub _num {
   my ($v, $name, $value, $min, $max) = @_;
-  return 1 if $value !~ /^[0-9]+$/;
+  return 1 if $value !~ /^-?[0-9]+$/;
   return defined $min && $min > $value || defined $max && $max < $value;
 }
 
@@ -109,11 +109,12 @@
 
   $v = $v->num;
   $v = $v->num(2, 5);
+  $v = $v->num(-3, 7);
   $v = $v->num(2, undef);
   $v = $v->num(undef, 5);
 
-String value needs to be a non-fractional number and if provided in the given
-range.
+String value needs to be a non-fractional number (positive or negative) and if
+provided in the given range.
 
 =head2 size
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-8.18/lib/Mojolicious/resources/templates/mojo/debug.html.ep 
new/Mojolicious-8.21/lib/Mojolicious/resources/templates/mojo/debug.html.ep
--- old/Mojolicious-8.18/lib/Mojolicious/resources/templates/mojo/debug.html.ep 
2019-01-01 19:21:42.000000000 +0100
+++ new/Mojolicious-8.21/lib/Mojolicious/resources/templates/mojo/debug.html.ep 
2019-07-14 19:07:28.000000000 +0200
@@ -322,7 +322,8 @@
                 % for my $frame (@{$exception->frames}) {
                   <tr>
                     <td class="striped value wide">
-                      <pre><%= $frame->[1] . ':' . $frame->[2] %></pre>
+                      <pre>File "<%= $frame->[1] %>", line <%= 
+                        $frame->[2] %>, in "<%= $frame->[0] %>"</pre>
                     </td>
                   </tr>
                 % }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/lib/Mojolicious.pm 
new/Mojolicious-8.21/lib/Mojolicious.pm
--- old/Mojolicious-8.18/lib/Mojolicious.pm     2019-06-28 01:35:47.000000000 
+0200
+++ new/Mojolicious-8.21/lib/Mojolicious.pm     2019-07-09 23:01:29.000000000 
+0200
@@ -59,7 +59,7 @@
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Supervillain';
-our $VERSION  = '8.18';
+our $VERSION  = '8.21';
 
 sub BUILD_DYNAMIC {
   my ($class, $method, $dyn_methods) = @_;
@@ -197,10 +197,11 @@
 
 sub startup { }
 
+sub _die { CORE::die ref $_[0] ? $_[0] : Mojo::Exception->new(shift)->trace }
+
 sub _exception {
   my ($next, $c) = @_;
-  local $SIG{__DIE__}
-    = sub { ref $_[0] ? CORE::die $_[0] : Mojo::Exception->throw(shift) };
+  local $SIG{__DIE__} = \&_die;
   $c->helpers->reply->exception($@) unless eval { $next->(); 1 };
 }
 
@@ -1156,6 +1157,8 @@
 
 Simone Tampieri
 
+Shoichi Kaji
+
 Shu Cho
 
 Skye Shaw
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/mojo/exception.t 
new/Mojolicious-8.21/t/mojo/exception.t
--- old/Mojolicious-8.18/t/mojo/exception.t     2019-05-31 21:28:59.000000000 
+0200
+++ new/Mojolicious-8.21/t/mojo/exception.t     2019-07-13 21:17:36.000000000 
+0200
@@ -1,16 +1,34 @@
 use Mojo::Base -strict;
 
 use Test::More;
-use Mojo::Exception;
+use Mojo::Exception qw(check raise);
 use Mojo::File 'path';
 
+package MojoTest::X::Foo;
+use Mojo::Base 'Mojo::Exception';
+
+package MojoTest::X::Bar;
+use Mojo::Base 'Mojo::Exception';
+
+package MojoTest::X::Yada;
+use Mojo::Base 'MojoTest::X::Bar';
+
+package main;
+
+# Verbose
+{
+  ok(!Mojo::Exception->new->verbose, 'not verbose');
+  local $ENV{MOJO_EXCEPTION_VERBOSE} = 1;
+  ok(Mojo::Exception->new->verbose, 'verbose');
+}
+
 # Basics
 my $e = Mojo::Exception->new;
 is $e->message, 'Exception!', 'right message';
-is "$e", 'Exception!', 'right message';
+is "$e", "Exception!\n", 'right message';
 $e = Mojo::Exception->new('Test!');
 is $e->message, 'Test!', 'right message';
-is "$e", 'Test!', 'right message';
+is "$e", "Test!\n", 'right message';
 
 # Context information
 my $line = __LINE__;
@@ -26,31 +44,31 @@
 };
 $e = $@;
 isa_ok $e, 'Mojo::Exception', 'right class';
-is $e,     'Works!',          'right result';
+like $e->inspect, qr/^Works!/, 'right result';
 like $e->frames->[0][1], qr/exception\.t/, 'right file';
-is $e->lines_before->[0][0], $line + 1, 'right number';
-is $e->lines_before->[0][1], 'eval {', 'right line';
-is $e->lines_before->[1][0], $line + 2, 'right number';
-ok !$e->lines_before->[1][1], 'empty line';
-is $e->lines_before->[2][0], $line + 3, 'right number';
-is $e->lines_before->[2][1], '  # test', 'right line';
-is $e->lines_before->[3][0], $line + 4, 'right number';
-ok !$e->lines_before->[3][1], 'empty line';
-is $e->lines_before->[4][0], $line + 5, 'right number';
-is $e->lines_before->[4][1],
-  "  my \$wrapper = sub { Mojo::Exception->throw('Works!') };", 'right line';
-is $e->line->[0], $line + 6, 'right number';
-is $e->line->[1], "  \$wrapper->();", 'right line';
-is $e->lines_after->[0][0], $line + 7, 'right number';
-ok !$e->lines_after->[0][1], 'empty line';
-is $e->lines_after->[1][0], $line + 8, 'right number';
-is $e->lines_after->[1][1], '  # test', 'right line';
-is $e->lines_after->[2][0], $line + 9, 'right number';
-ok !$e->lines_after->[2][1], 'empty line';
-is $e->lines_after->[3][0], $line + 10, 'right number';
-is $e->lines_after->[3][1], '};', 'right line';
-is $e->lines_after->[4][0], $line + 11, 'right number';
-is $e->lines_after->[4][1], '$e = $@;', 'right line';
+is $e->lines_before->[0][0], $line, 'right number';
+is $e->lines_before->[0][1], 'my $line = __LINE__;', 'right line';
+is $e->lines_before->[1][0], $line + 1, 'right number';
+is $e->lines_before->[1][1], 'eval {', 'right line';
+is $e->lines_before->[2][0], $line + 2, 'right number';
+ok !$e->lines_before->[2][1], 'empty line';
+is $e->lines_before->[3][0], $line + 3, 'right number';
+is $e->lines_before->[3][1], '  # test', 'right line';
+is $e->lines_before->[4][0], $line + 4, 'right number';
+ok !$e->lines_before->[4][1], 'empty line';
+is $e->line->[0], $line + 5, 'right number';
+is $e->line->[1], "  my \$wrapper = sub { Mojo::Exception->throw('Works!') };",
+  'right line';
+is $e->lines_after->[0][0], $line + 6, 'right number';
+is $e->lines_after->[0][1], '  $wrapper->();', 'right line';
+is $e->lines_after->[1][0], $line + 7, 'right number';
+ok !$e->lines_after->[1][1], 'empty line';
+is $e->lines_after->[2][0], $line + 8, 'right number';
+is $e->lines_after->[2][1], '  # test', 'right line';
+is $e->lines_after->[3][0], $line + 9, 'right number';
+ok !$e->lines_after->[3][1], 'empty line';
+is $e->lines_after->[4][0], $line + 10, 'right number';
+is $e->lines_after->[4][1], '};', 'right line';
 
 # Trace
 sub wrapper2 { Mojo::Exception->new->trace(@_) }
@@ -70,7 +88,7 @@
 is_deeply $e->lines_before->[-1], [2, 'use warnings;'], 'right line';
 is_deeply $e->line,               [3, 'use utf8;'],     'right line';
 is_deeply $e->lines_after->[0],   [4, ''],              'right line';
-$e->message("Died at $file line 4.")->inspect;
+$e = Mojo::Exception->new("Died at $file line 4.")->inspect;
 is_deeply $e->lines_before->[-1], [3, 'use utf8;'], 'right line';
 is_deeply $e->line,               [4, ''],          'right line';
 is_deeply $e->lines_after->[0], [5, "my \$s = 'Über•résumé';"],
@@ -82,18 +100,22 @@
 is_deeply $e->lines_before, [], 'no lines';
 is_deeply $e->line,         [], 'no line';
 is_deeply $e->lines_after,  [], 'no lines';
-$e->inspect;
+$e->inspect->inspect;
 is_deeply $e->lines_before->[-1], [2, 'use warnings;'], 'right line';
 is_deeply $e->line,               [3, 'no utf8;'],      'right line';
 is_deeply $e->lines_after->[0],   [4, ''],              'right line';
-$e->message("Died at $file line 4.")->inspect;
+$e = Mojo::Exception->new("Died at $file line 4.")->inspect;
 is_deeply $e->lines_before->[-1], [3, 'no utf8;'], 'right line';
 is_deeply $e->line,               [4, ''],         'right line';
 is_deeply $e->lines_after->[0], [5, "my \$s = '\xDCber\x95r\xE9sum\xE9';"],
   'right line';
 
-# Verbose
-$e = Mojo::Exception->new('Test!')->verbose(1);
+# Context
+$e = Mojo::Exception->new;
+is $e, "Exception!\n", 'right result';
+$e = Mojo::Exception->new->inspect->inspect;
+is $e, "Exception!\n", 'right result';
+$e = Mojo::Exception->new('Test!');
 $e->frames([
   ['Sandbox',     'template',      4],
   ['MyApp::Test', 'MyApp/Test.pm', 3],
@@ -103,20 +125,199 @@
   ->lines_after([[5, 'bar();']]);
 is $e, <<EOF, 'right result';
 Test!
-3: foo();
-4: die;
-5: bar();
-template:4 (Sandbox)
-MyApp/Test.pm:3 (MyApp::Test)
-foo.pl:4 (main)
+Context:
+  3: foo();
+  4: die;
+  5: bar();
+Traceback (most recent call first):
+  File "template", line 4, in "Sandbox"
+  File "MyApp/Test.pm", line 3, in "MyApp::Test"
+  File "foo.pl", line 4, in "main"
 EOF
 $e->message("Works!\n")->lines_before([])->lines_after([]);
 is $e, <<EOF, 'right result';
 Works!
-4: die;
-template:4 (Sandbox)
-MyApp/Test.pm:3 (MyApp::Test)
-foo.pl:4 (main)
+Context:
+  4: die;
+Traceback (most recent call first):
+  File "template", line 4, in "Sandbox"
+  File "MyApp/Test.pm", line 3, in "MyApp::Test"
+  File "foo.pl", line 4, in "main"
+EOF
+
+# Verbose
+$e = Mojo::Exception->new('Test!');
+$e->frames([
+  ['Sandbox',     'template',      4],
+  ['MyApp::Test', 'MyApp/Test.pm', 3],
+  ['MyApp::Test', 'MyApp/Test.pm', 4],
+  ['MyApp::Test', 'MyApp/Test.pm', 5],
+  ['MyApp::Test', 'MyApp/Test.pm', 6],
+  ['main',        'foo.pl',        4]
+]);
+is $e, <<EOF, 'right result';
+Test!
+Traceback (most recent call first):
+  File "template", line 4, in "Sandbox"
+  File "MyApp/Test.pm", line 3, in "MyApp::Test"
+  File "MyApp/Test.pm", line 4, in "MyApp::Test"
+  File "MyApp/Test.pm", line 5, in "MyApp::Test"
+  File "MyApp/Test.pm", line 6, in "MyApp::Test"
+  ...
+EOF
+is $e->verbose(1), <<EOF, 'right result';
+Test!
+Traceback (most recent call first):
+  File "template", line 4, in "Sandbox"
+  File "MyApp/Test.pm", line 3, in "MyApp::Test"
+  File "MyApp/Test.pm", line 4, in "MyApp::Test"
+  File "MyApp/Test.pm", line 5, in "MyApp::Test"
+  File "MyApp/Test.pm", line 6, in "MyApp::Test"
+  File "foo.pl", line 4, in "main"
 EOF
 
+# Missing error
+$e = Mojo::Exception->new->inspect;
+is_deeply $e->lines_before, [], 'no lines';
+is_deeply $e->line,         [], 'no line';
+is_deeply $e->lines_after,  [], 'no lines';
+is $e->message, 'Exception!', 'right message';
+$e = Mojo::Exception->new(undef)->inspect;
+is_deeply $e->lines_before, [], 'no lines';
+is_deeply $e->line,         [], 'no line';
+is_deeply $e->lines_after,  [], 'no lines';
+is $e->message, 'Exception!', 'right message';
+$e = Mojo::Exception->new('')->inspect;
+is_deeply $e->lines_before, [], 'no lines';
+is_deeply $e->line,         [], 'no line';
+is_deeply $e->lines_after,  [], 'no lines';
+is $e->message, '', 'right message';
+
+# Check (string exception)
+my $result;
+eval { die "test1\n" };
+ok check(default => sub { $result = $_ }), 'exception handled';
+is $result, "test1\n", 'exception arrived in handler';
+$result = undef;
+eval { die "test2\n" };
+ok check(default => sub { $result = shift }), 'exception handled';
+is $result, "test2\n", 'exception arrived in handler';
+$result = undef;
+eval { die "test3\n" };
+check
+  default    => sub { $result = 'fail' },
+  qr/^test2/ => sub { $result = 'fail' },
+  qr/^test3/ => sub { $result = 'test10' },
+  qr/^test4/ => sub { $result = 'fail' };
+is $result, 'test10', 'regular expression matched';
+$result = undef;
+check "test4\n",
+  qr/^test3/ => sub { $result = 'fail' },
+  qr/^test4/ => sub { $result = 'test11' },
+  qr/^test5/ => sub { $result = 'fail' };
+is $result, 'test11', 'regular expression matched';
+
+# Check (exception objects)
+$result = undef;
+eval { MojoTest::X::Foo->throw('fail') };
+check
+  default            => sub { $result = 'fail' },
+  'MojoTest::X::Foo' => sub { $result = 'test12' },
+  'MojoTest::X::Bar' => sub { $result = 'fail' };
+is $result, 'test12', 'class matched';
+$result = undef;
+eval { MojoTest::X::Bar->throw('fail') };
+check
+  'MojoTest::X::Foo' => sub { $result = 'fail' },
+  'MojoTest::X::Bar' => sub { $result = 'test13' };
+is $result, 'test13', 'class matched';
+$result = undef;
+check(
+  MojoTest::X::Yada->new('fail'),
+  qr/^MojoTest/      => sub { $result = 'fail' },
+  'MojoTest::X::Foo' => sub { $result = 'fail' },
+  'MojoTest::X::Bar' => sub { $result = 'test14' }
+);
+is $result, 'test14', 'class matched';
+$result = undef;
+check(
+  MojoTest::X::Yada->new('whatever'),
+  'MojoTest::X::Foo' => sub { $result = 'fail' },
+  qr/^whatever/      => sub { $result = 'test23' },
+);
+is $result, 'test23', 'regex matched';
+
+# Check (multiple)
+$result = undef;
+check(
+  MojoTest::X::Yada->new('whatever'),
+  ['MojoTest::X::Foo', 'MojoTest::X::Bar'] => sub { $result = 'test15' },
+  default => sub { $result = 'fail' }
+);
+is $result, 'test15', 'class matched';
+$result = undef;
+check(
+  MojoTest::X::Bar->new('whatever'),
+  ['MojoTest::X::Foo', 'MojoTest::X::Yada'] => sub { $result = 'fail' },
+  ['MojoTest::X::Bar'] => sub { $result = 'test16' }
+);
+is $result, 'test16', 'class matched';
+
+# Check (rethrow)
+eval {
+  check "test5\n", qr/test4/ => sub { die 'fail' };
+};
+is $@, "test5\n", 'exception has been rethrown';
+
+# Check (finally)
+my $finally;
+eval {
+  check "test7\n", finally => sub { $finally = 'finally7' };
+};
+is $@,       "test7\n",  'exception has been rethrown';
+is $finally, 'finally7', 'finally handler used';
+$result = [];
+check "test8\n",
+  qr/test7/ => sub { push @$result, 'fail' },
+  default   => sub { push @$result, $_ },
+  finally   => sub { push @$result, 'finally8' };
+is_deeply $result, ["test8\n", 'finally8'], 'default and finally handlers 
used';
+$finally = undef;
+eval {
+  check "fail\n",
+    default => sub { die "test17\n" },
+    finally => sub { $finally = 'finally17' };
+};
+is $@,       "test17\n",  'right exception';
+is $finally, 'finally17', 'finally handler used';
+
+# Check (nothing)
+ok !check(undef, default => sub { die 'fail' }), 'no exception';
+{
+  local $@;
+  ok !check(default => sub { die 'fail' }), 'no exception';
+}
+
+# Raise
+eval { raise 'MyApp::X::Baz', 'test19' };
+my $err = $@;
+isa_ok $err, 'MyApp::X::Baz',   'is a MyApp::X::Baz';
+isa_ok $err, 'Mojo::Exception', 'is a Mojo::Exception';
+like $err,   qr/^test19/,       'right error';
+eval { raise 'MyApp::X::Baz', 'test20' };
+$err = $@;
+isa_ok $err, 'MyApp::X::Baz',   'is a MyApp::X::Baz';
+isa_ok $err, 'Mojo::Exception', 'is a Mojo::Exception';
+like $err,   qr/^test20/,       'right error again';
+eval { raise 'test22' };
+$err = $@;
+isa_ok $err, 'Mojo::Exception', 'is a Mojo::Exception';
+like $err,   qr/^test22/,       'right error';
+eval { raise 'MojoTest::X::Foo', 'test21' };
+$err = $@;
+isa_ok $err, 'MojoTest::X::Foo', 'is a MojoTest::X::Baz';
+like $err,   qr/^test21/,        'right error';
+eval { raise 'Mojo::Base', 'fail' };
+like $@, qr/^Mojo::Base is not a Mojo::Exception subclass/, 'right error';
+
 done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/mojo/template.t 
new/Mojolicious-8.21/t/mojo/template.t
--- old/Mojolicious-8.18/t/mojo/template.t      2019-05-31 21:29:02.000000000 
+0200
+++ new/Mojolicious-8.21/t/mojo/template.t      2019-07-07 23:47:35.000000000 
+0200
@@ -607,7 +607,6 @@
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 is $output->message, "x\n", 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,      'right number';
 is $output->lines_before->[0][1], 'test', 'right line';
 is $output->lines_before->[1][0], 2,      'right number';
@@ -629,7 +628,6 @@
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/Missing right curly/, 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,          'right number';
 is $output->lines_before->[0][1], 'test',     'right line';
 is $output->lines_before->[1][0], 2,          'right number';
@@ -654,7 +652,6 @@
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/ohoh/, 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 14,  'right number';
 is $output->lines_before->[0][1], '}', 'right line';
 is $output->lines_before->[1][0], 15,  'right number';
@@ -686,7 +683,6 @@
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/oops!/, 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,               'right number';
 is $output->lines_before->[0][1], 'test',          'right line';
 is $output->lines_before->[1][0], 2,               'right number';
@@ -704,15 +700,17 @@
 $output->frames([['Sandbox', 'template', 5], ['main', 'template.t', 673]]);
 is $output, <<EOF, 'right result';
 oops! at template line 5.
-1: test
-2: 123\\
-3: 456
-4:  %# This dies
-5: % die 'oops!';
-6: %= 1 + 1
-7: test
-template:5 (Sandbox)
-template.t:673 (main)
+Context:
+  1: test
+  2: 123\\
+  3: 456
+  4:  %# This dies
+  5: % die 'oops!';
+  6: %= 1 + 1
+  7: test
+Traceback (most recent call first):
+  File "template", line 5, in "Sandbox"
+  File "template.t", line 673, in "main"
 EOF
 
 # Exception in template (empty perl lines)
@@ -730,7 +728,6 @@
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/oops!/, 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,          'right number';
 is $output->lines_before->[0][1], 'test\\\\', 'right line';
 ok $output->lines_before->[0][2], 'contains code';
@@ -767,8 +764,7 @@
 -$]
 $-= $output
 EOF
-like $output,
-  qr/test\n\nBareword "bar".+in use at template line 1\.\n1: %= bar/,
+like $output, qr/test\n\nBareword "bar".+in use at template line 1\./,
   'exception in nested template';
 
 # Control structures
@@ -1139,7 +1135,6 @@
 $output = $mt->render_file($file);
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/exception\.mt line 2/, 'message contains filename';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,      'right number';
 is $output->lines_before->[0][1], 'test', 'right line';
 is $output->line->[0], 2,        'right number';
@@ -1154,7 +1149,6 @@
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/foo\.mt from DATA section line 2/,
   'message contains filename';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][0], 1,      'right number';
 is $output->lines_before->[0][1], 'test', 'right line';
 is $output->line->[0], 2,        'right number';
@@ -1168,7 +1162,6 @@
 $file   = path(__FILE__)->sibling('templates', 'utf8_exception.mt');
 $output = $mt->render_file($file);
 isa_ok $output, 'Mojo::Exception', 'right exception';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0][1], '☃', 'right line';
 is $output->line->[1], '% die;♥', 'right line';
 is $output->lines_after->[0][1], '☃', 'right line';
@@ -1178,7 +1171,6 @@
 $output = $mt->render('<% die "Test at template line 99\n"; %>');
 isa_ok $output, 'Mojo::Exception', 'right exception';
 is $output->message, "Test at template line 99\n", 'right message';
-ok $output->verbose, 'verbose exception';
 is $output->lines_before->[0], undef, 'no lines before';
 is $output->line->[0],         1,     'right number';
 is $output->line->[1], '<% die "Test at template line 99\n"; %>', 'right line';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/mojolicious/commands.t 
new/Mojolicious-8.21/t/mojolicious/commands.t
--- old/Mojolicious-8.18/t/mojolicious/commands.t       2019-05-31 
21:29:08.000000000 +0200
+++ new/Mojolicious-8.21/t/mojolicious/commands.t       2019-07-03 
16:54:52.000000000 +0200
@@ -330,6 +330,16 @@
 ok -e $app->rel_file('Mojolicious-Plugin-MyPlugin/t/basic.t'), 'test exists';
 ok -e $app->rel_file('Mojolicious-Plugin-MyPlugin/Makefile.PL'),
   'Makefile.PL exists';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $plugin->run('-f', 'MyApp::Ext::Test');
+}
+like $buffer, qr/Test\.pm/, 'right output';
+ok -e $app->rel_file('MyApp-Ext-Test/lib/MyApp/Ext/Test.pm'), 'class exists';
+ok -e $app->rel_file('MyApp-Ext-Test/t/basic.t'),             'test exists';
+ok -e $app->rel_file('MyApp-Ext-Test/Makefile.PL'), 'Makefile.PL exists';
 chdir $cwd;
 
 # inflate
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/mojolicious/exception_lite_app.t 
new/Mojolicious-8.21/t/mojolicious/exception_lite_app.t
--- old/Mojolicious-8.18/t/mojolicious/exception_lite_app.t     2019-05-31 
21:29:07.000000000 +0200
+++ new/Mojolicious-8.21/t/mojolicious/exception_lite_app.t     2019-07-13 
21:36:18.000000000 +0200
@@ -113,7 +113,7 @@
 hook around_dispatch => sub {
   my ($next, $c) = @_;
   unless (eval { $next->(); 1 }) {
-    die $@ unless $@ eq "CUSTOM\n";
+    die $@ unless $@ =~ /^CUSTOM\n/;
     $c->render(text => 'Custom handling works!');
   }
 };
@@ -126,6 +126,17 @@
 
 my $t = Test::Mojo->new;
 
+# Missing error
+my $c = $t->app->build_controller;
+$c->reply->exception(undef);
+like $c->res->body, qr/Exception!/, 'right result';
+$c = $t->app->build_controller;
+$c->reply->exception;
+like $c->res->body, qr/Exception!/, 'right result';
+$c = $t->app->build_controller;
+$c->reply->exception(Mojo::Exception->new);
+like $c->res->body, qr/Exception!/, 'right result';
+
 # Debug
 $t->get_ok('/logger?level=debug&message=one')->status_is(200)
   ->content_is('debug: one');
@@ -211,11 +222,13 @@
 
 # Dead action with custom exception rendering
 $t->get_ok('/dead_action' => {Accept => 'text/plain'})->status_is(500)
-  ->content_type_is('text/plain;charset=UTF-8')->content_is("dead action!\n");
+  ->content_type_is('text/plain;charset=UTF-8')
+  ->content_like(qr/^dead action!\n/);
 
 # Action dies twice
 $t->get_ok('/double_dead_action_☃')->status_is(500)
-  ->content_like(qr!get &#39;/double_dead_action_☃&#39;.*lite_app\.t:\d!s)
+  ->content_like(qr!get &#39;/double_dead_action_☃&#39;!)
+  ->content_like(qr/File.+lite_app\.t\", line \d/)
   ->content_like(qr/double dead action!/);
 
 # Trapped exception
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/mojolicious/validation_lite_app.t 
new/Mojolicious-8.21/t/mojolicious/validation_lite_app.t
--- old/Mojolicious-8.18/t/mojolicious/validation_lite_app.t    2019-05-31 
21:29:03.000000000 +0200
+++ new/Mojolicious-8.21/t/mojolicious/validation_lite_app.t    2019-07-09 
19:25:28.000000000 +0200
@@ -158,6 +158,11 @@
 ok !$v->required('foo')->num(undef, 22)->is_valid, 'not valid';
 ok $v->has_error, 'has error';
 is_deeply $v->error('foo'), ['num', 1, undef, 22], 'right error';
+$v = $t->app->validation->input({foo => -5});
+ok $v->required('foo')->num->is_valid, 'valid';
+ok $v->required('foo')->num(undef, -1)->is_valid,    'valid';
+ok $v->required('foo')->num(-10,   10)->is_valid,    'valid';
+ok $v->required('foo')->num(-20,   undef)->is_valid, 'valid';
 
 # Size
 $v = $t->app->validation->input({foo => 'bar', baz => 'yada', yada => 'yada'});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.18/t/pod_coverage.t 
new/Mojolicious-8.21/t/pod_coverage.t
--- old/Mojolicious-8.18/t/pod_coverage.t       2019-06-21 23:12:48.000000000 
+0200
+++ new/Mojolicious-8.21/t/pod_coverage.t       2019-07-07 23:48:36.000000000 
+0200
@@ -7,4 +7,5 @@
 plan skip_all => 'Test::Pod::Coverage 1.04+ required for this test!'
   unless eval 'use Test::Pod::Coverage 1.04; 1';
 
-all_pod_coverage_ok({also_private => ['BUILD_DYNAMIC', 'slice', 'success']});
+all_pod_coverage_ok(
+  {also_private => ['BUILD_DYNAMIC', 'slice', 'success', 'verbose']});


Reply via email to