Hello community,

here is the log from the commit of package perl-Mojolicious for 
openSUSE:Factory checked in at 2019-12-30 12:34:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.6675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Mojolicious"

Mon Dec 30 12:34:48 2019 rev:119 rq:759925 version:8.29

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes        
2019-12-06 12:08:53.260126543 +0100
+++ 
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.6675/perl-Mojolicious.changes  
    2019-12-30 12:34:55.795807706 +0100
@@ -1,0 +2,14 @@
+Sun Dec 29 03:09:13 UTC 2019 -  <[email protected]>
+
+- updated to 8.29
+   see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+  8.29  2019-12-28
+    - Improved async/await support to work in many more cases, such as 
WebSocket
+      handlers.
+  
+  8.28  2019-12-26
+    - Added EXPERIMENTAL support for async/await (with -async Mojo::Base flag).
+    - Added EXPERIMENTAL all_settled and any methods to Mojo::Promise.
+
+-------------------------------------------------------------------

Old:
----
  Mojolicious-8.27.tar.gz

New:
----
  Mojolicious-8.29.tar.gz

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

Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.Y3U8cD/_old  2019-12-30 12:34:56.243807946 +0100
+++ /var/tmp/diff_new_pack.Y3U8cD/_new  2019-12-30 12:34:56.243807946 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           perl-Mojolicious
-Version:        8.27
+Version:        8.29
 Release:        0
 %define cpan_name Mojolicious
 Summary:        Real-time web framework

++++++ Mojolicious-8.27.tar.gz -> Mojolicious-8.29.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/Changes new/Mojolicious-8.29/Changes
--- old/Mojolicious-8.27/Changes        2019-12-04 18:30:14.000000000 +0100
+++ new/Mojolicious-8.29/Changes        2019-12-28 00:31:06.000000000 +0100
@@ -1,4 +1,12 @@
 
+8.29  2019-12-28
+  - Improved async/await support to work in many more cases, such as WebSocket
+    handlers.
+
+8.28  2019-12-26
+  - Added EXPERIMENTAL support for async/await (with -async Mojo::Base flag).
+  - Added EXPERIMENTAL all_settled and any methods to Mojo::Promise.
+
 8.27  2019-12-04
   - Added EXPERIMENTAL before_command hook.
   - Added EXPERIMENTAL scope_guard function to Mojo::Util.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/MANIFEST 
new/Mojolicious-8.29/MANIFEST
--- old/Mojolicious-8.27/MANIFEST       2019-12-04 20:47:12.000000000 +0100
+++ new/Mojolicious-8.29/MANIFEST       2019-12-28 17:57:13.000000000 +0100
@@ -216,6 +216,7 @@
 t/mojo/path.t
 t/mojo/prefork.t
 t/mojo/promise.t
+t/mojo/promise_async_await.t
 t/mojo/proxy.t
 t/mojo/psgi.t
 t/mojo/reactor_detect.t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/META.json 
new/Mojolicious-8.29/META.json
--- old/Mojolicious-8.27/META.json      2019-12-04 20:47:12.000000000 +0100
+++ new/Mojolicious-8.29/META.json      2019-12-28 17:57:13.000000000 +0100
@@ -4,7 +4,7 @@
       "Sebastian Riedel <[email protected]>"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "ExtUtils::MakeMaker version 7.38, CPAN::Meta::Converter 
version 2.150010",
+   "generated_by" : "ExtUtils::MakeMaker version 7.42, CPAN::Meta::Converter 
version 2.150010",
    "license" : [
       "artistic_2"
    ],
@@ -62,6 +62,6 @@
       },
       "x_IRC" : "irc://irc.freenode.net/#mojo"
    },
-   "version" : "8.27",
+   "version" : "8.29",
    "x_serialization_backend" : "JSON::PP version 4.04"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/META.yml 
