Hello community,
here is the log from the commit of package perl-Mojolicious for
openSUSE:Factory checked in at 2019-04-20 17:13:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
and /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.5536 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Mojolicious"
Sat Apr 20 17:13:04 2019 rev:107 rq:695937 version:8.14
Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes
2019-02-04 14:25:45.669043835 +0100
+++
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.5536/perl-Mojolicious.changes
2019-04-20 17:13:12.510927460 +0200
@@ -1,0 +2,30 @@
+Fri Apr 19 05:17:46 UTC 2019 - Stephan Kulow <[email protected]>
+
+- updated to 8.14
+ see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+ 8.14 2019-04-18
+ - Added EXPERIMENTAL timer method to Mojo::Promise.
+ - Added header_exists and header_exists_not methods to Test::Mojo.
+ - Fixed a bug where the finally callback in Mojo::Promise was passed a
value,
+ which is incompatible with the JavaScript API.
+ - Fixed a bug in Mojo::Promise where the finally method could change
promise
+ values.
+ - Fixed a merge bug in Mojo::Parameters where multiple values sharing the
same
+ name could get lost.
+
+-------------------------------------------------------------------
+Fri Mar 22 06:19:30 UTC 2019 - Stephan Kulow <[email protected]>
+
+- updated to 8.13
+ see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+ 8.13 2019-03-21
+ - Added EXPERIMENTAL map method to Mojo::Promise. (jberger)
+ - Added EXPERIMENTAL min_compress_size attribute to Mojolicious::Renderer.
+ (CandyAngel, mjemmeson)
+ - Improved the security of signed cookies by also signing the cookie name.
+ Note that this means that all sessions will be reset.
+ - Fixed Mojo::IOLoop::Server to not check if listen sockets are writable.
+
+-------------------------------------------------------------------
Old:
----
Mojolicious-8.12.tar.gz
New:
----
Mojolicious-8.14.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.RZEvoP/_old 2019-04-20 17:13:14.114929881 +0200
+++ /var/tmp/diff_new_pack.RZEvoP/_new 2019-04-20 17:13:14.114929881 +0200
@@ -17,7 +17,7 @@
Name: perl-Mojolicious
-Version: 8.12
+Version: 8.14
Release: 0
%define cpan_name Mojolicious
Summary: Real-time web framework
++++++ Mojolicious-8.12.tar.gz -> Mojolicious-8.14.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/Changes new/Mojolicious-8.14/Changes
--- old/Mojolicious-8.12/Changes 2019-01-31 00:20:26.000000000 +0100
+++ new/Mojolicious-8.14/Changes 2019-04-18 20:53:24.000000000 +0200
@@ -1,4 +1,22 @@
+8.14 2019-04-18
+ - Added EXPERIMENTAL timer method to Mojo::Promise.
+ - Added header_exists and header_exists_not methods to Test::Mojo.
+ - Fixed a bug where the finally callback in Mojo::Promise was passed a value,
+ which is incompatible with the JavaScript API.
+ - Fixed a bug in Mojo::Promise where the finally method could change promise
+ values.
+ - Fixed a merge bug in Mojo::Parameters where multiple values sharing the
same
+ name could get lost.
+
+8.13 2019-03-21
+ - Added EXPERIMENTAL map method to Mojo::Promise. (jberger)
+ - Added EXPERIMENTAL min_compress_size attribute to Mojolicious::Renderer.
+ (CandyAngel, mjemmeson)
+ - Improved the security of signed cookies by also signing the cookie name.
+ Note that this means that all sessions will be reset.
+ - Fixed Mojo::IOLoop::Server to not check if listen sockets are writable.
+
8.12 2019-01-27
- Added EXPERIMENTAL timeout method to Mojo::Promise. (batman)
- Removed deprecated module Mojolicious::Plugin::PODRenderer.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/META.json
new/Mojolicious-8.14/META.json
--- old/Mojolicious-8.12/META.json 2019-02-01 17:33:33.000000000 +0100
+++ new/Mojolicious-8.14/META.json 2019-04-18 21:10:14.000000000 +0200
@@ -58,6 +58,6 @@
},
"x_IRC" : "irc://irc.freenode.net/#mojo"
},
- "version" : "8.12",
- "x_serialization_backend" : "JSON::PP version 4.00"
+ "version" : "8.14",
+ "x_serialization_backend" : "JSON::PP version 4.02"
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/META.yml
new/Mojolicious-8.14/META.yml
--- old/Mojolicious-8.12/META.yml 2019-02-01 17:33:33.000000000 +0100
+++ new/Mojolicious-8.14/META.yml 2019-04-18 21:10:14.000000000 +0200
@@ -31,5 +31,5 @@
homepage: https://mojolicious.org
license: http://www.opensource.org/licenses/artistic-license-2.0
repository: https://github.com/mojolicious/mojo.git
-version: '8.12'
+version: '8.14'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojo/Base.pm
new/Mojolicious-8.14/lib/Mojo/Base.pm
--- old/Mojolicious-8.12/lib/Mojo/Base.pm 2018-12-30 19:18:59.000000000
+0100
+++ new/Mojolicious-8.14/lib/Mojo/Base.pm 2019-04-12 19:38:24.000000000
+0200
@@ -242,7 +242,7 @@
use Role::Tiny;
sub has { Mojo::Base::attr(__PACKAGE__, @_) }
-On Perl 5.20+ you can also append a C<-signatures> flag to all three forms and
+On Perl 5.20+ you can also append a C<-signatures> flag to all four forms and
enable support for L<subroutine signatures|perlsub/"Signatures">.
# Also enable signatures
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojo/IOLoop/Server.pm
new/Mojolicious-8.14/lib/Mojo/IOLoop/Server.pm
--- old/Mojolicious-8.12/lib/Mojo/IOLoop/Server.pm 2019-01-02
14:56:13.000000000 +0100
+++ new/Mojolicious-8.14/lib/Mojo/IOLoop/Server.pm 2019-03-19
18:30:39.000000000 +0100
@@ -91,7 +91,8 @@
my $self = shift;
weaken $self;
++$self->{active}
- and $self->reactor->io($self->{handle} => sub { $self->_accept });
+ and $self->reactor->io($self->{handle} => sub { $self->_accept })
+ ->watch($self->{handle}, 1, 0);
}
sub stop { delete($_[0]{active}) and $_[0]->reactor->remove($_[0]{handle}) }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojo/Parameters.pm
new/Mojolicious-8.14/lib/Mojo/Parameters.pm
--- old/Mojolicious-8.12/lib/Mojo/Parameters.pm 2018-11-22 21:21:52.000000000
+0100
+++ new/Mojolicious-8.14/lib/Mojo/Parameters.pm 2019-04-02 20:49:20.000000000
+0200
@@ -53,8 +53,9 @@
sub merge {
my $self = shift;
- my @pairs = @_ == 1 ? @{shift->pairs} : @_;
- while (my ($name, $value) = splice @pairs, 0, 2) {
+ my $merge = @_ == 1 ? shift->to_hash : {@_};
+ for my $name (sort keys %$merge) {
+ my $value = $merge->{$name};
defined $value ? $self->param($name => $value) : $self->remove($name);
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojo/Promise.pm
new/Mojolicious-8.14/lib/Mojo/Promise.pm
--- old/Mojolicious-8.12/lib/Mojo/Promise.pm 2019-01-29 14:41:50.000000000
+0100
+++ new/Mojolicious-8.14/lib/Mojo/Promise.pm 2019-04-18 20:48:16.000000000
+0200
@@ -41,6 +41,31 @@
return $new;
}
+sub map {
+ my ($class, $options) = (shift, ref $_[0] eq 'HASH' ? shift : {});
+ my ($cb, @items) = @_;
+
+ my @start = map { $_->$cb } splice @items, 0,
+ $options->{concurrency} // @items;
+ my $proto = $class->resolve($start[0]);
+
+ my (@trigger, @wait);
+ for my $item (@items) {
+ my $p = $proto->clone;
+ push @trigger, $p;
+ push @wait, $p->then(sub { local $_ = $item; $_->$cb });
+ }
+
+ my @all = map {
+ $proto->clone->resolve($_)->then(
+ sub { shift(@trigger)->resolve if @trigger; @_ },
+ sub { @trigger = (); $proto->clone->reject($_[0]) },
+ )
+ } (@start, @wait);
+
+ return $class->all(@all);
+}
+
sub race {
my ($class, @promises) = @_;
my $new = $promises[0]->clone;
@@ -63,12 +88,8 @@
return $new;
}
-sub timeout {
- my ($self, $after, $err)
- = (ref $_[0] ? shift : shift->new, @_, 'Promise timeout');
- $self->ioloop->timer($after => sub { $self->reject($err) });
- return $self;
-}
+sub timer { shift->_timer('resolve', @_) }
+sub timeout { shift->_timer('reject', @_) }
sub wait {
my $self = shift;
@@ -89,19 +110,18 @@
sub _finally {
my ($new, $finally, $method, @result) = @_;
- my ($res) = eval { $finally->(@result) };
- return $new->$method(@result)
- unless $res && blessed $res && $res->can('then');
- $res->then(sub { $new->$method(@result) }, sub { $new->$method(@result) });
+ return $new->reject($@) unless eval { $finally->(); 1 };
+ return $new->$method(@result);
}
sub _settle {
my ($self, $status) = (shift, shift);
- $self = $self->new unless ref $self;
+ my $thenable = blessed $_[0] && $_[0]->can('then');
+ $self = $thenable ? $_[0]->clone : $self->new unless ref $self;
$_[0]->then(sub { $self->resolve(@_); () }, sub { $self->reject(@_); () })
and return $self
- if blessed $_[0] && $_[0]->can('then');
+ if $thenable;
return $self if $self->{result};
@@ -120,6 +140,14 @@
return $new->resolve(@res);
}
+sub _timer {
+ my ($self, $method, $after, @result) = @_;
+ $self = $self->new unless ref $self;
+ $result[0] = 'Promise timeout' if $method eq 'reject' && !@result;
+ $self->ioloop->timer($after => sub { $self->$method(@result) });
+ return $self;
+}
+
1;
=encoding utf8
@@ -286,10 +314,36 @@
# Do something on fulfillment and rejection
$promise->finally(sub {
- my @value_or_reason = @_;
say "We are done!";
});
+=head2 map
+
+ my $new = Mojo::Promise->map(sub { ... }, @items);
+ my $new = Mojo::Promise->map({concurrency => 3}, sub { ... }, @items);
+
+Apply a function that returns a L<Mojo::Promise> to each item in a list of
+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!
+
+ # Perform 3 requests at a time concurrently
+ Mojo::Promise->map({concurrency => 3}, sub { $ua->get_p($_) }, @urls)
+ ->then(sub{ say $_->[0]->res->dom->at('title')->text for @_ });
+
+These options are currently available:
+
+=over 2
+
+=item concurrency
+
+ concurrency => 3
+
+The maximum number of items that are in progress at the same time.
+
+=back
+
=head2 race
my $new = Mojo::Promise->race(@promises);
@@ -355,6 +409,17 @@
}
);
+=head2 timer
+
+ my $new = Mojo::Promise->timer(5 => 'Success!');
+ $promise = $promise->timer(5 => 'Success!');
+ $promise = $promise->timer(5);
+
+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!
+
=head2 timeout
my $new = Mojo::Promise->timeout(5 => 'Timeout!');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojolicious/Controller.pm
new/Mojolicious-8.14/lib/Mojolicious/Controller.pm
--- old/Mojolicious-8.12/lib/Mojolicious/Controller.pm 2018-11-22
21:21:59.000000000 +0100
+++ new/Mojolicious-8.14/lib/Mojolicious/Controller.pm 2019-03-13
21:34:12.000000000 +0100
@@ -85,7 +85,7 @@
my $valid;
for my $secret (@$secrets) {
- my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
+ my $check = Mojo::Util::hmac_sha1_sum("$name=$value", $secret);
++$valid and last if Mojo::Util::secure_compare($signature, $check);
}
if ($valid) { push @results, $value }
@@ -239,8 +239,8 @@
return $self->every_signed_cookie($name)->[-1] unless defined $value;
# Response cookie
- my $checksum = Mojo::Util::hmac_sha1_sum($value, $self->app->secrets->[0]);
- return $self->cookie($name, "$value--$checksum", $options);
+ my $sum = Mojo::Util::hmac_sha1_sum("$name=$value",
$self->app->secrets->[0]);
+ return $self->cookie($name, "$value--$sum", $options);
}
sub stash { Mojo::Util::_stash(stash => @_) }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/Mojolicious-8.12/lib/Mojolicious/Guides/Contributing.pod
new/Mojolicious-8.14/lib/Mojolicious/Guides/Contributing.pod
--- old/Mojolicious-8.12/lib/Mojolicious/Guides/Contributing.pod
2018-12-31 13:37:21.000000000 +0100
+++ new/Mojolicious-8.14/lib/Mojolicious/Guides/Contributing.pod
2019-04-12 19:39:32.000000000 +0200
@@ -182,22 +182,6 @@
=back
-=head1 DONATIONS
-
-L<Mojolicious> is open source and free to use. However, the amount of effort
-needed to maintain the project and develop new features for it is not
-sustainable without proper financial backing. You can support the ongoing
-development of L<Mojolicious> through PayPal (C<[email protected]>).
-
-If you run a business and use L<Mojolicious> in a revenue generating product,
it
-makes business sense to support L<Mojolicious> development. Because it ensures
-that the project your product relies on stays healthy and actively maintained.
-It can also help your exposure within the community and will make it easier to
-attract L<Mojolicious> developers.
-
-Please email us (C<[email protected]>) if you have any questions about
-becoming a sponsor.
-
=head1 CODE OF CONDUCT
Like the technical community as a whole, the L<Mojolicious> team and community
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojolicious/Guides/Routing.pod
new/Mojolicious-8.14/lib/Mojolicious/Guides/Routing.pod
--- old/Mojolicious-8.12/lib/Mojolicious/Guides/Routing.pod 2019-01-05
02:32:59.000000000 +0100
+++ new/Mojolicious-8.14/lib/Mojolicious/Guides/Routing.pod 2019-04-05
00:21:34.000000000 +0200
@@ -855,7 +855,7 @@
my $resource = $r->any("/$name")->to("$name#");
# Render a list of resources
- $resource->get->to('#index')->name($name);
+ $resource->get('/')->to('#index')->name($name);
# Render a form to create a new resource (submitted to "store")
$resource->get('/create')->to('#create')->name("create_$name");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojolicious/Renderer.pm
new/Mojolicious-8.14/lib/Mojolicious/Renderer.pm
--- old/Mojolicious-8.12/lib/Mojolicious/Renderer.pm 2018-11-22
21:21:59.000000000 +0100
+++ new/Mojolicious-8.14/lib/Mojolicious/Renderer.pm 2019-03-13
23:50:43.000000000 +0100
@@ -14,7 +14,8 @@
has default_format => 'html';
has encoding => 'UTF-8';
has [qw(handlers helpers)] => sub { {} };
-has paths => sub { [] };
+has min_compress_size => 860;
+has paths => sub { [] };
# Bundled templates
my $TEMPLATES = path(__FILE__)->sibling('resources', 'templates');
@@ -122,7 +123,7 @@
# Gzip compression
my $res = $c->res;
- if ($self->compress) {
+ if ($self->compress && length($output) >= $self->min_compress_size) {
my $headers = $res->headers;
$headers->append(Vary => 'Accept-Encoding');
my $gzip = ($c->req->headers->accept_encoding // '') =~ /gzip/i;
@@ -328,6 +329,15 @@
Registered helpers.
+=head2 min_compress_size
+
+ my $size = $renderer->min_compress_size;
+ $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
+without warning!
+
=head2 paths
my $paths = $renderer->paths;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Mojolicious.pm
new/Mojolicious-8.14/lib/Mojolicious.pm
--- old/Mojolicious-8.12/lib/Mojolicious.pm 2019-01-02 19:04:39.000000000
+0100
+++ new/Mojolicious-8.14/lib/Mojolicious.pm 2019-03-21 23:06:06.000000000
+0100
@@ -59,7 +59,7 @@
has validator => sub { Mojolicious::Validator->new };
our $CODENAME = 'Supervillain';
-our $VERSION = '8.12';
+our $VERSION = '8.14';
sub BUILD_DYNAMIC {
my ($class, $method, $dyn_methods) = @_;
@@ -1078,6 +1078,8 @@
Michael Harris
+Michael Jemmeson
+
Mike Magowan
Mirko Westermeier
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/lib/Test/Mojo.pm
new/Mojolicious-8.14/lib/Test/Mojo.pm
--- old/Mojolicious-8.12/lib/Test/Mojo.pm 2018-11-22 21:21:40.000000000
+0100
+++ new/Mojolicious-8.14/lib/Test/Mojo.pm 2019-03-23 02:23:14.000000000
+0100
@@ -121,6 +121,20 @@
sub get_ok { shift->_build_ok(GET => @_) }
sub head_ok { shift->_build_ok(HEAD => @_) }
+sub header_exists {
+ my ($self, $name, $desc) = @_;
+ $desc = _desc($desc, qq{header "$name" exists});
+ return $self->_test('ok', !!@{$self->tx->res->headers->every_header($name)},
+ $desc);
+}
+
+sub header_exists_not {
+ my ($self, $name, $desc) = @_;
+ $desc = _desc($desc, qq{no "$name" header});
+ return $self->_test('ok', !@{$self->tx->res->headers->every_header($name)},
+ $desc);
+}
+
sub header_is {
my ($self, $name, $value, $desc) = @_;
$desc = _desc($desc, "$name: " . ($value // ''));
@@ -724,6 +738,20 @@
Perform a C<HEAD> request and check for transport errors, takes the same
arguments as L<Mojo::UserAgent/"head">, except for the callback.
+=head2 header_exists
+
+ $t = $t->header_exists('ETag');
+ $t = $t->header_exists('ETag', 'header exists');
+
+Check if response header exists.
+
+=head2 header_exists_not
+
+ $t = $t->header_exists_not('ETag');
+ $t = $t->header_exists_not('ETag', 'header is missing');
+
+Opposite of L</"header_exists">.
+
=head2 header_is
$t = $t->header_is(ETag => '"abc321"');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/t/mojo/parameters.t
new/Mojolicious-8.14/t/mojo/parameters.t
--- old/Mojolicious-8.12/t/mojo/parameters.t 2018-11-22 21:22:59.000000000
+0100
+++ new/Mojolicious-8.14/t/mojo/parameters.t 2019-04-02 20:54:59.000000000
+0200
@@ -37,6 +37,17 @@
is $params->merge(Mojo::Parameters->new(z => 6))->to_string,
'foo=b%3Bar&a=4&a=5&b=6&b=7&c=f%3Boo&x=1&y=3&z=6', 'right format';
+# Merge (instances)
+$params
+ = Mojo::Parameters->new->merge(Mojo::Parameters->new(foo => [123, 456]));
+is_deeply $params->to_hash, {foo => [123, 456]}, 'right structure';
+$params = Mojo::Parameters->new(foo => 321)
+ ->merge(Mojo::Parameters->new(foo => [123, 456]));
+is_deeply $params->to_hash, {foo => [123, 456]}, 'right structure';
+$params = Mojo::Parameters->new(bar => 321)
+ ->merge(Mojo::Parameters->new(foo => [123, 456]));
+is_deeply $params->to_hash, {foo => [123, 456], bar => 321}, 'right structure';
+
# Param
$params
= Mojo::Parameters->new('foo=b%3Bar&a=4&a=5&b=6&b=7&c=f%3Boo&x=1&y=3&z=6');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/t/mojo/promise.t
new/Mojolicious-8.14/t/mojo/promise.t
--- old/Mojolicious-8.12/t/mojo/promise.t 2019-01-29 14:40:35.000000000
+0100
+++ new/Mojolicious-8.14/t/mojo/promise.t 2019-04-18 20:51:27.000000000
+0200
@@ -31,11 +31,11 @@
# Resolved with finally
$promise = Mojo::Promise->new;
@results = ();
-$promise->finally(sub { @results = @_; 'fail' })
+$promise->finally(sub { @results = ('finally'); 'fail' })
->then(sub { push @results, @_ });
$promise->resolve('hello', 'world');
Mojo::IOLoop->one_tick;
-is_deeply \@results, ['hello', 'world', 'hello', 'world'], 'promise settled';
+is_deeply \@results, ['finally', 'hello', 'world'], 'promise settled';
# Rejected
$promise = Mojo::Promise->new;
@@ -62,11 +62,11 @@
# Rejected with finally
$promise = Mojo::Promise->new;
@errors = ();
-$promise->finally(sub { @errors = @_; 'fail' })
+$promise->finally(sub { @errors = ('finally'); 'fail' })
->then(undef, sub { push @errors, @_ });
$promise->reject('bye', 'world');
Mojo::IOLoop->one_tick;
-is_deeply \@errors, ['bye', 'world', 'bye', 'world'], 'promise settled';
+is_deeply \@errors, ['finally', 'bye', 'world'], 'promise settled';
# No state change
$promise = Mojo::Promise->new;
@@ -120,17 +120,31 @@
Mojo::IOLoop->one_tick;
is_deeply \@errors, ['hello world'], 'promise rejected';
+# Double finally
+$promise = Mojo::Promise->new;
+@results = ();
+$promise->finally(sub { push @results, 'finally1' })
+ ->finally(sub { push @results, 'finally2' });
+$promise->resolve('pass');
+Mojo::IOLoop->one_tick;
+is_deeply \@results, ['finally1', 'finally2'], 'promise not resolved';
+
# Resolved nested with finally
$promise = Mojo::Promise->new;
$promise2 = Mojo::Promise->new;
@results = ();
-$promise->finally(sub {$promise2})->finally(sub { @results = @_ });
+$promise->finally(sub {$promise2})->finally(sub { @results = ('finally') });
$promise->resolve('pass');
Mojo::IOLoop->one_tick;
-is_deeply \@results, [], 'promise not resolved';
-$promise2->resolve('fail');
+is_deeply \@results, ['finally'], 'promise already resolved';
+
+# Exception in finally
+$promise = Mojo::Promise->new;
+@results = ();
+$promise->finally(sub { die "Test!\n" })->catch(sub { push @results, @_ });
+$promise->resolve('pass');
Mojo::IOLoop->one_tick;
-is_deeply \@results, ['pass'], 'promise resolved';
+is_deeply \@results, ["Test!\n"], 'promise rejected';
# Clone
my $loop = Mojo::IOLoop->new;
@@ -196,6 +210,17 @@
Mojo::Promise->timeout(0.025)->catch(sub { @errors = @_ })->wait;
is_deeply \@errors, ['Promise timeout'], 'default timeout message';
+# Timer without value
+@results = ();
+Mojo::Promise->timer(0.025)->then(sub { @results = (@_, 'works!') })->wait;
+is_deeply \@results, ['works!'], 'default timer result';
+
+# Timer with values
+@results = ();
+Mojo::Promise->new->timer(0, 'first', 'second')
+ ->then(sub { @results = (@_, 'works too!') })->wait;
+is_deeply \@results, ['first', 'second', 'works too!'], 'timer result';
+
# All
$promise = Mojo::Promise->new->then(sub {@_});
$promise2 = Mojo::Promise->new->then(sub {@_});
@@ -239,4 +264,58 @@
is_deeply \@errors, ['first', 'works too', 'second', 'works too'],
'promises rejected';
+# Map
+my @started;
+(@results, @errors) = ();
+$promise = Mojo::Promise->map(sub { push @started, $_; $_ }, 1 .. 5)
+ ->then(sub { @results = @_ }, sub { @errors = @_ });
+is_deeply \@started, [1, 2, 3, 4, 5], 'all started without concurrency';
+$promise->wait;
+is_deeply \@results, [[1], [2], [3], [4], [5]], 'correct result';
+is_deeply \@errors, [], 'promise not rejected';
+
+# Map (with concurrency limit)
+my $concurrent = 0;
+(@results, @errors) = ();
+Mojo::Promise->map(
+ {concurrency => 3},
+ sub {
+ my $n = $_;
+ fail 'Concurrency too high' if ++$concurrent > 3;
+ Mojo::Promise->resolve->then(sub {
+ fail 'Concurrency too high' if $concurrent-- > 3;
+ $n;
+ });
+ },
+ 1 .. 5
+)->then(sub { @results = @_ }, sub { @errors = @_ })->wait;
+is_deeply \@results, [[1], [2], [3], [4], [5]], 'correct result';
+is_deeply \@errors, [], 'promise not rejected';
+
+# Map (with reject)
+(@started, @results, @errors) = ();
+Mojo::Promise->map(
+ {concurrency => 3},
+ sub {
+ my $n = $_;
+ push @started, $n;
+ Mojo::Promise->resolve->then(sub { Mojo::Promise->reject($n) });
+ },
+ 1 .. 5
+)->then(sub { @results = @_ }, sub { @errors = @_ })->wait;
+is_deeply \@results, [], 'promise not resolved';
+is_deeply \@errors, [1], 'correct errors';
+is_deeply \@started, [1, 2, 3], 'only initial batch started';
+
+# Map (custom event loop)
+my $ok;
+$loop = Mojo::IOLoop->new;
+$promise
+ = Mojo::Promise->map(sub { Mojo::Promise->new(ioloop => $loop)->resolve },
1);
+is $promise->ioloop, $loop, 'same loop';
+isnt $promise->ioloop, Mojo::IOLoop->singleton, 'not the singleton';
+$promise->then(sub { $ok = 1; $loop->stop });
+$loop->start;
+ok $ok, 'loop completed';
+
done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/t/mojo/template.t
new/Mojolicious-8.14/t/mojo/template.t
--- old/Mojolicious-8.12/t/mojo/template.t 2018-11-22 21:23:08.000000000
+0100
+++ new/Mojolicious-8.14/t/mojo/template.t 2019-03-19 21:38:55.000000000
+0100
@@ -39,6 +39,11 @@
$output = $mt->render('<%= (1,2,3)[1] %><%== (1,2,3)[2] %>');
is $output, "23\n", 'no ambiguity';
+# String
+$mt = Mojo::Template->new;
+$output = $mt->render('Just a <%= "test" %>');
+is $output, "Just a test\n", 'rendered string';
+
# Trim tag
$mt = Mojo::Template->new;
$output = $mt->render(" ♥ <%= 'test♥' =%> \n");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/t/mojolicious/lite_app.t
new/Mojolicious-8.14/t/mojolicious/lite_app.t
--- old/Mojolicious-8.12/t/mojolicious/lite_app.t 2018-11-22
21:23:19.000000000 +0100
+++ new/Mojolicious-8.14/t/mojolicious/lite_app.t 2019-03-23
02:24:28.000000000 +0100
@@ -726,7 +726,10 @@
->header_is(Server => 'Mojolicious (Perl)')->content_is('root fallback!');
# Root with format
-$t->get_ok('/.html')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+$t->get_ok('/.html')->status_is(200)->header_exists_not('Servers')
+ ->header_exists_not('Servers', 'the header is missing')
+ ->header_exists('Server')->header_exists('Server', 'the header exists')
+ ->header_is(Server => 'Mojolicious (Perl)')
->content_is("/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
# Reverse proxy with "X-Forwarded-For"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.12/t/mojolicious/renderer.t
new/Mojolicious-8.14/t/mojolicious/renderer.t
--- old/Mojolicious-8.12/t/mojolicious/renderer.t 2018-11-22
21:23:12.000000000 +0100
+++ new/Mojolicious-8.14/t/mojolicious/renderer.t 2019-03-13
01:05:14.000000000 +0100
@@ -143,6 +143,17 @@
'right "Content-Encoding" value';
is $c->res->body, $output, 'same string';
+# Compression (below minimum length)
+$output = 'a' x 850;
+$c = $app->build_controller;
+$c->req->headers->accept_encoding('gzip');
+$renderer->respond($c, $output, 'html');
+is $c->res->headers->content_type, 'text/html;charset=UTF-8',
+ 'right "Content-Type" value';
+ok !$c->res->headers->vary, 'no "Vary" value';
+ok !$c->res->headers->content_encoding, 'no "Content-Encoding" value';
+is $c->res->body, $output, 'same string';
+
# Missing method (AUTOLOAD)
my $class = ref $first->myapp;
eval { $first->myapp->missing };