new/Mojolicious-8.29/META.yml
--- old/Mojolicious-8.27/META.yml       2019-12-04 20:47:12.000000000 +0100
+++ new/Mojolicious-8.29/META.yml       2019-12-28 17:57:12.000000000 +0100
@@ -7,7 +7,7 @@
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 0
-generated_by: 'ExtUtils::MakeMaker version 7.38, CPAN::Meta::Converter version 
2.150010'
+generated_by: 'ExtUtils::MakeMaker version 7.42, CPAN::Meta::Converter version 
2.150010'
 license: artistic_2
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -34,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.27'
+version: '8.29'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/README.md 
new/Mojolicious-8.29/README.md
--- old/Mojolicious-8.27/README.md      2019-11-21 17:49:22.000000000 +0100
+++ new/Mojolicious-8.29/README.md      2019-12-26 16:13:28.000000000 +0100
@@ -34,8 +34,8 @@
     applications, independently of the web framework.
     * Full stack HTTP and WebSocket client/server implementation with IPv6, 
TLS,
       SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet (long polling),
-      Promises/A+, keep-alive, connection pooling, timeout, cookie, multipart,
-      and gzip compression support.
+      Promises/A+, async/await, keep-alive, connection pooling, timeout, 
cookie,
+      multipart, and gzip compression support.
     * Built-in non-blocking I/O web server, supporting multiple event loops as
       well as optional pre-forking and hot deployment, perfect for building
       highly scalable web services.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/lib/Mojo/Base.pm 
new/Mojolicious-8.29/lib/Mojo/Base.pm
--- old/Mojolicious-8.27/lib/Mojo/Base.pm       2019-11-25 22:39:57.000000000 
+0100
+++ new/Mojolicious-8.29/lib/Mojo/Base.pm       2019-12-26 16:50:14.000000000 
+0100
@@ -20,6 +20,13 @@
 use constant ROLES =>
   !!(eval { require Role::Tiny; Role::Tiny->VERSION('2.000001'); 1 });
 
+# async/await support requires Future::AsyncAwait::Frozen 0.36+
+use constant ASYNC => $ENV{MOJO_NO_ASYNC} ? 0 : !!(eval {
+  require Future::AsyncAwait::Frozen;
+  Future::AsyncAwait::Frozen->VERSION('0.000001');
+  1;
+});
+
 # Protect subclasses using AUTOLOAD
 sub DESTROY { }
 
@@ -120,6 +127,14 @@
       eval "package $caller; use Role::Tiny; 1" or die $@;
     }
 
+    # async/await
+    elsif ($flag eq '-async') {
+      Carp::croak 'Future::AsyncAwait::Frozen 0.36+ is required for 
async/await'
+        unless ASYNC;
+      Future::AsyncAwait::Frozen->import_into($caller,
+        future_class => 'Mojo::Promise');
+    }
+
     # Signatures (Perl 5.20+)
     elsif ($flag eq '-signatures') {
       Carp::croak 'Subroutine signatures require Perl 5.20+' if $] < 5.020;
@@ -257,6 +272,15 @@
   use Mojo::Base 'SomeBaseClass', -signatures;
   use Mojo::Base -role, -signatures;
 
+If you have L<Future::AsyncAwait::Frozen> 0.36+ installed you can also use the
+C<-async> flag to activate the C<async> and C<await> keywords to deal much more
+efficiently with promises. Note that this feature is B<EXPERIMENTAL> and might
+change without warning!
+
+  # Also enable async/await
+  use Mojo::Base -strict, -async;
+  use Mojo::Base -base, -signatures, -async;
+
 This will also disable experimental warnings on versions of Perl where this
 feature was still experimental.
 
@@ -264,7 +288,7 @@
 
 Fluent interfaces are a way to design object-oriented APIs around method
 chaining to create domain-specific languages, with the goal of making the
-readablity of the source code close to written prose.
+readability of the source code close to written prose.
 
   package Duck;
   use Mojo::Base -base;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/lib/Mojo/Promise.pm 
new/Mojolicious-8.29/lib/Mojo/Promise.pm
--- old/Mojolicious-8.27/lib/Mojo/Promise.pm    2019-11-25 22:39:45.000000000 
+0100
+++ new/Mojolicious-8.29/lib/Mojo/Promise.pm    2019-12-28 17:51:50.000000000 
+0100
@@ -7,25 +7,35 @@
 
 has ioloop => sub { Mojo::IOLoop->singleton }, weak => 1;
 
-sub all {
-  my ($class, @promises) = @_;
+sub AWAIT_CLONE { _await('clone', @_) }
 
-  my $all       = $promises[0]->clone;
-  my $results   = [];
-  my $remaining = scalar @promises;
-  for my $i (0 .. $#promises) {
-    $promises[$i]->then(
-      sub {
-        $results->[$i] = [@_];
-        $all->resolve(@$results) if --$remaining <= 0;
-      },
-      sub { $all->reject(@_) }
-    );
-  }
+sub AWAIT_DONE { shift->resolve(@_) }
+sub AWAIT_FAIL { shift->reject(@_) }
 
-  return $all;
+sub AWAIT_GET {
+  my $self    = shift;
+  my @results = @{$self->{result} // []};
+  die $results[0] unless $self->{status} eq 'resolve';
+  return wantarray ? @results : $results[0];
 }
 
+sub AWAIT_IS_CANCELLED {undef}
+
+sub AWAIT_IS_READY {
+  my $self = shift;
+  return !!$self->{result} && !@{$self->{resolve}} && !@{$self->{reject}};
+}
+
+sub AWAIT_NEW_DONE { _await('resolve', @_) }
+sub AWAIT_NEW_FAIL { _await('reject',  @_) }
+
+sub AWAIT_ON_CANCEL { }
+sub AWAIT_ON_READY  { shift->finally(@_) }
+
+sub all         { _all(2, @_) }
+sub all_settled { _all(0, @_) }
+sub any         { _all(3, @_) }
+
 sub catch { shift->then(undef, shift) }
 
 sub clone { $_[0]->new->ioloop($_[0]->ioloop) }
@@ -44,7 +54,7 @@
 
 sub map {
   my ($class, $options) = (shift, ref $_[0] eq 'HASH' ? shift : {});
-  my ($cb, @items) = @_;
+  my ($cb,    @items)   = @_;
 
   my @start = map { $_->$cb } splice @items, 0,
     $options->{concurrency} // @items;
@@ -80,12 +90,7 @@
   return $self;
 }
 
-sub race {
-  my ($class, @promises) = @_;
-  my $new = $promises[0]->clone;
-  $_->then(sub { $new->resolve(@_) }, sub { $new->reject(@_) }) for @promises;
-  return $new;
-}
+sub race { _all(1, @_) }
 
 sub reject  { shift->_settle('reject',  @_) }
 sub resolve { shift->_settle('resolve', @_) }
@@ -113,12 +118,72 @@
   $loop->start until $done;
 }
 
+sub _all {
+  my ($type, $class, @promises) = @_;
+
+  my $all       = $promises[0]->clone;
+  my $results   = [];
+  my $remaining = scalar @promises;
+  for my $i (0 .. $#promises) {
+
+    # "race"
+    if ($type == 1) {
+      $promises[$i]->then(sub { $all->resolve(@_) }, sub { $all->reject(@_) });
+    }
+
+    # "all"
+    elsif ($type == 2) {
+      $promises[$i]->then(
+        sub {
+          $results->[$i] = [@_];
+          $all->resolve(@$results) if --$remaining <= 0;
+        },
+        sub { $all->reject(@_) }
+      );
+    }
+
+    # "any"
+    elsif ($type == 3) {
+      $promises[$i]->then(
+        sub { $all->resolve(@_) },
+        sub {
+          $results->[$i] = [@_];
+          $all->reject(@$results) if --$remaining <= 0;
+        }
+      );
+    }
+
+    # "all_settled"
+    else {
+      $promises[$i]->then(
+        sub {
+          $results->[$i] = {status => 'fulfilled', value => [@_]};
+          $all->resolve(@$results) if --$remaining <= 0;
+        },
+        sub {
+          $results->[$i] = {status => 'rejected', reason => [@_]};
+          $all->resolve(@$results) if --$remaining <= 0;
+        }
+      );
+    }
+  }
+
+  return $all;
+}
+
+sub _await {
+  my ($method, $class) = (shift, shift);
+  my $promise = $class->$method(@_);
+  $promise->{cycle} = $promise;
+  return $promise;
+}
+
 sub _defer {
   my $self = shift;
 
   return unless my $result = $self->{result};
   my $cbs = $self->{status} eq 'resolve' ? $self->{resolve} : $self->{reject};
-  @{$self}{qw(resolve reject)} = ([], []);
+  @{$self}{qw(cycle resolve reject)} = (undef, [], []);
 
   $self->ioloop->next_tick(sub { $_->(@$result) for @$cbs });
 }
@@ -178,7 +243,7 @@
 
   # Wrap continuation-passing style APIs with promises
   my $ua = Mojo::UserAgent->new;
-  sub get {
+  sub get_p {
     my $promise = Mojo::Promise->new;
     $ua->get(@_ => sub {
       my ($ua, $tx) = @_;
@@ -190,7 +255,7 @@
   }
 
   # Perform non-blocking operations sequentially
-  get('https://mojolicious.org')->then(sub {
+  get_p('https://mojolicious.org')->then(sub {
     my $mojo = shift;
     say $mojo->res->code;
     return get('https://metacpan.org');
@@ -203,8 +268,8 @@
   })->wait;
 
   # Synchronize non-blocking operations (all)
-  my $mojo = get('https://mojolicious.org');
-  my $cpan = get('https://metacpan.org');
+  my $mojo = get_p('https://mojolicious.org');
+  my $cpan = get_p('https://metacpan.org');
   Mojo::Promise->all($mojo, $cpan)->then(sub {
     my ($mojo, $cpan) = @_;
     say $mojo->[0]->res->code;
@@ -215,8 +280,8 @@
   })->wait;
 
   # Synchronize non-blocking operations (race)
-  my $mojo = get('https://mojolicious.org');
-  my $cpan = get('https://metacpan.org');
+  my $mojo = get_p('https://mojolicious.org');
+  my $cpan = get_p('https://metacpan.org');
   Mojo::Promise->race($mojo, $cpan)->then(sub {
     my $tx = shift;
     say $tx->req->url, ' won!';
@@ -285,8 +350,26 @@
 Returns a new L<Mojo::Promise> object that either fulfills when all of the
 passed L<Mojo::Promise> objects have fulfilled or rejects as soon as one of 
them
 rejects. If the returned promise fulfills, it is fulfilled with the values from
-the fulfilled promises in the same order as the passed promises. This method 
can
-be useful for aggregating results of multiple promises.
+the fulfilled promises in the same order as the passed promises.
+
+=head2 all_settled
+
+  my $new = Mojo::Promise->all_settled(@promises);
+
+Returns a new L<Mojo::Promise> object that fulfills when all of the passed
+L<Mojo::Promise> objects have fulfilled or rejected, with hash references that
+describe the outcome of each promise. Note that this method is B<EXPERIMENTAL>
+and might change without warning!
+
+=head2 any
+
+  my $new = Mojo::Promise->any(@promises);
+
+Returns a new L<Mojo::Promise> object that fulfills as soon as one of
+the passed L<Mojo::Promise> objects fulfills, with the value from that promise.
+If no promises fulfill, it is rejected with the reasons from the rejected
+promises in the same order as the passed promises. Note that this method is
+B<EXPERIMENTAL> and might change without warning!
 
 =head2 catch
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/lib/Mojolicious/Command/version.pm 
new/Mojolicious-8.29/lib/Mojolicious/Command/version.pm
--- old/Mojolicious-8.27/lib/Mojolicious/Command/version.pm     2019-11-25 
22:40:02.000000000 +0100
+++ new/Mojolicious-8.29/lib/Mojolicious/Command/version.pm     2019-12-26 
16:13:28.000000000 +0100
@@ -18,7 +18,8 @@
     = Mojo::IOLoop::Client->can_socks ? $IO::Socket::Socks::VERSION : 'n/a';
   my $tls = Mojo::IOLoop::TLS->can_tls    ? $IO::Socket::SSL::VERSION  : 'n/a';
   my $nnr = Mojo::IOLoop::Client->can_nnr ? $Net::DNS::Native::VERSION : 'n/a';
-  my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION : 'n/a';
+  my $roles = Mojo::Base->ROLES ? $Role::Tiny::VERSION                 : 'n/a';
+  my $async = Mojo::Base->ASYNC ? $Future::AsyncAwait::Frozen::VERSION : 'n/a';
 
   print <<EOF;
 CORE
@@ -26,12 +27,13 @@
   Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME)
 
 OPTIONAL
-  Cpanel::JSON::XS 4.09+  ($json)
-  EV 4.0+                 ($ev)
-  IO::Socket::Socks 0.64+ ($socks)
-  IO::Socket::SSL 2.009+  ($tls)
-  Net::DNS::Native 0.15+  ($nnr)
-  Role::Tiny 2.000001+    ($roles)
+  Cpanel::JSON::XS 4.09+           ($json)
+  EV 4.0+                          ($ev)
+  IO::Socket::Socks 0.64+          ($socks)
+  IO::Socket::SSL 2.009+           ($tls)
+  Net::DNS::Native 0.15+           ($nnr)
+  Role::Tiny 2.000001+             ($roles)
+  Future::AsyncAwait::Frozen 0.36+ ($async)
 
 EOF
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/lib/Mojolicious/Guides/Cookbook.pod 
new/Mojolicious-8.29/lib/Mojolicious/Guides/Cookbook.pod
--- old/Mojolicious-8.27/lib/Mojolicious/Guides/Cookbook.pod    2019-11-21 
17:49:24.000000000 +0100
+++ new/Mojolicious-8.29/lib/Mojolicious/Guides/Cookbook.pod    2019-12-26 
17:24:12.000000000 +0100
@@ -649,6 +649,114 @@
 L<Mojo::Promise/"resolve"> to transition them to C<fulfilled>, or
 L<Mojo::Promise/"reject"> to transition them to C<rejected>.
 
+=head2 async/await
+
+And if you have L<Future::AsyncAwait::Frozen> installed you can make using
+promises even easier. The C<async> and C<await> keywords are enabled with the
+C<-async> flag of L<Mojo::Base>, and make the use of closures with promises
+completely optional.
+
+  use Mojo::Base -strict, -async;
+
+The C<async> keyword is placed before the C<sub> keyword, and means that this
+function always returns a promise. Returned values that are not 
L<Mojo::Promise>
+objects will be wrapped in a resolved promise automatically. And if an 
exception
+gets thrown in the function it will result in a rejected promise.
+
+  use Mojo::Base -strict, -async;
+
+  async sub hello_p {
+    return 'Hello Mojo!';
+  }
+
+  hello_p()->then(sub { say @_ })->wait;
+
+The C<await> keyword on the other hand makes Perl wait for the promise to be
+settled. It then returns the fulfillment values or throws an exception with the
+rejection reason. While waiting, the event loop is free to perform other tasks
+however, so no resources are wasted.
+
+  use Mojo::Base -strict, -signatures, -async;
+  use Mojo::UserAgent;
+  use Mojo::URL;
+
+  my $ua = Mojo::UserAgent->new;
+
+  # Search MetaCPAN non-blocking for multiple terms sequentially
+  async sub search_cpan_p ($terms) {
+    my $cpan = Mojo::URL->new('http://fastapi.metacpan.org/v1/module/_search');
+    my @urls = map { $cpan->clone->query(q => $_) } @$terms;
+
+    for my $url (@urls) {
+      my $tx = await $ua->get_p($url);
+      say $tx->result->json('/hits/hits/0/_source/release');
+    }
+  }
+
+  search_cpan_p(['mojo', 'minion'])->wait;
+
+The loop above performs all requests sequentially, awaiting a result before
+sending the next request. But you can also perform those requests concurrently
+instead, by using methods like L<Mojo::Promise/"all"> to combine multiple
+promises before awaiting the results.
+
+  use Mojo::Base -strict, -signatures, -async;
+  use Mojo::Promise;
+  use Mojo::UserAgent;
+  use Mojo::URL;
+
+  my $ua = Mojo::UserAgent->new;
+
+  # Search MetaCPAN non-blocking for multiple terms concurrently
+  async sub search_cpan_p ($terms) {
+    my $cpan = Mojo::URL->new('http://fastapi.metacpan.org/v1/module/_search');
+    my @urls = map { $cpan->clone->query(q => $_) } @$terms;
+
+    my @promises = map { $ua->get_p($_) } @urls;
+    my @results  = await Mojo::Promise->all(@promises);
+    for my $result (@results) {
+      say $result->[0]->result->json('/hits/hits/0/_source/release');
+    }
+  }
+
+  search_cpan_p(['mojo', 'minion'])->wait;
+
+All of this also means that you can use normal Perl exception handling again.
+Even many 3rd party exception handling modules from CPAN work just fine.
+
+  use Mojo::Base -strict, -async;
+  use Mojo::Promise;
+
+  # Catch a non-blocking exception
+  async sub hello_p {
+    eval { await Mojo::Promise->reject('This is an exception') };
+    if (my $err = $@) { warn "Error: $err" }
+  }
+
+  hello_p()->wait;
+
+And it works just the same in L<Mojolicious> and L<Mojolicious::Lite>
+applications. Just declare your actions with the C<async> keyword and use
+C<await> to wait for promises to be C<fulfilled> or C<rejected>.
+
+  use Mojolicious::Lite -signatures, -async;
+
+  # Request HTML titles from two sites non-blocking
+  get '/' => async sub ($c) {
+    my $mojo_tx    = await $c->ua->get_p('https://mojolicious.org');
+    my $mojo_title = $mojo_tx->result->dom->at('title')->text;
+    my $cpan_tx    = await $c->ua->get_p('https://metacpan.org');
+    my $cpan_title = $cpan_tx->result->dom->at('title')->text;
+
+    $c->render(json => {mojo => $mojo_title, cpan => $cpan_title});
+  };
+
+  app->start;
+
+Promises returned by actions will automatically get the default L<Mojolicious>
+exception handler attached. Making it much harder to ever miss a non-blocking
+exception again, even if you forgot to handle it yourself.
+
 =head2 Timers
 
 Timers, another primary feature of the event loop, are created with
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/lib/Mojolicious.pm 
new/Mojolicious-8.29/lib/Mojolicious.pm
--- old/Mojolicious-8.27/lib/Mojolicious.pm     2019-11-25 22:39:44.000000000 
+0100
+++ new/Mojolicious-8.29/lib/Mojolicious.pm     2019-12-28 00:28:28.000000000 
+0100
@@ -59,7 +59,7 @@
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Supervillain';
-our $VERSION  = '8.27';
+our $VERSION  = '8.29';
 
 sub BUILD_DYNAMIC {
   my ($class, $method, $dyn_methods) = @_;
@@ -137,7 +137,7 @@
 
   # Dispatcher has to be last in the chain
   ++$self->{dispatch}
-    and $self->hook(around_action   => sub { $_[2]($_[1]) })
+    and $self->hook(around_action   => \&_action)
     and $self->hook(around_dispatch => sub { $_[1]->app->dispatch($_[1]) })
     unless $self->{dispatch};
 
@@ -197,6 +197,14 @@
 
 sub startup { }
 
+sub _action {
+  my ($next, $c, $action, $last) = @_;
+  my $val = $action->($c);
+  $val->catch(sub { $c->helpers->reply->exception(shift) })
+    if Scalar::Util::blessed $val && $val->isa('Mojo::Promise');
+  return $val;
+}
+
 sub _die { CORE::die ref $_[0] ? $_[0] : Mojo::Exception->new(shift)->trace }
 
 sub _exception {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/t/mojo/promise.t 
new/Mojolicious-8.29/t/mojo/promise.t
--- old/Mojolicious-8.27/t/mojo/promise.t       2019-11-25 22:40:21.000000000 
+0100
+++ new/Mojolicious-8.29/t/mojo/promise.t       2019-12-24 01:09:02.000000000 
+0100
@@ -142,7 +142,7 @@
 $promise = Mojo::Promise->new;
 @results = ();
 $promise->finally(sub { push @results, 'finally1' })
-  ->finally(sub       { push @results, 'finally2' });
+  ->finally(sub { push @results, 'finally2' });
 $promise->resolve('pass');
 Mojo::IOLoop->one_tick;
 is_deeply \@results, ['finally1', 'finally2'], 'promise not resolved';
@@ -197,7 +197,7 @@
 $promise3->resolve('third');
 $promise->resolve('first');
 Mojo::IOLoop->one_tick;
-is_deeply \@results, ['second'], 'promises resolved';
+is_deeply \@results, ['second'], 'promise resolved';
 
 # Rejected race
 $promise  = Mojo::Promise->new->then(sub {@_});
@@ -213,6 +213,32 @@
 is_deeply \@results, [], 'promises not resolved';
 is_deeply \@errors, ['second'], 'promise rejected';
 
+# Any
+$promise  = Mojo::Promise->new->then(sub {@_});
+$promise2 = Mojo::Promise->new->then(sub {@_});
+$promise3 = Mojo::Promise->new->then(sub {@_});
+@results  = ();
+Mojo::Promise->any($promise2, $promise, $promise3)->then(sub { @results = @_ 
});
+$promise2->reject('second');
+$promise3->resolve('third');
+$promise->resolve('first');
+Mojo::IOLoop->one_tick;
+is_deeply \@results, ['third'], 'promise resolved';
+
+# Any (all rejections)
+$promise  = Mojo::Promise->new->then(sub {@_});
+$promise2 = Mojo::Promise->new->then(sub {@_});
+$promise3 = Mojo::Promise->new->then(sub {@_});
+(@results, @errors) = ();
+Mojo::Promise->any($promise, $promise2, $promise3)
+  ->then(sub { @results = @_ }, sub { @errors = @_ });
+$promise2->reject('second');
+$promise3->reject('third');
+$promise->reject('first');
+Mojo::IOLoop->one_tick;
+is_deeply \@results, [], 'promises not resolved';
+is_deeply \@errors, [['first'], ['second'], ['third']], 'promises rejected';
+
 # Timeout
 (@errors, @results) = @_;
 $promise  = Mojo::Promise->timeout(0.25 => 'Timeout1');
@@ -265,6 +291,43 @@
 is_deeply \@results, [], 'promises not resolved';
 is_deeply \@errors, ['third'], 'promise rejected';
 
+# All settled
+$promise  = Mojo::Promise->new->then(sub {@_});
+$promise2 = Mojo::Promise->new->then(sub {@_});
+$promise3 = Mojo::Promise->new->then(sub {@_});
+@results  = ();
+Mojo::Promise->all_settled($promise, $promise2, $promise3)
+  ->then(sub { @results = @_ });
+$promise2->resolve('second');
+$promise3->resolve('third');
+$promise->resolve('first');
+Mojo::IOLoop->one_tick;
+my $result = [
+  {status => 'fulfilled', value => ['first']},
+  {status => 'fulfilled', value => ['second']},
+  {status => 'fulfilled', value => ['third']}
+];
+is_deeply \@results, $result, 'promise resolved';
+
+# All settled (with rejection)
+$promise  = Mojo::Promise->new->then(sub {@_});
+$promise2 = Mojo::Promise->new->then(sub {@_});
+$promise3 = Mojo::Promise->new->then(sub {@_});
+(@results, @errors) = ();
+Mojo::Promise->all_settled($promise, $promise2, $promise3)
+  ->then(sub { @results = @_ }, sub { @errors = @_ });
+$promise2->resolve('second');
+$promise3->reject('third');
+$promise->resolve('first');
+Mojo::IOLoop->one_tick;
+is_deeply \@errors, [], 'promise not rejected';
+$result = [
+  {status => 'fulfilled', value  => ['first']},
+  {status => 'fulfilled', value  => ['second']},
+  {status => 'rejected',  reason => ['third']}
+];
+is_deeply \@results, $result, 'promise resolved';
+
 # Settle with promise
 $promise  = Mojo::Promise->new->resolve('works');
 @results  = ();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/t/mojo/promise_async_await.t 
new/Mojolicious-8.29/t/mojo/promise_async_await.t
--- old/Mojolicious-8.27/t/mojo/promise_async_await.t   1970-01-01 
01:00:00.000000000 +0100
+++ new/Mojolicious-8.29/t/mojo/promise_async_await.t   2019-12-28 
17:56:17.000000000 +0100
@@ -0,0 +1,139 @@
+use Mojo::Base -strict;
+
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
+
+use Test::More;
+
+BEGIN {
+  plan skip_all => 'set TEST_ASYNC_AWAIT to enable this test (developer only!)'
+    unless $ENV{TEST_ASYNC_AWAIT} || $ENV{TEST_ALL};
+  plan skip_all => 'Future::AsyncAwait::Frozen 0.36+ required for this test!'
+    unless Mojo::Base->ASYNC;
+}
+use Mojo::Base -async;
+
+use Test::Mojo;
+use Mojo::Promise;
+use Mojo::UserAgent;
+use Mojolicious::Lite;
+
+# Silence
+app->log->level('fatal');
+
+helper defer_resolve_p => sub {
+  my ($c, $msg) = @_;
+  my $promise = Mojo::Promise->new;
+  Mojo::IOLoop->next_tick(sub { $promise->resolve($msg) });
+  return $promise;
+};
+
+helper defer_reject_p => sub {
+  my ($c, $msg) = @_;
+  my $promise = Mojo::Promise->new;
+  Mojo::IOLoop->next_tick(sub { $promise->reject($msg) });
+  return $promise;
+};
+
+get '/one' => {text => 'works!'};
+
+get '/two' => {text => 'also'};
+
+get '/three' => async sub {
+  my $c      = shift;
+  my $first  = await $c->defer_resolve_p('this ');
+  my $second = await $c->defer_resolve_p('works');
+  my $third  = await $c->defer_resolve_p(' too!');
+  $c->render(text => "$first$second$third");
+};
+
+get '/four' => async sub {
+  my $c = shift;
+
+  my $text = await Mojo::Promise->resolve('fail');
+  eval { await $c->defer_reject_p('this went perfectly') };
+  if   (my $err = $@) { $c->render(text => $err, status => 500) }
+  else                { $c->render(text => $text) }
+};
+
+get '/five' => async sub {
+  my $c       = shift;
+  my $runaway = $c->defer_reject_p('runaway too');
+  await $c->defer_resolve_p('fail');
+  await $runaway;
+};
+
+get '/six' => sub {
+  my $c = shift;
+  $c->on(
+    message => async sub {
+      my ($c, $msg) = @_;
+      my $first  = await $c->defer_resolve_p("One: $msg");
+      my $second = await $c->defer_resolve_p("Two: $msg");
+      $c->send("$first $second")->finish;
+    }
+  );
+};
+
+my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+
+async sub test_one {
+  await $ua->get_p('/one');
+}
+
+async sub test_two {
+  my $separator = shift;
+
+  my $text = '';
+  my $two  = await $ua->get_p('/two');
+  $text .= $two->res->body;
+  my $one = await $ua->get_p('/one');
+  $text .= $separator . $one->res->body;
+
+  return $text;
+}
+
+async sub test_three {
+  my $ok = shift;
+  return Mojo::Promise->new(sub {
+    my ($resolve, $reject) = @_;
+    Mojo::IOLoop->next_tick(sub { ($ok ? $resolve : $reject)->('value') });
+  });
+}
+
+my $t = Test::Mojo->new;
+
+# Basic async/await
+my $promise = test_one();
+isa_ok $promise, 'Mojo::Promise', 'right class';
+my $tx;
+$promise->then(sub { $tx = shift })->catch(sub { warn @_ });
+$promise->wait;
+is $tx->res->body, 'works!', 'right content';
+
+# Multiple awaits
+my $text;
+test_two(' ')->then(sub { $text = shift })->catch(sub { warn @_ })->wait;
+is $text, 'also works!', 'right content';
+
+# Application with async/await action
+$t->get_ok('/three')->content_is('this works too!');
+
+# Exception handling and async/await
+$t->get_ok('/four')->status_is(500)->content_like(qr/this went perfectly/);
+
+# Runaway exception
+$t->get_ok('/five')->status_is(500)->content_like(qr/runaway too/);
+
+# Async function body returning a promise
+$text = undef;
+test_three(1)->then(sub { $text = shift })->catch(sub { warn @_ })->wait;
+is $text, 'value', 'right content';
+$text = undef;
+test_three(0)->then(sub { warn @_ })->catch(sub { $text = shift })->wait;
+is $text, 'value', 'right content';
+
+# Async WebSocket
+$t->websocket_ok('/six')->send_ok('test')
+  ->message_ok->message_is('One: test Two: test')->finish_ok;
+
+done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-8.27/t/pod_coverage.t 
new/Mojolicious-8.29/t/pod_coverage.t
--- old/Mojolicious-8.27/t/pod_coverage.t       2019-11-25 22:40:27.000000000 
+0100
+++ new/Mojolicious-8.29/t/pod_coverage.t       2019-12-26 16:13:28.000000000 
+0100
@@ -7,4 +7,11 @@
 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', 'success']});
+# async/await hooks
+my @await = (
+  qw(AWAIT_CLONE AWAIT_DONE AWAIT_FAIL AWAIT_GET AWAIT_IS_CANCELLED),
+  qw(AWAIT_IS_READY AWAIT_NEW_DONE AWAIT_NEW_FAIL AWAIT_ON_CANCEL),
+  qw(AWAIT_ON_READY)
+);
+
+all_pod_coverage_ok({also_private => ['BUILD_DYNAMIC', @await, 'success']});


Reply via email to