The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 041
MANIFEST 01
META.json 11
META.yml 11
README.md 11
examples/connect-proxy.pl 11
examples/microhttpd.pl 11
lib/Mojo/ByteStream.pm 1536
lib/Mojo/Collection.pm 310
lib/Mojo/DOM/CSS.pm 11
lib/Mojo/DOM/HTML.pm 3118
lib/Mojo/DOM.pm 6323
lib/Mojo/IOLoop/Client.pm 11
lib/Mojo/IOLoop/Delay.pm 03
lib/Mojo/IOLoop.pm 39
lib/Mojo/JSON/Pointer.pm 92
lib/Mojo/JSON.pm 234
lib/Mojo/Log.pm 1919
lib/Mojo/Message/Request.pm 11
lib/Mojo/Message.pm 147
lib/Mojo/Parameters.pm 1715
lib/Mojo/Server/Daemon.pm 58
lib/Mojo/Server/Hypnotoad.pm 1921
lib/Mojo/Server/Morbo.pm 1413
lib/Mojo/Server/Prefork.pm 5571
lib/Mojo/Server.pm 55
lib/Mojo/Template.pm 33
lib/Mojo/Transaction/WebSocket.pm 55
lib/Mojo/Transaction.pm 25
lib/Mojo/UserAgent.pm 79
lib/Mojo/Util.pm 657
lib/Mojo.pm 14
lib/Mojolicious/Command/cgi.pm 44
lib/Mojolicious/Command/cpanify.pm 55
lib/Mojolicious/Command/daemon.pm 1111
lib/Mojolicious/Command/eval.pm 55
lib/Mojolicious/Command/generate/app.pm 33
lib/Mojolicious/Command/generate/lite_app.pm 44
lib/Mojolicious/Command/generate/makefile.pm 33
lib/Mojolicious/Command/generate/plugin.pm 33
lib/Mojolicious/Command/generate.pm 44
lib/Mojolicious/Command/get.pm 1212
lib/Mojolicious/Command/inflate.pm 33
lib/Mojolicious/Command/prefork.pm 2020
lib/Mojolicious/Command/psgi.pm 33
lib/Mojolicious/Command/routes.pm 44
lib/Mojolicious/Command/test.pm 44
lib/Mojolicious/Command/version.pm 44
lib/Mojolicious/Command.pm 55
lib/Mojolicious/Commands.pm 2728
lib/Mojolicious/Controller.pm 7575
lib/Mojolicious/Guides/Cookbook.pod 1919
lib/Mojolicious/Guides/FAQ.pod 11
lib/Mojolicious/Guides/Growing.pod 11
lib/Mojolicious/Guides/Rendering.pod 67
lib/Mojolicious/Guides/Routing.pod 12
lib/Mojolicious/Guides/Tutorial.pod 0951
lib/Mojolicious/Guides.pod 811
lib/Mojolicious/Lite.pm 94720
lib/Mojolicious/Plugin/Config.pm 11
lib/Mojolicious/Plugin/DefaultHelpers.pm 424
lib/Mojolicious/Plugin/EPLRenderer.pm 55
lib/Mojolicious/Plugin/TagHelpers.pm 132105
lib/Mojolicious/Renderer.pm 11
lib/Mojolicious/Routes/Route.pm 2518
lib/Mojolicious/Routes.pm 1212
lib/Mojolicious/Static.pm 11
lib/Mojolicious/Validator/Validation.pm 107
lib/Mojolicious/templates/development.html.ep 11
lib/Mojolicious.pm 1317
lib/Test/Mojo.pm 11
lib/ojo.pm 11
script/hypnotoad 44
script/morbo 55
t/mojo/asset.t 016
t/mojo/bytestream.t 03
t/mojo/content.t 09
t/mojo/cookie.t 06
t/mojo/daemon.t 04
t/mojo/hypnotoad.t 33
t/mojo/ioloop.t 23
t/mojo/json_pointer.t 01
t/mojo/log.t 2726
t/mojo/parameters.t 1010
t/mojo/prefork.t 811
t/mojo/reactor_poll.t 024
t/mojo/request.t 910
t/mojo/response.t 30
t/mojo/transactor.t 010
t/mojo/user_agent.t 32
t/mojo/util.t 310
t/mojo/websocket.t 11
t/mojolicious/app.t 1718
t/mojolicious/command.t 15
t/mojolicious/commands.t 1170
t/mojolicious/embedded_app.t 20
t/mojolicious/embedded_lite_app.t 66
t/mojolicious/external_lite_app.t 11
t/mojolicious/group_lite_app.t 66
t/mojolicious/json_config_lite_app.t 11
t/mojolicious/lib/MojoliciousTest.pm 77
t/mojolicious/lite_app.t 3818
t/mojolicious/longpolling_lite_app.t 22
t/mojolicious/rebased_lite_app.t 88
t/mojolicious/renderer.t 22
t/mojolicious/routes.t 77
t/mojolicious/tag_helper_lite_app.t 126107
t/mojolicious/validation_lite_app.t 22
t/mojolicious/websocket_lite_app.t 1519
t/pod_coverage.t 11
110 files changed (This is a version diff) 20652331
@@ -1,4 +1,45 @@
 
+5.75  2015-01-26
+  - Added healthy method to Mojo::Server::Prefork.
+  - Improved all built-in web servers to die if group or user assignment
+    failed.
+  - Improved Hypnotoad to wait for new workers to be ready before stopping the
+    old ones during hot deployment.
+  - Improved commands and log messages to use less punctuation.
+  - Fixed bug in Mojo::IOLoop where the callback passed to next_tick would
+    receive the wrong invocant.
+  - Fixed race condition and memory leak in Mojo::Server::Prefork.
+
+5.74  2015-01-25
+  - Improved parser errors to be more consistent with connection errors in
+    Mojo::Message::Request and Mojo::Message::Response.
+  - Fixed "0" value bug in Mojo::Parameters.
+  - Fixed bug where placeholder default values would not always have
+    precedence.
+  - Fixed proxy detection in get command.
+
+5.73  2015-01-24
+  - Deprecated Mojolicious::Routes::Route::bridge in favor of
+    Mojolicious::Routes::Route::under.
+  - Deprecated Mojolicious::Controller::render_exception in favor of
+    reply->exception helper.
+  - Deprecated Mojolicious::Controller::render_not_found in favor of
+    reply->not_found helper.
+  - Removed deprecated object-oriented Mojo::JSON API.
+  - Removed deprecated stringification support from Mojo::Collection.
+  - Removed deprecated support for data arguments from Mojo::JSON::Pointer.
+  - Removed deprecated AUTOLOAD and pluck methods from Mojo::Collection.
+  - Removed deprecated AUTOLOAD and val methods from Mojo::DOM.
+  - Moved tutorial from Mojolicious::Lite to Mojolicious::Guides::Tutorial.
+  - Added term_escape method to Mojo::ByteStream.
+  - Added term_escape function to Mojo::Util.
+  - Improved get command to use the user agent of the application.
+  - Improved diagnostics information for MOJO_DAEMON_DEBUG,
+    MOJO_USERAGENT_DEBUG and MOJO_WEBSOCKET_DEBUG environment variables.
+  - Fixed tag helpers to generate correct HTML5. (batman, sri)
+  - Fixed JSON Pointer escaping bug.
+  - Fixed portability bug in monkey_patch tests.
+
 5.72  2015-01-11
   - Added EXPERIMENTAL support for case-insensitive attribute selectors like
     [foo="bar" i] to Mojo::DOM::CSS.
@@ -96,6 +96,7 @@ lib/Mojolicious/Guides/FAQ.pod
 lib/Mojolicious/Guides/Growing.pod
 lib/Mojolicious/Guides/Rendering.pod
 lib/Mojolicious/Guides/Routing.pod
+lib/Mojolicious/Guides/Tutorial.pod
 lib/Mojolicious/Lite.pm
 lib/Mojolicious/Plugin.pm
 lib/Mojolicious/Plugin/Charset.pm
@@ -54,5 +54,5 @@
       },
       "x_IRC" : "irc://irc.perl.org/#mojo"
    },
-   "version" : "5.72"
+   "version" : "5.75"
 }
@@ -29,4 +29,4 @@ resources:
   homepage: http://mojolicio.us
   license: http://www.opensource.org/licenses/artistic-license-2.0
   repository: https://github.com/kraih/mojo.git
-version: '5.72'
+version: '5.75'
@@ -54,7 +54,7 @@ app->start;
   code into a file and start it with `morbo`.
 
     $ morbo hello.pl
-    Server available at http://127.0.0.1:3000.
+    Server available at http://127.0.0.1:3000
 
     $ curl http://127.0.0.1:3000/
     I ♥ Mojolicious!
@@ -37,7 +37,7 @@ Mojo::IOLoop->server(
                 }
 
                 # Start forwarding data in both directions
-                say "Forwarding to $address:$port.";
+                say "Forwarding to $address:$port";
                 Mojo::IOLoop->stream($client)
                   ->write("HTTP/1.1 200 OK\x0d\x0a"
                     . "Connection: keep-alive\x0d\x0a\x0d\x0a");
@@ -33,7 +33,7 @@ Mojo::IOLoop->server(
 
 print <<'EOF';
 Starting server on port 8080.
-Try something like "wrk -c 100 -d 10s http://127.0.0.1:8080/" for testing.
+For testing use something like "wrk -c 100 -d 10s http://127.0.0.1:8080/".
 On a MacBook Air this results in about 18k req/s.
 EOF
 
@@ -12,8 +12,8 @@ our @EXPORT_OK = ('b');
 my @UTILS = (
   qw(b64_decode b64_encode camelize decamelize hmac_sha1_sum html_unescape),
   qw(md5_bytes md5_sum punycode_decode punycode_encode quote sha1_bytes),
-  qw(sha1_sum slurp spurt squish trim unindent unquote url_escape),
-  qw(url_unescape xml_escape xor_encode)
+  qw(sha1_sum slurp spurt squish term_escape trim unindent unquote),
+  qw(url_escape url_unescape xml_escape xor_encode)
 );
 for my $name (@UTILS) {
   my $sub = Mojo::Util->can($name);
@@ -124,7 +124,8 @@ Base64 decode bytestream with L<Mojo::Util/"b64_decode">.
 
 Base64 encode bytestream with L<Mojo::Util/"b64_encode">.
 
-  b('foo bar baz')->b64_encode('')->say;
+  # "Zm9vIGJhciBiYXo="
+  b('foo bar baz')->b64_encode('');
 
 =head2 camelize
 
@@ -149,18 +150,20 @@ Decamelize bytestream with L<Mojo::Util/"decamelize">.
   $stream = $stream->decode;
   $stream = $stream->decode('iso-8859-1');
 
-Decode bytestream with L<Mojo::Util/"decode">, defaults to C<UTF-8>.
+Decode bytestream with L<Mojo::Util/"decode">, defaults to using C<UTF-8>.
 
-  $stream->decode('UTF-16LE')->unquote->trim->say;
+  # "♥"
+  b('%E2%99%A5')->url_unescape->decode;
 
 =head2 encode
 
   $stream = $stream->encode;
   $stream = $stream->encode('iso-8859-1');
 
-Encode bytestream with L<Mojo::Util/"encode">, defaults to C<UTF-8>.
+Encode bytestream with L<Mojo::Util/"encode">, defaults to using C<UTF-8>.
 
-  $stream->trim->quote->encode->say;
+  # "%E2%99%A5"
+  b('♥')->encode->url_escape;
 
 =head2 hmac_sha1_sum
 
@@ -168,7 +171,8 @@ Encode bytestream with L<Mojo::Util/"encode">, defaults to C<UTF-8>.
 
 Generate HMAC-SHA1 checksum for bytestream with L<Mojo::Util/"hmac_sha1_sum">.
 
-  b('foo bar baz')->hmac_sha1_sum('secr3t')->quote->say;
+  # "7fbdc89263974a89210ea71f171c77d3f8c21471"
+  b('foo bar baz')->hmac_sha1_sum('secr3t');
 
 =head2 html_unescape
 
@@ -176,7 +180,8 @@ Generate HMAC-SHA1 checksum for bytestream with L<Mojo::Util/"hmac_sha1_sum">.
 
 Unescape all HTML entities in bytestream with L<Mojo::Util/"html_unescape">.
 
-  b('&lt;html&gt;')->html_unescape->url_escape->say;
+  # "%3Chtml%3E"
+  b('&lt;html&gt;')->html_unescape->url_escape;
 
 =head2 md5_bytes
 
@@ -219,7 +224,7 @@ Quote bytestream with L<Mojo::Util/"quote">.
   $stream = $stream->say;
   $stream = $stream->say(*STDERR);
 
-Print bytestream to handle and append a newline, defaults to C<STDOUT>.
+Print bytestream to handle and append a newline, defaults to using C<STDOUT>.
 
 =head2 secure_compare
 
@@ -227,8 +232,6 @@ Print bytestream to handle and append a newline, defaults to C<STDOUT>.
 
 Compare bytestream with L<Mojo::Util/"secure_compare">.
 
-  say 'Match!' if b('foo')->secure_compare('foo');
-
 =head2 sha1_bytes
 
   $stream = $stream->sha1_bytes;
@@ -253,6 +256,7 @@ Size of bytestream.
 
 Read all data at once from file into bytestream with L<Mojo::Util/"slurp">.
 
+  # Read file and print lines in random order
   b('/home/sri/myapp.pl')->slurp->split("\n")->shuffle->join("\n")->say;
 
 =head2 spurt
@@ -261,6 +265,7 @@ Read all data at once from file into bytestream with L<Mojo::Util/"slurp">.
 
 Write all data from bytestream at once to file with L<Mojo::Util/"spurt">.
 
+  # Remove unnecessary whitespace from file
   b('/home/sri/foo.txt')->slurp->squish->spurt('/home/sri/bar.txt');
 
 =head2 split
@@ -270,7 +275,8 @@ Write all data from bytestream at once to file with L<Mojo::Util/"spurt">.
 Turn bytestream into L<Mojo::Collection> object containing L<Mojo::ByteStream>
 objects.
 
-  b('a,b,c')->split(',')->quote->join(',')->say;
+  # "One,Two,Three"
+  b("one,two,three")->split(',')->map('camelize')->join(',');
 
 =head2 squish
 
@@ -286,6 +292,16 @@ L<Mojo::Util/"squish">.
 
 Alias for L<Mojo::Base/"tap">.
 
+=head2 term_escape
+
+  $stream = $stream->term_escape;
+
+Escape POSIX control characters in bytestream with
+L<Mojo::Util/"term_escape">.
+
+  # Print binary checksum to terminal
+  b('foo')->sha1_bytes->term_escape->say;
+
 =head2 to_string
 
   my $str = $stream->to_string;
@@ -319,7 +335,8 @@ Unquote bytestream with L<Mojo::Util/"unquote">.
 Percent encode all unsafe characters in bytestream with
 L<Mojo::Util/"url_escape">.
 
-  b('foo bar baz')->url_escape->say;
+  # "%E2%98%83"
+  b('☃')->encode->url_escape;
 
 =head2 url_unescape
 
@@ -328,7 +345,8 @@ L<Mojo::Util/"url_escape">.
 Decode percent encoded characters in bytestream with
 L<Mojo::Util/"url_unescape">.
 
-  b('%3Chtml%3E')->url_unescape->xml_escape->say;
+  # "&lt;html&gt;"
+  b('%3Chtml%3E')->url_unescape->xml_escape;
 
 =head2 xml_escape
 
@@ -343,6 +361,9 @@ bytestream with L<Mojo::Util/"xml_escape">.
 
 XOR encode bytestream with L<Mojo::Util/"xor_encode">.
 
+  # "%04%0E%15B%03%1B%10"
+  b('foo bar')->xor_encode('baz')->url_escape;
+
 =head1 OPERATORS
 
 L<Mojo::ByteStream> overloads the following operators.
@@ -5,33 +5,10 @@ use Carp 'croak';
 use Exporter 'import';
 use List::Util;
 use Mojo::ByteStream;
-use Mojo::Util 'deprecated';
 use Scalar::Util 'blessed';
 
-# DEPRECATED in Tiger Face!
-use overload '""' => sub {
-  deprecated 'Stringification support in Mojo::Collection is DEPRECATED'
-    . ' in favor of Mojo::Collection::join';
-  shift->join("\n");
-};
-use overload bool => sub {1}, fallback => 1;
-
 our @EXPORT_OK = ('c');
 
-# DEPRECATED in Tiger Face!
-sub AUTOLOAD {
-  my $self = shift;
-  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
-  deprecated "Mojo::Collection::AUTOLOAD ($method) is DEPRECATED"
-    . ' in favor of Mojo::Collection::map';
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
-  return $self->map($method, @_);
-}
-
-# DEPRECATED in Tiger Face!
-sub DESTROY { }
-
 sub c { __PACKAGE__->new(@_) }
 
 sub compact {
@@ -77,14 +54,6 @@ sub new {
   return bless [@_], ref $class || $class;
 }
 
-# DEPRECATED in Tiger Face!
-sub pluck {
-  deprecated
-    'Mojo::Collection::pluck is DEPRECATED in favor of Mojo::Collection::map';
-  my ($self, $key) = (shift, shift);
-  return $self->new(map { ref eq 'HASH' ? $_->{$key} : $_->$key(@_) } @$self);
-}
-
 sub reduce {
   my $self = shift;
   @_ = (@_, @$self);
@@ -270,7 +270,7 @@ sub _unescape {
   $value =~ s/\\\n//g;
 
   # Unescape Unicode characters
-  $value =~ s/\\([0-9a-fA-F]{1,6})\s?/pack('U', hex $1)/ge;
+  $value =~ s/\\([0-9a-fA-F]{1,6})\s?/pack 'U', hex $1/ge;
 
   # Remove backslash
   $value =~ s/\\//g;
@@ -143,7 +143,7 @@ sub parse {
 
         # Raw text elements
         next if $xml || !$RAW{$start} && !$RCDATA{$start};
-        next unless $html =~ m!\G(.*?)<\s*/\s*$start\s*>!gcsi;
+        next unless $html =~ m!\G(.*?)<\s*/\s*\Q$start\E\s*>!gcsi;
         _node($current, 'raw', $RCDATA{$start} ? html_unescape $1 : $1);
         _end($start, 0, \$current);
       }
@@ -217,43 +217,30 @@ sub _render {
   # Processing instruction
   return '<?' . $tree->[1] . '?>' if $type eq 'pi';
 
-  # Start tag
-  my $result = '';
-  if ($type eq 'tag') {
-
-    # Open tag
-    my $tag = $tree->[1];
-    $result .= "<$tag";
-
-    # Attributes
-    my @attrs;
-    for my $key (sort keys %{$tree->[2]}) {
+  # Root
+  return join '', map { _render($_, $xml) } @$tree[1 .. $#$tree]
+    if $type eq 'root';
 
-      # No value
-      push @attrs, $key and next unless defined(my $value = $tree->[2]{$key});
-
-      # Key and value
-      push @attrs, $key . '="' . xml_escape($value) . '"';
-    }
-    $result .= join ' ', '', @attrs if @attrs;
-
-    # Element without end tag
-    return $xml ? "$result />" : $EMPTY{$tag} ? "$result>" : "$result></$tag>"
-      unless $tree->[4];
+  # Start tag
+  my $tag    = $tree->[1];
+  my $result = "<$tag";
 
-    # Close tag
-    $result .= '>';
+  # Attributes
+  for my $key (sort keys %{$tree->[2]}) {
+    $result .= " $key" and next unless defined(my $value = $tree->[2]{$key});
+    $result .= " $key" . '="' . xml_escape($value) . '"';
   }
 
-  # Render whole tree
+  # No children
+  return $xml ? "$result />" : $EMPTY{$tag} ? "$result>" : "$result></$tag>"
+    unless $tree->[4];
+
+  # Children
   no warnings 'recursion';
-  $result .= _render($tree->[$_], $xml)
-    for ($type eq 'root' ? 1 : 4) .. $#$tree;
+  $result .= '>' . join '', map { _render($_, $xml) } @$tree[4 .. $#$tree];
 
   # End tag
-  $result .= '</' . $tree->[1] . '>' if $type eq 'tag';
-
-  return $result;
+  return "$result</$tag>";
 }
 
 sub _start {
@@ -16,24 +16,6 @@ use Mojo::DOM::HTML;
 use Mojo::Util qw(deprecated squish);
 use Scalar::Util qw(blessed weaken);
 
-# DEPRECATED in Tiger Face!
-sub AUTOLOAD {
-  my $self = shift;
-
-  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
-  deprecated "Mojo::DOM::AUTOLOAD ($method) is DEPRECATED"
-    . ' in favor of Mojo::DOM::children';
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
-
-  my $children = $self->children($method);
-  return @$children > 1 ? $children : $children->[0] if @$children;
-  croak qq{Can't locate object method "$method" via package "$package"};
-}
-
-# DEPRECATED in Tiger Face!
-sub DESTROY { }
-
 sub all_contents { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
 
 sub all_text { shift->_all_text(1, @_) }
@@ -72,8 +54,8 @@ sub children { _select($_[0]->_collect(_nodes($_[0]->tree, 1)), $_[1]) }
 sub content {
   my $self = shift;
 
-  my $node = $self->node;
-  if ($node eq 'root' || $node eq 'tag') {
+  my $type = $self->node;
+  if ($type eq 'root' || $type eq 'tag') {
     return $self->_content(0, 1, @_) if @_;
     my $html = Mojo::DOM::HTML->new(xml => $self->xml);
     return join '', map { $html->tree($_)->render } _nodes($self->tree);
@@ -100,11 +82,11 @@ sub namespace {
 
   # Extract namespace prefix and search parents
   my $ns = $tree->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef;
-  for my $n ($tree, $self->_ancestors) {
+  for my $node ($tree, $self->_ancestors) {
 
     # Namespace for prefix
-    my $attrs = $n->[2];
-    if ($ns) { /^\Q$ns\E$/ and return $attrs->{$_} for keys %$attrs }
+    my $attrs = $node->[2];
+    if ($ns) { $_ eq $ns and return $attrs->{$_} for keys %$attrs }
 
     # Namespace attribute
     elsif (defined $attrs->{xmlns}) { return $attrs->{xmlns} }
@@ -184,27 +166,6 @@ sub type {
   return $self;
 }
 
-# DEPRECATED in Tiger Face!
-sub val {
-  deprecated 'Mojo::DOM::val is DEPRECATED';
-  my $self = shift;
-
-  # "option"
-  my $type = $self->type;
-  return Mojo::Collection->new($self->{value} // $self->text)
-    if $type eq 'option';
-
-  # "select"
-  return $self->find('option[selected]')->map('val')->flatten
-    if $type eq 'select';
-
-  # "textarea"
-  return Mojo::Collection->new($self->text) if $type eq 'textarea';
-
-  # "input" or "button"
-  return Mojo::Collection->new($self->{value} // ());
-}
-
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -288,12 +249,11 @@ sub _link {
   my ($children, $parent) = @_;
 
   # Link parent to children
-  my @new;
-  for my $n (@$children[1 .. $#$children]) {
-    push @new, $n;
-    my $offset = $n->[0] eq 'tag' ? 3 : 2;
-    $n->[$offset] = $parent;
-    weaken $n->[$offset];
+  my @new = @$children[1 .. $#$children];
+  for my $node (@new) {
+    my $offset = $node->[0] eq 'tag' ? 3 : 2;
+    $node->[$offset] = $parent;
+    weaken $node->[$offset];
   }
 
   return @new;
@@ -359,27 +319,27 @@ sub _text {
   }
 
   my $text = '';
-  for my $n (@$nodes) {
-    my $type = $n->[0];
-
-    # Nested tag
-    my $content = '';
-    if ($type eq 'tag' && $recurse) {
-      no warnings 'recursion';
-      $content = _text([_nodes($n)], 1, $n->[1] eq 'pre' ? 0 : $trim);
-    }
+  for my $node (@$nodes) {
+    my $type = $node->[0];
 
     # Text
-    elsif ($type eq 'text') { $content = $trim ? squish($n->[1]) : $n->[1] }
+    my $chunk = '';
+    if ($type eq 'text') { $chunk = $trim ? squish($node->[1]) : $node->[1] }
 
     # CDATA or raw text
-    elsif ($type eq 'cdata' || $type eq 'raw') { $content = $n->[1] }
+    elsif ($type eq 'cdata' || $type eq 'raw') { $chunk = $node->[1] }
+
+    # Nested tag
+    elsif ($type eq 'tag' && $recurse) {
+      no warnings 'recursion';
+      $chunk = _text([_nodes($node)], 1, $node->[1] eq 'pre' ? 0 : $trim);
+    }
 
     # Add leading whitespace if punctuation allows it
-    $content = " $content" if $text =~ /\S\z/ && $content =~ /^[^.!?,;:\s]+/;
+    $chunk = " $chunk" if $text =~ /\S\z/ && $chunk =~ /^[^.!?,;:\s]+/;
 
     # Trim whitespace blocks
-    $text .= $content if $content =~ /\S+/ || !$trim;
+    $text .= $chunk if $chunk =~ /\S+/ || !$trim;
   }
 
   return $text;
@@ -92,7 +92,7 @@ sub _connect {
     ->watch($handle, 0, 1);
 }
 
-sub _port { $_[0]->{socks_port} || $_[0]->{port} || ($_[0]->{tls} ? 443 : 80) }
+sub _port { $_[0]{socks_port} || $_[0]{port} || ($_[0]{tls} ? 443 : 80) }
 
 sub _ready {
   my ($self, $args) = @_;
@@ -236,6 +236,9 @@ Data shared between all L</"steps">.
   # Remove value
   my $foo = delete $delay->data->{foo};
 
+  # Assign multiple values at once
+  $delay->data(foo => 'test', bar => 23);
+
 =head2 pass
 
   $delay = $delay->pass;
@@ -86,8 +86,14 @@ sub delay {
 }
 
 sub is_running { _instance(shift)->reactor->is_running }
-sub next_tick  { _instance(shift)->reactor->next_tick(@_) }
-sub one_tick   { _instance(shift)->reactor->one_tick }
+
+sub next_tick {
+  my ($self, $cb) = (_instance(shift), @_);
+  weaken $self;
+  return $self->reactor->next_tick(sub { $self->$cb });
+}
+
+sub one_tick { _instance(shift)->reactor->one_tick }
 
 sub recurring { shift->_timer(recurring => @_) }
 
@@ -169,7 +175,7 @@ sub _accepting {
   # Check if multi-accept is desirable
   my $multi = $self->multi_accept;
   $_->multi_accept($max < $multi ? 1 : $multi)->start for values %$acceptors;
-  $self->{accepting}++;
+  $self->{accepting} = 1;
 }
 
 sub _id {
@@ -1,8 +1,6 @@
 package Mojo::JSON::Pointer;
 use Mojo::Base -base;
 
-use Mojo::Util 'deprecated';
-
 has 'data';
 
 sub contains { shift->_pointer(1, @_) }
@@ -12,17 +10,12 @@ sub new { @_ > 1 ? shift->SUPER::new(data => shift) : shift->SUPER::new }
 
 sub _pointer {
   my ($self, $contains, $pointer) = @_;
-  my $data = $self->data;
-
-  # DEPRECATED in Tiger Face!
-  deprecated 'Support for data arguments in Mojo::JSON::Pointer is DEPRECATED'
-    and (($pointer, $data) = ($_[3], $pointer))
-    if defined $_[3];
 
+  my $data = $self->data;
   return $data unless $pointer =~ s!^/!!;
   for my $p ($pointer eq '' ? ($pointer) : (split '/', $pointer)) {
-    $p =~ s/~0/~/g;
     $p =~ s!~1!/!g;
+    $p =~ s/~0/~/g;
 
     # Hash
     if (ref $data eq 'HASH' && exists $data->{$p}) { $data = $data->{$p} }
@@ -1,20 +1,16 @@
 package Mojo::JSON;
-use Mojo::Base -base;
+use Mojo::Base -strict;
 
 use B;
 use Carp 'croak';
 use Exporter 'import';
-use Mojo::Util 'deprecated';
+use Mojo::Util;
 use Scalar::Util 'blessed';
 
-# DEPRECATED in Tiger Face!
-has 'error';
-
 our @EXPORT_OK = qw(decode_json encode_json false from_json j to_json true);
 
-# Literal names
-my $FALSE = bless \(my $false = 0), 'Mojo::JSON::_Bool';
-my $TRUE  = bless \(my $true  = 1), 'Mojo::JSON::_Bool';
+# Booleans
+my ($FALSE, $TRUE) = map { bless \(my $dummy = $_), 'Mojo::JSON::_Bool' } 0, 1;
 
 # Escaped special character map (with u2028 and u2029)
 my %ESCAPE = (
@@ -32,20 +28,11 @@ my %ESCAPE = (
 my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
 for (0x00 .. 0x1f) { $REVERSE{pack 'C', $_} //= sprintf '\u%.4X', $_ }
 
-# DEPRECATED in Tiger Face!
-sub decode {
-  shift->error(my $err = _decode(\my $value, pop));
-  return defined $err ? undef : $value;
-}
-
 sub decode_json {
   my $err = _decode(\my $value, shift);
   return defined $err ? croak $err : $value;
 }
 
-# DEPRECATED in Tiger Face!
-sub encode { encode_json($_[1]) }
-
 sub encode_json { Mojo::Util::encode 'UTF-8', _encode_value(shift) }
 
 sub false () {$FALSE}
@@ -60,12 +47,6 @@ sub j {
   return eval { decode_json($_[0]) };
 }
 
-# DEPRECATED in Tiger Face!
-sub new {
-  deprecated 'Object-oriented Mojo::JSON API is DEPRECATED';
-  return shift->SUPER::new(@_);
-}
-
 sub to_json { _encode_value(shift) }
 
 sub true () {$TRUE}
@@ -20,7 +20,7 @@ has level => 'debug';
 has max_history_size => 10;
 has 'path';
 
-# Supported log level
+# Supported log levels
 my $LEVEL = {debug => 1, info => 2, warn => 3, error => 4, fatal => 5};
 
 sub append {
@@ -94,11 +94,11 @@ Mojo::Log - Simple logger
   my $log = Mojo::Log->new(path => '/var/log/mojo.log', level => 'warn');
 
   # Log messages
-  $log->debug('Why is this not working?');
-  $log->info('FYI: it happened again.');
-  $log->warn('This might be a problem.');
-  $log->error('Garden variety error.');
-  $log->fatal('Boom!');
+  $log->debug('Not sure what is happening here');
+  $log->info('FYI: it happened again');
+  $log->warn('This might be a problem');
+  $log->error('Garden variety error');
+  $log->fatal('Boom');
 
 =head1 DESCRIPTION
 
@@ -137,7 +137,7 @@ A callback for formatting log messages.
 
   $log->format(sub {
     my ($time, $level, @lines) = @_;
-    return "[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious.\n";
+    return "[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious\n";
   });
 
 =head2 handle
@@ -151,7 +151,7 @@ L</"path"> or C<STDERR>.
 =head2 history
 
   my $history = $log->history;
-  $log        = $log->history([[time, 'debug', 'That went wrong.']]);
+  $log        = $log->history([[time, 'debug', 'That went wrong']]);
 
 The last few logged messages.
 
@@ -186,35 +186,35 @@ the following new ones.
 
 =head2 append
 
-  $log->append("[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious.\n");
+  $log->append("[Thu May 15 17:47:04 2014] [info] I ♥ Mojolicious\n");
 
 Append message to L</"handle">.
 
 =head2 debug
 
-  $log = $log->debug('You screwed up, but that is ok.');
-  $log = $log->debug('All', 'cool!');
+  $log = $log->debug('You screwed up, but that is ok');
+  $log = $log->debug('All', 'cool');
 
 Log debug message.
 
 =head2 error
 
-  $log = $log->error('You really screwed up this time.');
-  $log = $log->error('Wow', 'seriously!');
+  $log = $log->error('You really screwed up this time');
+  $log = $log->error('Wow', 'seriously');
 
 Log error message.
 
 =head2 fatal
 
   $log = $log->fatal('Its over...');
-  $log = $log->fatal('Bye', 'bye!');
+  $log = $log->fatal('Bye', 'bye');
 
 Log fatal message.
 
 =head2 info
 
-  $log = $log->info('You are bad, but you prolly know already.');
-  $log = $log->info('Ok', 'then!');
+  $log = $log->info('You are bad, but you prolly know already');
+  $log = $log->info('Ok', 'then');
 
 Log info message.
 
@@ -256,8 +256,8 @@ Check for warn log level.
 
 =head2 log
 
-  $log = $log->log(debug => 'This should work.');
-  $log = $log->log(debug => 'This', 'too!');
+  $log = $log->log(debug => 'This should work');
+  $log = $log->log(debug => 'This', 'too');
 
 Emit L</"message"> event.
 
@@ -271,7 +271,7 @@ default logger.
 =head2 warn
 
   $log = $log->warn('Dont do that Dave...');
-  $log = $log->warn('No', 'really!');
+  $log = $log->warn('No', 'really');
 
 Log warn message.
 
@@ -60,7 +60,7 @@ sub extract_start_line {
   return undef unless $$bufref =~ s/^\s*(.*?)\x0d?\x0a//;
 
   # We have a (hopefully) full request-line
-  return !$self->error({message => 'Bad request start-line', advice => 400})
+  return !$self->error({message => 'Bad request start-line'})
     unless $1 =~ $START_LINE_RE;
   my $url = $self->method($1)->version($3)->url;
   return !!($1 eq 'CONNECT' ? $url->authority($2) : $url->parse($2));
@@ -156,7 +156,7 @@ sub parse {
     # Check start-line size
     my $len = index $self->{buffer}, "\x0a";
     $len = length $self->{buffer} if $len < 0;
-    return $self->_limit('Maximum start-line size exceeded', 431)
+    return $self->_limit('Maximum start-line size exceeded')
       if $len > $self->max_line_size;
 
     $self->{state} = 'content' if $self->extract_start_line(\$self->{buffer});
@@ -169,15 +169,15 @@ sub parse {
 
   # Check message size
   my $max = $self->max_message_size;
-  return $self->_limit('Maximum message size exceeded', 413)
+  return $self->_limit('Maximum message size exceeded')
     if $max && $max < $self->{raw_size};
 
   # Check header size
-  return $self->_limit('Maximum header size exceeded', 431)
+  return $self->_limit('Maximum header size exceeded')
     if $self->headers->is_limit_exceeded;
 
   # Check buffer size
-  return $self->_limit('Maximum buffer size exceeded', 400)
+  return $self->_limit('Maximum buffer size exceeded')
     if $self->content->is_limit_exceeded;
 
   return $self->emit('progress')->content->is_finished ? $self->finish : $self;
@@ -253,11 +253,7 @@ sub _cache {
   return $all ? $objects : $objects->[-1];
 }
 
-sub _limit {
-  my ($self, $msg, $code) = @_;
-  $self->{limit} = 1;
-  return $self->error({message => $msg, advice => $code});
-}
+sub _limit { ++$_[0]{limit} and return $_[0]->error({message => $_[1]}) }
 
 sub _parse_formdata {
   my ($self, $upload) = @_;
@@ -499,17 +495,14 @@ make sure it is not excessively large, there's a 10MB limit by default.
 =head2 error
 
   my $err = $msg->error;
-  $msg    = $msg->error({message => 'Parser error', advice => 500});
+  $msg    = $msg->error({message => 'Parser error'});
 
 Get or set message error, an C<undef> return value indicates that there is no
 error.
 
-  # Connection error
+  # Connection or parser error
   $msg->error({message => 'Connection refused'});
 
-  # Parser error
-  $msg->error({message => 'Maximum message size exceeded', advice => 413});
-
   # 4xx/5xx response
   $msg->error({message => 'Internal Server Error', code => 500});
 
@@ -38,7 +38,17 @@ sub clone {
   return $clone;
 }
 
-sub every_param { shift->_param(@_) }
+sub every_param {
+  my ($self, $name) = @_;
+
+  my @values;
+  my $params = $self->params;
+  for (my $i = 0; $i < @$params; $i += 2) {
+    push @values, $params->[$i + 1] if $params->[$i] eq $name;
+  }
+
+  return \@values;
+}
 
 sub merge {
   my $self = shift;
@@ -56,14 +66,14 @@ sub new { @_ > 1 ? shift->SUPER::new->parse(@_) : shift->SUPER::new }
 sub param {
   my ($self, $name) = (shift, shift);
 
-  # List names
-  return sort keys %{$self->to_hash} unless $name;
-
   # Multiple names
   return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
+  # List names
+  return sort keys %{$self->to_hash} unless defined $name;
+
   # Last value
-  return $self->_param($name)->[-1] unless @_;
+  return $self->every_param($name)->[-1] unless @_;
 
   # Replace values
   $self->remove($name);
@@ -177,18 +187,6 @@ sub to_string {
   return join '&', @pairs;
 }
 
-sub _param {
-  my ($self, $name) = @_;
-
-  my @values;
-  my $params = $self->params;
-  for (my $i = 0; $i < @$params; $i += 2) {
-    push @values, $params->[$i + 1] if $params->[$i] eq $name;
-  }
-
-  return \@values;
-}
-
 1;
 
 =encoding utf8
@@ -3,6 +3,7 @@ use Mojo::Base 'Mojo::Server';
 
 use Mojo::IOLoop;
 use Mojo::URL;
+use Mojo::Util 'term_escape';
 use Scalar::Util 'weaken';
 
 use constant DEBUG => $ENV{MOJO_DAEMON_DEBUG} || 0;
@@ -165,15 +166,15 @@ sub _listen {
           sub { $self && $self->app->log->error(pop) && $self->_close($id) });
       $stream->on(read => sub { $self->_read($id => pop) });
       $stream->on(timeout =>
-          sub { $self->app->log->debug('Inactivity timeout.') if $c->{tx} });
+          sub { $self->app->log->debug('Inactivity timeout') if $c->{tx} });
     }
   );
 
   return if $self->silent;
-  $self->app->log->info(qq{Listening at "$url".});
+  $self->app->log->info(qq{Listening at "$url"});
   $query->params([]);
   $url->host('127.0.0.1') if $url->host eq '*';
-  say "Server available at $url.";
+  say "Server available at $url";
 }
 
 sub _read {
@@ -182,7 +183,7 @@ sub _read {
   # Make sure we have a transaction and parse chunk
   return unless my $c = $self->{connections}{$id};
   my $tx = $c->{tx} ||= $self->_build_tx($id, $c);
-  warn "-- Server <<< Client (@{[$tx->req->url->to_abs]})\n$chunk\n" if DEBUG;
+  warn term_escape "-- Server <<< Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
   $tx->server_read($chunk);
 
   # Last keep-alive request or corrupted connection
@@ -200,6 +201,8 @@ sub _remove {
   $self->_close($id);
 }
 
+sub _url { shift->req->url->to_abs }
+
 sub _write {
   my ($self, $id) = @_;
 
@@ -209,7 +212,7 @@ sub _write {
   return if !$tx->is_writing || $c->{writing}++;
   my $chunk = $tx->server_write;
   delete $c->{writing};
-  warn "-- Server >>> Client (@{[$tx->req->url->to_abs]})\n$chunk\n" if DEBUG;
+  warn term_escape "-- Server >>> Client (@{[_url($tx)]})\n$chunk\n" if DEBUG;
   my $stream = $self->ioloop->stream($id)->write($chunk);
 
   # Finish or continue writing
@@ -57,7 +57,7 @@ sub run {
   $self->configure('hypnotoad');
   weaken $self;
   $prefork->on(wait   => sub { $self->_manage });
-  $prefork->on(reap   => sub { $self->_reap(pop) });
+  $prefork->on(reap   => sub { $self->_cleanup(pop) });
   $prefork->on(finish => sub { $self->{finished} = 1 });
 
   # Testing
@@ -78,6 +78,15 @@ sub run {
   $prefork->cleanup(1)->run;
 }
 
+sub _cleanup {
+  my ($self, $pid) = @_;
+
+  # Clean up failed upgrade
+  return unless ($self->{new} || '') eq $pid;
+  $self->prefork->app->log->error('Zero downtime software upgrade failed');
+  delete @$self{qw(new upgrade)};
+}
+
 sub _exit { say shift and exit 0 }
 
 sub _hot_deploy {
@@ -94,9 +103,11 @@ sub _manage {
   my $self = shift;
 
   # Upgraded
-  my $log = $self->prefork->app->log;
+  my $prefork = $self->prefork;
+  my $log     = $prefork->app->log;
   if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) {
-    $log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}.");
+    return unless $prefork->healthy == $prefork->workers;
+    $log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}");
     kill 'QUIT', $ENV{HYPNOTOAD_PID};
   }
   $ENV{HYPNOTOAD_PID} = $$ unless ($ENV{HYPNOTOAD_PID} // '') eq $$;
@@ -106,7 +117,7 @@ sub _manage {
 
     # Fresh start
     unless ($self->{new}) {
-      $log->info('Starting zero downtime software upgrade.');
+      $log->info('Starting zero downtime software upgrade');
       die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork);
       exec $^X, $ENV{HYPNOTOAD_EXE} or die "Can't exec: $!" unless $pid;
     }
@@ -117,15 +128,6 @@ sub _manage {
   }
 }
 
-sub _reap {
-  my ($self, $pid) = @_;
-
-  # Clean up failed upgrade
-  return unless ($self->{new} || '') eq $pid;
-  $self->prefork->app->log->error('Zero downtime software upgrade failed.');
-  delete @$self{qw(new upgrade)};
-}
-
 sub _stop {
   _exit('Hypnotoad server not running.')
     unless my $pid = shift->prefork->check_pid;
@@ -157,12 +159,12 @@ keep-alive, multiple event loop and hot deployment support that just works.
 Note that the server uses signals for process management, so you should avoid
 modifying signal handlers in your applications.
 
-To start applications with it you can use the L<hypnotoad> script, for
-L<Mojolicious> and L<Mojolicious::Lite> applications it will default to
-C<production> mode.
+To start applications with it you can use the L<hypnotoad> script, which
+listens on port C<8080>, automatically daemonizes the server process and
+defaults to C<production> mode for L<Mojolicious> and L<Mojolicious::Lite>
+applications.
 
   $ hypnotoad ./myapp.pl
-  Server available at http://127.0.0.1:8080.
 
 You can run the same command again for automatic hot deployment.
 
@@ -188,11 +190,11 @@ with the following signals.
 
 =head2 INT, TERM
 
-Shutdown server immediately.
+Shut down server immediately.
 
 =head2 QUIT
 
-Shutdown server gracefully.
+Shut down server gracefully.
 
 =head2 TTIN
 
@@ -23,7 +23,6 @@ sub check {
     elsif (-r $watch) { push @files, $watch }
   }
 
-  # Check files
   $self->_check($_) and return $_ for @files;
   return undef;
 }
@@ -32,10 +31,10 @@ sub run {
   my ($self, $app) = @_;
 
   # Clean manager environment
-  local $SIG{CHLD} = sub { $self->_reap };
+  local $SIG{CHLD} = sub { $self->_reap if $self->{worker} };
   local $SIG{INT} = local $SIG{TERM} = local $SIG{QUIT} = sub {
     $self->{finished} = 1;
-    kill 'TERM', $self->{running} if $self->{running};
+    kill 'TERM', $self->{worker} if $self->{worker};
   };
   unshift @{$self->watch}, $app;
   $self->{modified} = 1;
@@ -43,7 +42,7 @@ sub run {
   # Prepare and cache listen sockets for smooth restarting
   my $daemon = Mojo::Server::Daemon->new(silent => 1)->start->stop;
 
-  $self->_manage while !$self->{finished} || $self->{running};
+  $self->_manage while !$self->{finished} || $self->{worker};
   exit 0;
 }
 
@@ -64,28 +63,28 @@ sub _manage {
 
   if (defined(my $file = $self->check)) {
     say qq{File "$file" changed, restarting.} if $ENV{MORBO_VERBOSE};
-    kill 'TERM', $self->{running} if $self->{running};
+    kill 'TERM', $self->{worker} if $self->{worker};
     $self->{modified} = 1;
   }
 
+  # Windows workaround
+  delete $self->{worker} if $self->{worker} && !kill 0, $self->{worker};
+
   $self->_reap;
-  delete $self->{running} if $self->{running} && !kill 0, $self->{running};
-  $self->_spawn if !$self->{running} && delete $self->{modified};
+  $self->_spawn if !$self->{worker} && delete $self->{modified};
   sleep 1;
 }
 
-sub _reap { delete $_[0]{running} while (waitpid -1, WNOHANG) > 0 }
+sub _reap { delete $_[0]{worker} while (waitpid -1, WNOHANG) > 0 }
 
 sub _spawn {
   my $self = shift;
 
-  # Fork
+  # Manager
   my $manager = $$;
   $ENV{MORBO_REV}++;
-  die "Can't fork: $!" unless defined(my $pid = fork);
-
-  # Manager
-  return $self->{running} = $pid if $pid;
+  die "Can't fork: $!" unless defined(my $pid = $self->{worker} = fork);
+  return if $pid;
 
   # Worker
   $SIG{CHLD} = 'DEFAULT';
@@ -128,7 +127,7 @@ applications.
 To start applications with it you can use the L<morbo> script.
 
   $ morbo ./myapp.pl
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 For better scalability (epoll, kqueue) and to provide non-blocking name
 resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
@@ -51,13 +51,17 @@ sub ensure_pid_file {
   return if -e (my $file = $self->pid_file);
 
   # Create PID file
-  $self->app->log->info(qq{Creating process id file "$file".});
+  $self->app->log->info(qq{Creating process id file "$file"});
   die qq{Can't create process id file "$file": $!}
     unless open my $handle, '>', $file;
   chmod 0644, $handle;
   print $handle $$;
 }
 
+sub healthy {
+  scalar grep { $_->{healthy} } values %{shift->{pool}};
+}
+
 sub run {
   my $self = shift;
 
@@ -78,8 +82,7 @@ sub run {
   local $SIG{INT} = local $SIG{TERM} = sub { $self->_term };
   local $SIG{CHLD} = sub {
     while ((my $pid = waitpid -1, WNOHANG) > 0) {
-      $self->app->log->debug("Worker $pid stopped.")
-        if delete $self->emit(reap => $pid)->{pool}{$pid};
+      $self->emit(reap => $pid)->_stopped($pid);
     }
   };
   local $SIG{QUIT} = sub { $self->_term(1) };
@@ -91,33 +94,17 @@ sub run {
   };
 
   # Preload application before starting workers
-  $self->start->app->log->info("Manager $$ started.");
+  $self->start->app->log->info("Manager $$ started");
   $self->{running} = 1;
   $self->_manage while $self->{running};
 }
 
-sub _heartbeat {
-  my $self = shift;
-
-  # Poll for heartbeats
-  my $poll = $self->{poll};
-  $poll->poll(1);
-  return unless $poll->handles(POLLIN | POLLPRI);
-  return unless $self->{reader}->sysread(my $chunk, 4194304);
-
-  # Update heartbeats (and stop gracefully if necessary)
-  my $time = steady_time;
-  while ($chunk =~ /(\d+):(\d)\n/g) {
-    next unless my $w = $self->{pool}{$1};
-    $self->emit(heartbeat => $1) and $w->{time} = $time;
-    $w->{graceful} ||= $time if $2;
-  }
-}
+sub _heartbeat { shift->{writer}->syswrite("$$:$_[0]\n") or exit 0 }
 
 sub _manage {
   my $self = shift;
 
-  # Spawn more workers and check PID file
+  # Spawn more workers if necessary and check PID file
   if (!$self->{finished}) {
     $self->_spawn while keys %{$self->{pool}} < $self->workers;
     $self->ensure_pid_file;
@@ -127,31 +114,32 @@ sub _manage {
   elsif (!keys %{$self->{pool}}) { return delete $self->{running} }
 
   # Wait for heartbeats
-  $self->emit('wait')->_heartbeat;
+  $self->_wait;
 
   my $interval = $self->heartbeat_interval;
   my $ht       = $self->heartbeat_timeout;
   my $gt       = $self->graceful_timeout;
-  my $time     = steady_time;
   my $log      = $self->app->log;
+  my $time     = steady_time;
 
   for my $pid (keys %{$self->{pool}}) {
     next unless my $w = $self->{pool}{$pid};
 
     # No heartbeat (graceful stop)
-    $log->error("Worker $pid has no heartbeat, restarting.")
+    $log->error("Worker $pid has no heartbeat, restarting")
       and $w->{graceful} = $time
       if !$w->{graceful} && ($w->{time} + $interval + $ht <= $time);
 
     # Graceful stop with timeout
     my $graceful = $w->{graceful} ||= $self->{graceful} ? $time : undef;
-    $log->debug("Trying to stop worker $pid gracefully.")
-      and kill 'QUIT', $pid
+    $log->debug("Stopping worker $pid gracefully")
+      and (kill 'QUIT', $pid or $self->_stopped($pid))
       if $graceful && !$w->{quit}++;
     $w->{force} = 1 if $graceful && $graceful + $gt <= $time;
 
     # Normal stop
-    $log->debug("Stopping worker $pid.") and kill 'KILL', $pid
+    $log->debug("Stopping worker $pid")
+      and (kill 'KILL', $pid or $self->_stopped($pid))
       if $w->{force} || ($self->{finished} && !$graceful);
   }
 }
@@ -165,60 +153,82 @@ sub _spawn {
     if $pid;
 
   # Prepare lock file
-  my $file = $self->{lock_file};
+  my $file = $self->cleanup(0)->{lock_file};
   $self->app->log->error(qq{Can't open lock file "$file": $!})
     unless open my $handle, '>', $file;
 
   # Change user/group
-  $self->setuidgid->cleanup(0);
+  $self->setuidgid;
 
   # Accept mutex
+  weaken $self;
   my $loop = $self->ioloop->lock(
     sub {
 
-      # Blocking ("ualarm" can't be imported on Windows)
-      my $lock;
-      if ($_[0]) {
-        eval {
-          local $SIG{ALRM} = sub { die "alarm\n" };
-          my $old = Time::HiRes::ualarm $self->lock_timeout * 1000000;
-          $lock = flock $handle, LOCK_EX;
-          Time::HiRes::ualarm $old;
-          1;
-        } or $lock = $@ eq "alarm\n" ? 0 : die $@;
-      }
-
       # Non-blocking
-      else { $lock = flock $handle, LOCK_EX | LOCK_NB }
+      return flock $handle, LOCK_EX | LOCK_NB unless shift;
 
+      # Blocking ("ualarm" can't be imported on Windows)
+      my $lock;
+      eval {
+        local $SIG{ALRM} = sub { die "alarm\n" };
+        my $old = Time::HiRes::ualarm $self->lock_timeout * 1000000;
+        $lock = flock $handle, LOCK_EX;
+        Time::HiRes::ualarm $old;
+        1;
+      } or $lock = $@ eq "alarm\n" ? 0 : die $@;
       return $lock;
     }
   );
   $loop->unlock(sub { flock $handle, LOCK_UN });
 
   # Heartbeat messages
-  weaken $self;
-  $loop->recurring(
-    $self->heartbeat_interval => sub {
-      my $graceful = shift->max_connections ? 0 : 1;
-      $self->{writer}->syswrite("$$:$graceful\n") or exit 0;
-    }
-  );
+  my $cb = sub { $self->_heartbeat(shift->max_connections ? 0 : 1) };
+  $loop->next_tick($cb);
+  $loop->recurring($self->heartbeat_interval => $cb);
 
   # Clean worker environment
   $SIG{$_} = 'DEFAULT' for qw(INT TERM CHLD TTIN TTOU);
   $SIG{QUIT} = sub { $loop->max_connections(0) };
   delete @$self{qw(poll reader)};
 
-  $self->app->log->debug("Worker $$ started.");
+  $self->app->log->debug("Worker $$ started");
   $loop->start;
   exit 0;
 }
 
+sub _stopped {
+  my ($self, $pid) = @_;
+
+  return unless my $w = delete $self->{pool}{$pid};
+
+  my $log = $self->app->log;
+  $log->debug("Worker $pid stopped");
+  $log->error("Worker $pid stopped too early, shutting down") and $self->_term
+    unless $w->{healthy};
+}
+
 sub _term {
   my ($self, $graceful) = @_;
-  $self->emit(finish => $graceful)->{finished} = 1;
-  $self->{graceful} = 1 if $graceful;
+  @{$self->emit(finish => $graceful)}{qw(finished graceful)} = (1, $graceful);
+}
+
+sub _wait {
+  my $self = shift;
+
+  # Poll for heartbeats
+  my $poll = $self->emit('wait')->{poll};
+  $poll->poll(1);
+  return unless $poll->handles(POLLIN | POLLPRI);
+  return unless $self->{reader}->sysread(my $chunk, 4194304);
+
+  # Update heartbeats (and stop gracefully if necessary)
+  my $time = steady_time;
+  while ($chunk =~ /(\d+):(\d)\n/g) {
+    next unless my $w = $self->{pool}{$1};
+    @$w{qw(healthy time)} = (1, $time) and $self->emit(heartbeat => $1);
+    $w->{graceful} ||= $time if $2;
+  }
 }
 
 1;
@@ -277,11 +287,11 @@ the following signals.
 
 =head2 INT, TERM
 
-Shutdown server immediately.
+Shut down server immediately.
 
 =head2 QUIT
 
-Shutdown server gracefully.
+Shut down server gracefully.
 
 =head2 TTIN
 
@@ -499,6 +509,12 @@ is not running.
 
 Ensure L</"pid_file"> exists.
 
+=head2 healthy
+
+  my $healthy = $prefork->healthy;
+
+Number of currently active worker processes with a heartbeat.
+
 =head2 run
 
   $prefork->run;
@@ -76,23 +76,23 @@ sub setuidgid {
 
   # Group (make sure secondary groups are reassigned too)
   if (my $group = $self->group) {
-    return $self->_log(qq{Group "$group" does not exist.})
+    $self->_error(qq{Group "$group" does not exist})
       unless defined(my $gid = getgrnam $group);
-    return $self->_log(qq{Can't switch to group "$group": $!})
+    $self->_error(qq{Can't switch to group "$group": $!})
       unless ($( = $) = "$gid $gid") && $) eq "$gid $gid" && $( eq "$gid $gid";
   }
 
   # User
   return $self unless my $user = $self->user;
-  return $self->_log(qq{User "$user" does not exist.})
+  $self->_error(qq{User "$user" does not exist})
     unless defined(my $uid = getpwnam $user);
-  return $self->_log(qq{Can't switch to user "$user": $!})
+  $self->_error(qq{Can't switch to user "$user": $!})
     unless POSIX::setuid($uid);
 
   return $self;
 }
 
-sub _log { $_[0]->app->log->error($_[1]) and return $_[0] }
+sub _error { $_[0]->app->log->error($_[1]) and croak $_[1] }
 
 1;
 
@@ -306,12 +306,12 @@ Mojo::Template - Perl-ish templates!
   say $output;
 
   # More advanced
-  my $output = $mt->render(<<'EOF', 23, 'foo bar');
-  % my ($num, $text) = @_;
+  my $output = $mt->render(<<'EOF', 23, 'More advanced');
+  % my ($num, $title) = @_;
   %= 5 * 5
   <!DOCTYPE html>
   <html>
-    <head><title>More advanced</title></head>
+    <head><title><%= $title %></title></head>
     <body>
       test 123
       foo <% my $i = $num + 2; %>
@@ -5,7 +5,7 @@ use Compress::Raw::Zlib 'Z_SYNC_FLUSH';
 use Config;
 use Mojo::JSON qw(encode_json j);
 use Mojo::Transaction::HTTP;
-use Mojo::Util qw(b64_encode decode encode sha1_bytes xor_encode);
+use Mojo::Util qw(b64_encode decode dumper encode sha1_bytes xor_encode);
 
 use constant DEBUG => $ENV{MOJO_WEBSOCKET_DEBUG} || 0;
 
@@ -45,19 +45,19 @@ sub build_frame {
   my $len    = length $payload;
   my $masked = $self->masked;
   if ($len < 126) {
-    warn "-- Small payload ($len)\n$payload\n" if DEBUG;
+    warn "-- Small payload ($len)\n@{[dumper $payload]}" if DEBUG;
     $frame .= pack 'C', $masked ? ($len | 128) : $len;
   }
 
   # Extended payload (16-bit)
   elsif ($len < 65536) {
-    warn "-- Extended 16-bit payload ($len)\n$payload\n" if DEBUG;
+    warn "-- Extended 16-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
     $frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
   }
 
   # Extended payload (64-bit with 32-bit fallback)
   else {
-    warn "-- Extended 64-bit payload ($len)\n$payload\n" if DEBUG;
+    warn "-- Extended 64-bit payload ($len)\n@{[dumper $payload]}" if DEBUG;
     $frame .= pack 'C', $masked ? (127 | 128) : 127;
     $frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
   }
@@ -202,7 +202,7 @@ sub parse_frame {
   # Payload
   my $payload = $len ? substr($$buffer, 0, $len, '') : '';
   $payload = xor_encode($payload, substr($payload, 0, 4, '') x 128) if $masked;
-  warn "$payload\n" if DEBUG;
+  warn dumper $payload if DEBUG;
 
   return [$fin, $rsv1, $rsv2, $rsv3, $op, $payload];
 }
@@ -214,8 +214,11 @@ Connection identifier or socket.
 
   my $err = $tx->error;
 
-Return transaction error or C<undef> if there is no error, commonly used
-together with L</"success">.
+Get request or response error and return C<undef> if there is no error,
+commonly used together with L</"success">.
+
+  # Longer version
+  my $err = $tx->req->error || $tx->res->error;
 
   # Check for different kinds of errors
   if (my $err = $tx->error) {
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojo::EventEmitter';
 # "Fry: Since when is the Internet about robbing people of their privacy?
 #  Bender: August 6, 1991."
 use Mojo::IOLoop;
-use Mojo::Util 'monkey_patch';
+use Mojo::Util qw(monkey_patch term_escape);
 use Mojo::UserAgent::CookieJar;
 use Mojo::UserAgent::Proxy;
 use Mojo::UserAgent::Server;
@@ -50,12 +50,12 @@ sub start {
 
   # Non-blocking
   if ($cb) {
-    warn "-- Non-blocking request (@{[$tx->req->url->to_abs]})\n" if DEBUG;
+    warn "-- Non-blocking request (@{[_url($tx)]})\n" if DEBUG;
     return $self->_start(1, $tx, $cb);
   }
 
   # Blocking
-  warn "-- Blocking request (@{[$tx->req->url->to_abs]})\n" if DEBUG;
+  warn "-- Blocking request (@{[_url($tx)]})\n" if DEBUG;
   $self->_start(0, $tx => sub { shift->ioloop->stop; $tx = shift });
   $self->ioloop->start;
 
@@ -187,7 +187,7 @@ sub _connection {
   my ($proto, $host, $port) = $self->transactor->endpoint($tx);
   $id ||= $self->_dequeue($nb, "$proto:$host:$port", 1);
   if ($id && !ref $id) {
-    warn "-- Reusing connection ($proto:$host:$port)\n" if DEBUG;
+    warn "-- Reusing connection ($proto://$host:$port)\n" if DEBUG;
     $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $tx};
     $tx->kept_alive(1) unless $tx->connection;
     $self->_connected($id);
@@ -198,7 +198,7 @@ sub _connection {
   if (my $id = $self->_connect_proxy($nb, $tx, $cb)) { return $id }
 
   # Connect
-  warn "-- Connect ($proto:$host:$port)\n" if DEBUG;
+  warn "-- Connect ($proto://$host:$port)\n" if DEBUG;
   $id = $self->_connect($nb, 1, $tx, $id, \&_connected);
   $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $tx};
 
@@ -279,7 +279,7 @@ sub _read {
   return $self->_remove($id) unless my $tx = $c->{tx};
 
   # Process incoming data
-  warn "-- Client <<< Server (@{[$tx->req->url->to_abs]})\n$chunk\n" if DEBUG;
+  warn term_escape "-- Client <<< Server (@{[_url($tx)]})\n$chunk\n" if DEBUG;
   $tx->client_read($chunk);
   if    ($tx->is_finished) { $self->_finish($id) }
   elsif ($tx->is_writing)  { $self->_write($id) }
@@ -329,6 +329,8 @@ sub _start {
   return $id;
 }
 
+sub _url { shift->req->url->to_abs }
+
 sub _write {
   my ($self, $id) = @_;
 
@@ -338,7 +340,7 @@ sub _write {
   return if !$tx->is_writing || $c->{writing}++;
   my $chunk = $tx->client_write;
   delete $c->{writing};
-  warn "-- Client >>> Server (@{[$tx->req->url->to_abs]})\n$chunk\n" if DEBUG;
+  warn term_escape "-- Client >>> Server (@{[_url($tx)]})\n$chunk\n" if DEBUG;
   my $stream = $self->_loop($c->{nb})->stream($id)->write($chunk);
   $self->_finish($id) if $tx->is_finished;
 
@@ -56,8 +56,8 @@ our @EXPORT_OK = (
   qw(decode deprecated dumper encode hmac_sha1_sum html_unescape md5_bytes),
   qw(md5_sum monkey_patch punycode_decode punycode_encode quote),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
-  qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode xss_escape)
+  qw(steady_time tablify term_escape trim unindent unquote url_escape),
+  qw(url_unescape xml_escape xor_encode xss_escape)
 );
 
 sub b64_decode { decode_base64 $_[0] }
@@ -76,7 +76,7 @@ sub camelize {
 sub class_to_file {
   my $class = shift;
   $class =~ s/::|'//g;
-  $class =~ s/([A-Z])([A-Z]*)/$1.lc($2)/ge;
+  $class =~ s/([A-Z])([A-Z]*)/$1 . lc $2/ge;
   return decamelize($class);
 }
 
@@ -299,6 +299,12 @@ sub tablify {
   return join '', map { sprintf "$format\n", @$_ } @$rows;
 }
 
+sub term_escape {
+  my $str = shift;
+  $str =~ s/([\x00-\x09\x0b-\x1f\x7f\x80-\x9f])/sprintf '\\x%02x', ord $1/ge;
+  return $str;
+}
+
 sub trim {
   my $str = shift;
   $str =~ s/^\s+//;
@@ -323,14 +329,14 @@ sub unquote {
 
 sub url_escape {
   my ($str, $pattern) = @_;
-  if ($pattern) { $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge }
-  else          { $str =~ s/([^A-Za-z0-9\-._~])/sprintf('%%%02X',ord($1))/ge }
+  if   ($pattern) { $str =~ s/([$pattern])/sprintf '%%%02X', ord $1/ge }
+  else            { $str =~ s/([^A-Za-z0-9\-._~])/sprintf '%%%02X', ord $1/ge }
   return $str;
 }
 
 sub url_unescape {
   my $str = shift;
-  $str =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;
+  $str =~ s/%([0-9a-fA-F]{2})/chr hex $1/ge;
   return $str;
 }
 
@@ -558,12 +564,18 @@ Encode characters to bytes.
 
 Generate HMAC-SHA1 checksum for bytes.
 
+  # "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
+  hmac_sha1_sum 'foo', 'passw0rd';
+
 =head2 html_unescape
 
   my $str = html_unescape $escaped;
 
 Unescape all HTML entities in string.
 
+  # "<div>"
+  html_unescape '&lt;div&gt;';
+
 =head2 md5_bytes
 
   my $checksum = md5_bytes $bytes;
@@ -576,6 +588,9 @@ Generate binary MD5 checksum for bytes.
 
 Generate MD5 checksum for bytes.
 
+  # "acbd18db4cc2f85cedef654fccc4a4d8"
+  md5_sum 'foo';
+
 =head2 monkey_patch
 
   monkey_patch $package, foo => sub {...};
@@ -595,6 +610,9 @@ Monkey patch functions into package.
 Punycode decode string as described in
 L<RFC 3492|http://tools.ietf.org/html/rfc3492>.
 
+  # "bücher"
+  punycode_decode 'bcher-kva';
+
 =head2 punycode_encode
 
   my $punycode = punycode_encode $str;
@@ -602,6 +620,9 @@ L<RFC 3492|http://tools.ietf.org/html/rfc3492>.
 Punycode encode string as described in
 L<RFC 3492|http://tools.ietf.org/html/rfc3492>.
 
+  # "bcher-kva"
+  punycode_encode 'bücher';
+
 =head2 quote
 
   my $quoted = quote $str;
@@ -626,6 +647,9 @@ Generate binary SHA1 checksum for bytes.
 
 Generate SHA1 checksum for bytes.
 
+  # "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+  sha1_sum 'foo';
+
 =head2 slurp
 
   my $bytes = slurp '/etc/passwd';
@@ -660,6 +684,9 @@ Write all data at once to file.
 Trim whitespace characters from both ends of string and then change all
 consecutive groups of whitespace into one space each.
 
+  # "foo bar"
+  squish '  foo  bar  ';
+
 =head2 steady_time
 
   my $time = steady_time;
@@ -677,18 +704,33 @@ Row-oriented generator for text tables.
   # "foo   bar\nyada  yada\nbaz   yada\n"
   tablify [['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']];
 
+=head2 term_escape
+
+  my $escaped = term_escape $str;
+
+Escape all POSIX control characters except for C<\n>.
+
+  # "foo\\x09bar\\x0d\n"
+  term_escape "foo\tbar\r\n";
+
 =head2 trim
 
   my $trimmed = trim $str;
 
 Trim whitespace characters from both ends of string.
 
+  # "foo bar"
+  trim '  foo bar  ';
+
 =head2 unindent
 
   my $unindented = unindent $str;
 
 Unindent multiline string.
 
+  # "foo\nbar\nbaz\n"
+  unindent "  foo\n  bar\n  baz\n";
+
 =head2 unquote
 
   my $str = unquote $quoted;
@@ -704,6 +746,9 @@ Percent encode unsafe characters in string as described in
 L<RFC 3986|http://tools.ietf.org/html/rfc3986>, the pattern used defaults to
 C<^A-Za-z0-9\-._~>.
 
+  # "foo%3Bbar"
+  url_unescape 'foo;bar';
+
 =head2 url_unescape
 
   my $str = url_unescape $escaped;
@@ -711,12 +756,18 @@ C<^A-Za-z0-9\-._~>.
 Decode percent encoded characters in string as described in
 L<RFC 3986|http://tools.ietf.org/html/rfc3986>.
 
+  # "foo;bar"
+  url_unescape 'foo%3Bbar';
+
 =head2 xml_escape
 
   my $escaped = xml_escape $str;
 
 Escape unsafe characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in string.
 
+  # "&lt;div&gt;"
+  xml_escape '<div>';
+
 =head2 xor_encode
 
   my $encoded = xor_encode $str, $key;
@@ -87,7 +87,7 @@ which stringifies to the actual path.
 The logging layer of your application, defaults to a L<Mojo::Log> object.
 
   # Log debug message
-  $app->log->debug('It works!');
+  $app->log->debug('It works');
 
 =head2 ua
 
@@ -124,6 +124,9 @@ Application configuration.
   # Remove value
   my $foo = delete $app->config->{foo};
 
+  # Assign multiple values at once
+  $app->config(foo => 'test', bar => 23);
+
 =head2 handler
 
   $app->handler(Mojo::Transaction::HTTP->new);
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 use Mojo::Server::CGI;
 
-has description => 'Start application with CGI.';
+has description => 'Start application with CGI';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -26,7 +26,7 @@ Mojolicious::Command::cgi - CGI command
   Usage: APPLICATION cgi [OPTIONS]
 
   Options:
-    --nph   Enable non-parsed-header mode.
+    --nph   Enable non-parsed-header mode
 
 =head1 DESCRIPTION
 
@@ -47,14 +47,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $cgi->description;
-  $cgi            = $cgi->description('Foo!');
+  $cgi            = $cgi->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $cgi->usage;
-  $cgi      = $cgi->usage('Foo!');
+  $cgi      = $cgi->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -5,7 +5,7 @@ use File::Basename 'basename';
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 use Mojo::UserAgent;
 
-has description => 'Upload distribution to CPAN.';
+has description => 'Upload distribution to CPAN';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -53,8 +53,8 @@ Mojolicious::Command::cpanify - Cpanify command
     mojo cpanify -u sri -p secr3t Mojolicious-Plugin-MyPlugin-0.01.tar.gz
 
   Options:
-    -p, --password <password>   PAUSE password.
-    -u, --user <name>           PAUSE username.
+    -p, --password <password>   PAUSE password
+    -u, --user <name>           PAUSE username
 
 =head1 DESCRIPTION
 
@@ -74,14 +74,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $cpanify->description;
-  $cpanify        = $cpanify->description('Foo!');
+  $cpanify        = $cpanify->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $cpanify->usage;
-  $cpanify  = $cpanify->usage('Foo!');
+  $cpanify  = $cpanify->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 use Mojo::Server::Daemon;
 
-has description => 'Start application with HTTP and WebSocket server.';
+has description => 'Start application with HTTP and WebSocket server';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -43,20 +43,20 @@ Mojolicious::Command::daemon - Daemon command
     ./myapp.pl daemon -l 'https://*:443?cert=./server.crt&key=./server.key'
 
   Options:
-    -b, --backlog <size>         Listen backlog size, defaults to SOMAXCONN.
+    -b, --backlog <size>         Listen backlog size, defaults to SOMAXCONN
     -c, --clients <number>       Maximum number of concurrent clients,
-                                 defaults to 1000.
-    -g, --group <name>           Group name for process.
+                                 defaults to 1000
+    -g, --group <name>           Group name for process
     -i, --inactivity <seconds>   Inactivity timeout, defaults to the value of
-                                 MOJO_INACTIVITY_TIMEOUT or 15.
+                                 MOJO_INACTIVITY_TIMEOUT or 15
     -l, --listen <location>      One or more locations you want to listen on,
                                  defaults to the value of MOJO_LISTEN or
-                                 "http://*:3000".
+                                 "http://*:3000"
     -p, --proxy                  Activate reverse proxy support, defaults to
-                                 the value of MOJO_REVERSE_PROXY.
+                                 the value of MOJO_REVERSE_PROXY
     -r, --requests <number>      Maximum number of requests per keep-alive
-                                 connection, defaults to 25.
-    -u, --user <name>            Username for process.
+                                 connection, defaults to 25
+    -u, --user <name>            Username for process
 
 =head1 DESCRIPTION
 
@@ -77,14 +77,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $daemon->description;
-  $daemon         = $daemon->description('Foo!');
+  $daemon         = $daemon->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $daemon->usage;
-  $daemon   = $daemon->usage('Foo!');
+  $daemon   = $daemon->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';
 
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 
-has description => 'Run code against application.';
+has description => 'Run code against application';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -37,8 +37,8 @@ Mojolicious::Command::eval - Eval command
     ./myapp.pl eval -V 'app->renderer->paths'
 
   Options:
-    -v, --verbose   Print return value to STDOUT.
-    -V              Print returned data structure to STDOUT.
+    -v, --verbose   Print return value to STDOUT
+    -V              Print returned data structure to STDOUT
 
 =head1 DESCRIPTION
 
@@ -58,14 +58,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $eval->description;
-  $eval           = $eval->description('Foo!');
+  $eval           = $eval->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $eval->usage;
-  $eval     = $eval->usage('Foo!');
+  $eval     = $eval->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';
 
 use Mojo::Util qw(class_to_file class_to_path);
 
-has description => 'Generate Mojolicious application directory structure.';
+has description => 'Generate Mojolicious application directory structure';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -77,14 +77,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $app->description;
-  $app            = $app->description('Foo!');
+  $app            = $app->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $app->usage;
-  $app      = $app->usage('Foo!');
+  $app      = $app->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -1,7 +1,7 @@
 package Mojolicious::Command::generate::lite_app;
 use Mojo::Base 'Mojolicious::Command';
 
-has description => 'Generate Mojolicious::Lite application.';
+has description => 'Generate Mojolicious::Lite application';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -42,14 +42,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $app->description;
-  $app            = $app->description('Foo!');
+  $app            = $app->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $app->usage;
-  $app      = $app->usage('Foo!');
+  $app      = $app->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -81,7 +81,7 @@ plugin 'PODRenderer';
 
 get '/' => sub {
   my $c = shift;
-  $c->render('index');
+  $c->render(template => 'index');
 };
 
 app->start;
@@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';
 
 use Mojolicious;
 
-has description => 'Generate "Makefile.PL".';
+has description => 'Generate "Makefile.PL"';
 has usage => sub { shift->extract_usage };
 
 sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') }
@@ -39,14 +39,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $makefile->description;
-  $makefile       = $makefile->description('Foo!');
+  $makefile       = $makefile->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $makefile->usage;
-  $makefile = $makefile->usage('Foo!');
+  $makefile = $makefile->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
 use Mojo::Util qw(camelize class_to_path);
 use Mojolicious;
 
-has description => 'Generate Mojolicious plugin directory structure.';
+has description => 'Generate Mojolicious plugin directory structure';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -56,14 +56,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $plugin->description;
-  $plugin         = $plugin->description('Foo!');
+  $plugin         = $plugin->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $plugin->usage;
-  $plugin   = $plugin->usage('Foo!');
+  $plugin   = $plugin->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -1,7 +1,7 @@
 package Mojolicious::Command::generate;
 use Mojo::Base 'Mojolicious::Commands';
 
-has description => 'Generate files and directories from templates.';
+has description => 'Generate files and directories from templates';
 has hint        => <<EOF;
 
 See 'APPLICATION generate help GENERATOR' for more information on a specific
@@ -42,21 +42,21 @@ L<Mojolicious::Commands> and implements the following new ones.
 =head2 description
 
   my $description = $generator->description;
-  $generator      = $generator->description('Foo!');
+  $generator      = $generator->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 hint
 
   my $hint   = $generator->hint;
-  $generator = $generator->hint('Foo!');
+  $generator = $generator->hint('Foo');
 
 Short hint shown after listing available generator commands.
 
 =head2 message
 
   my $msg    = $generator->message;
-  $generator = $generator->message('Bar!');
+  $generator = $generator->message('Bar');
 
 Short usage message shown before listing available generator commands.
 
@@ -6,11 +6,10 @@ use Mojo::DOM;
 use Mojo::IOLoop;
 use Mojo::JSON qw(encode_json j);
 use Mojo::JSON::Pointer;
-use Mojo::UserAgent;
 use Mojo::Util qw(decode encode);
 use Scalar::Util 'weaken';
 
-has description => 'Perform HTTP request.';
+has description => 'Perform HTTP request';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -32,8 +31,9 @@ sub run {
   my %headers = map { /^\s*([^:]+)\s*:\s*(.+)$/ ? ($1, $2) : () } @headers;
 
   # Detect proxy for absolute URLs
-  my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
-  $url !~ m!^/! ? $ua->proxy->detect : $ua->server->app($self->app);
+  my $ua = $self->app->ua->ioloop(Mojo::IOLoop->singleton);
+  $ua->server->ioloop(Mojo::IOLoop->singleton);
+  $ua->proxy->detect unless $url =~ m!^/!;
   $ua->max_redirects(10) if $redirect;
 
   my $buffer = '';
@@ -143,12 +143,12 @@ Mojolicious::Command::get - Get command
 
   Options:
     -C, --charset <charset>     Charset of HTML/XML content, defaults to auto
-                                detection.
-    -c, --content <content>     Content to send with request.
-    -H, --header <name:value>   Additional HTTP header.
-    -M, --method <method>       HTTP method to use, defaults to "GET".
-    -r, --redirect              Follow up to 10 redirects.
-    -v, --verbose               Print request and response headers to STDERR.
+                                detection
+    -c, --content <content>     Content to send with request
+    -H, --header <name:value>   Additional HTTP header
+    -M, --method <method>       HTTP method to use, defaults to "GET"
+    -r, --redirect              Follow up to 10 redirects
+    -v, --verbose               Print request and response headers to STDERR
 
 =head1 DESCRIPTION
 
@@ -169,14 +169,14 @@ applications.
 =head2 description
 
   my $description = $get->description;
-  $get            = $get->description('Foo!');
+  $get            = $get->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $get->usage;
-  $get      = $get->usage('Foo!');
+  $get      = $get->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious::Command';
 use Mojo::Loader;
 use Mojo::Util 'encode';
 
-has description => 'Inflate embedded files to real files.';
+has description => 'Inflate embedded files to real files';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -60,14 +60,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $inflate->description;
-  $inflate        = $inflate->description('Foo!');
+  $inflate        = $inflate->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $inflate->usage;
-  $inflate  = $inflate->usage('Foo!');
+  $inflate  = $inflate->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -5,7 +5,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 use Mojo::Server::Prefork;
 
 has description =>
-  'Start application with preforking HTTP and WebSocket server.';
+  'Start application with preforking HTTP and WebSocket server';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -55,37 +55,37 @@ Mojolicious::Command::prefork - Prefork command
 
   Options:
     -A, --accepts <number>               Number of connections for workers to
-                                         accept, defaults to 1000.
-    -a, --accept-interval <seconds>      Accept interval, defaults to 0.025.
+                                         accept, defaults to 1000
+    -a, --accept-interval <seconds>      Accept interval, defaults to 0.025
     -b, --backlog <size>                 Listen backlog size, defaults to
-                                         SOMAXCONN.
+                                         SOMAXCONN
     -c, --clients <number>               Maximum number of concurrent clients,
-                                         defaults to 1000.
+                                         defaults to 1000
     -G, --graceful-timeout <seconds>     Graceful timeout, defaults to 20.
-    -g, --group <name>                   Group name for process.
-        --heartbeat-interval <seconds>   Heartbeat interval, defaults to 5.
-    -H, --heartbeat-timeout <seconds>    Heartbeat timeout, defaults to 20.
+    -g, --group <name>                   Group name for process
+        --heartbeat-interval <seconds>   Heartbeat interval, defaults to 5
+    -H, --heartbeat-timeout <seconds>    Heartbeat timeout, defaults to 20
     -i, --inactivity <seconds>           Inactivity timeout, defaults to the
                                          value of MOJO_INACTIVITY_TIMEOUT or
-                                         15.
+                                         15
         --lock-file <path>               Path to lock file, defaults to a
-                                         random file.
-    -L, --lock-timeout <seconds>         Lock timeout, defaults to 1.
+                                         random file
+    -L, --lock-timeout <seconds>         Lock timeout, defaults to 1
     -l, --listen <location>              One or more locations you want to
                                          listen on, defaults to the value of
-                                         MOJO_LISTEN or "http://*:3000".
+                                         MOJO_LISTEN or "http://*:3000"
         --multi-accept <number>          Number of connection to accept at
-                                         once, defaults to 50.
+                                         once, defaults to 50
     -P, --pid-file <path>                Path to process id file, defaults to
-                                         a random file.
+                                         a random file
     -p, --proxy                          Activate reverse proxy support,
                                          defaults to the value of
-                                         MOJO_REVERSE_PROXY.
+                                         MOJO_REVERSE_PROXY
     -r, --requests <number>              Maximum number of requests per
                                          keep-alive connection, defaults to
-                                         25.
-    -u, --user <name>                    Username for process.
-    -w, --workers <number>               Number of workers, defaults to 4.
+                                         25
+    -u, --user <name>                    Username for process
+    -w, --workers <number>               Number of workers, defaults to 4
 
 =head1 DESCRIPTION
 
@@ -106,14 +106,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $prefork->description;
-  $prefork        = $prefork->description('Foo!');
+  $prefork        = $prefork->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $prefork->usage;
-  $prefork  = $prefork->usage('Foo!');
+  $prefork  = $prefork->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';
 
 use Mojo::Server::PSGI;
 
-has description => 'Start application with PSGI.';
+has description => 'Start application with PSGI';
 has usage => sub { shift->extract_usage };
 
 sub run { Mojo::Server::PSGI->new(app => shift->app)->to_psgi_app }
@@ -39,14 +39,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $psgi->description;
-  $psgi           = $psgi->description('Foo!');
+  $psgi           = $psgi->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $psgi->usage;
-  $psgi     = $psgi->usage('Foo!');
+  $psgi     = $psgi->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -5,7 +5,7 @@ use re 'regexp_pattern';
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 use Mojo::Util qw(encode tablify);
 
-has description => 'Show available routes.';
+has description => 'Show available routes';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -68,7 +68,7 @@ Mojolicious::Command::routes - Routes command
 
   Options:
     -v, --verbose   Print additional details about routes, flags indicate
-                    C=Conditions, D=Detour, U=Under and W=WebSocket.
+                    C=Conditions, D=Detour, U=Under and W=WebSocket
 
 =head1 DESCRIPTION
 
@@ -88,14 +88,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $routes->description;
-  $routes         = $routes->description('Foo!');
+  $routes         = $routes->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $routes->usage;
-  $routes   = $routes->usage('Foo!');
+  $routes   = $routes->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -3,7 +3,7 @@ use Mojo::Base 'Mojolicious::Command';
 
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
 
-has description => 'Run tests.';
+has description => 'Run tests';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -40,7 +40,7 @@ Mojolicious::Command::test - Test command
     ./myapp.pl test -v t/foo/*.t
 
   Options:
-    -v, --verbose   Print verbose debug information to STDERR.
+    -v, --verbose   Print verbose debug information to STDERR
 
 =head1 DESCRIPTION
 
@@ -60,14 +60,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $test->description;
-  $test           = $test->description('Foo!');
+  $test           = $test->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $test->usage;
-  $test     = $test->usage('Foo!');
+  $test     = $test->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -5,7 +5,7 @@ use Mojo::IOLoop::Client;
 use Mojo::UserAgent;
 use Mojolicious;
 
-has description => 'Show versions of available modules.';
+has description => 'Show versions of available modules';
 has usage => sub { shift->extract_usage };
 
 sub run {
@@ -39,7 +39,7 @@ EOF
   my $msg = 'This version is up to date, have fun!';
   $msg = 'Thanks for testing a development release, you are awesome!'
     if $latest < $Mojolicious::VERSION;
-  $msg = "You might want to update your Mojolicious to $latest."
+  $msg = "You might want to update your Mojolicious to $latest!"
     if $latest > $Mojolicious::VERSION;
   say $msg;
 }
@@ -75,14 +75,14 @@ L<Mojolicious::Command> and implements the following new ones.
 =head2 description
 
   my $description = $v->description;
-  $v              = $v->description('Foo!');
+  $v              = $v->description('Foo');
 
 Short description of this command, used for the command list.
 
 =head2 usage
 
   my $usage = $v->usage;
-  $v        = $v->usage('Foo!');
+  $v        = $v->usage('Foo');
 
 Usage information for this command, used for the help screen.
 
@@ -13,7 +13,7 @@ use Mojo::Util qw(spurt unindent);
 use Pod::Usage 'pod2usage';
 
 has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') };
-has description => 'No description.';
+has description => 'No description';
 has 'quiet';
 has usage => "Usage: APPLICATION\n";
 
@@ -99,14 +99,14 @@ Mojolicious::Command - Command base class
   use Mojo::Base 'Mojolicious::Command';
 
   # Short description
-  has description => 'My first Mojo command.';
+  has description => 'My first Mojo command';
 
   # Short usage message
   has usage => <<EOF;
   Usage: APPLICATION mycommand [OPTIONS]
 
   Options:
-    -s, --something   Does something.
+    -s, --something   Does something
   EOF
 
   sub run {
@@ -139,7 +139,7 @@ Application for command, defaults to a L<Mojo::HelloWorld> object.
 =head2 description
 
   my $description = $command->description;
-  $command        = $command->description('Foo!');
+  $command        = $command->description('Foo');
 
 Short description of command, used for the command list.
 
@@ -153,7 +153,7 @@ Limited command output.
 =head2 usage
 
   my $usage = $command->usage;
-  $command  = $command->usage('Foo!');
+  $command  = $command->usage('Foo');
 
 Usage information for command, used for the help screen.
 
@@ -13,7 +13,6 @@ has message    => sub { shift->extract_usage . "\nCommands:\n" };
 has namespaces => sub { ['Mojolicious::Command'] };
 
 sub detect {
-  my ($self, $guess) = @_;
 
   # PSGI (Plack only for now)
   return 'psgi' if defined $ENV{PLACK_ENV};
@@ -22,24 +21,9 @@ sub detect {
   return 'cgi' if defined $ENV{PATH_INFO} || defined $ENV{GATEWAY_INTERFACE};
 
   # Nothing
-  return $guess;
+  return undef;
 }
 
-# Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
-sub _args {
-  return if __PACKAGE__->detect;
-
-  my $save
-    = Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case pass_through));
-  GetOptionsFromArray shift,
-    'h|help'   => \$ENV{MOJO_HELP},
-    'home=s'   => \$ENV{MOJO_HOME},
-    'm|mode=s' => \$ENV{MOJO_MODE};
-  Getopt::Long::Configure($save);
-}
-
-BEGIN { _args([@ARGV]) }
-
 sub run {
   my ($self, $name, @args) = @_;
 
@@ -47,14 +31,14 @@ sub run {
   return $self->app if defined $ENV{MOJO_APP_LOADER};
 
   # Try to detect environment
-  $name = $self->detect($name) unless $ENV{MOJO_NO_DETECT};
+  if (!$ENV{MOJO_NO_DETECT} && (my $env = $self->detect)) { $name = $env }
 
   # Run command
   if ($name && $name =~ /^\w+$/ && ($name ne 'help' || $args[0])) {
 
     # Help
     $name = shift @args if my $help = $name eq 'help';
-    $help = $ENV{MOJO_HELP} = $ENV{MOJO_HELP} ? 1 : $help;
+    $help = $ENV{MOJO_HELP} ||= $help;
 
     # Remove options shared by all commands before loading the command
     _args(\@args);
@@ -91,6 +75,22 @@ sub run {
 
 sub start_app { shift; Mojo::Server->new->build_app(shift)->start(@_) }
 
+# Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
+sub _args {
+  return if __PACKAGE__->detect;
+
+  my $save
+    = Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case pass_through));
+  GetOptionsFromArray shift,
+    'h|help'   => \$ENV{MOJO_HELP},
+    'home=s'   => \$ENV{MOJO_HOME},
+    'm|mode=s' => \$ENV{MOJO_MODE};
+  Getopt::Long::Configure($save);
+}
+
+# Do not remove options from @ARGV
+BEGIN { _args([@ARGV]) }
+
 sub _command {
   my ($module, $fatal) = @_;
   return $module->isa('Mojolicious::Command') ? $module : undef
@@ -114,11 +114,11 @@ Mojolicious::Commands - Command line interface
        work without commands.
 
   Options (for all commands):
-    -h, --help          Get more information on a specific command.
+    -h, --help          Get more information on a specific command
         --home <path>   Path to your applications home directory, defaults to
-                        the value of MOJO_HOME or auto detection.
+                        the value of MOJO_HOME or auto detection
     -m, --mode <name>   Operating mode for your application, defaults to the
-                        value of MOJO_MODE/PLACK_ENV or "development".
+                        value of MOJO_MODE/PLACK_ENV or "development"
 
 =head1 DESCRIPTION
 
@@ -270,7 +270,7 @@ and implements the following new ones.
 =head2 hint
 
   my $hint  = $commands->hint;
-  $commands = $commands->hint('Foo!');
+  $commands = $commands->hint('Foo');
 
 Short hint shown after listing available commands.
 
@@ -299,9 +299,8 @@ implements the following new ones.
 =head2 detect
 
   my $env = $commands->detect;
-  my $env = $commands->detect($guess);
 
-Try to detect environment.
+Try to detect environment or return C<undef> if none could be detected.
 
 =head2 run
 
@@ -316,9 +315,11 @@ disabled with the C<MOJO_NO_DETECT> environment variable.
   Mojolicious::Commands->start_app('MyApp');
   Mojolicious::Commands->start_app(MyApp => @ARGV);
 
-Load application from class and start the command line interface for it.
+Load application from class and start the command line interface for it. Note
+that the options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode>, which are
+shared by all commands, will be parsed from C<@ARGV> during compile time.
 
-  # Always start daemon for application and ignore @ARGV
+  # Always start daemon for application
   Mojolicious::Commands->start_app('MyApp', 'daemon', '-l', 'http://*:8080');
 
 =head1 SEE ALSO
@@ -49,7 +49,7 @@ sub cookie {
 
     # Cookie too big
     my $cookie = {name => $name, value => shift, %{shift || {}}};
-    $self->app->log->error(qq{Cookie "$name" is bigger than 4096 bytes.})
+    $self->app->log->error(qq{Cookie "$name" is bigger than 4096 bytes})
       if length $cookie->{value} > 4096;
 
     $self->res->cookies($cookie);
@@ -65,9 +65,48 @@ sub every_cookie {
   [map { $_->value } @{shift->req->every_cookie(shift)}];
 }
 
-sub every_param { _param(@_) }
+sub every_param {
+  my ($self, $name) = @_;
+
+  # Captured unreserved values
+  my $captures = $self->stash->{'mojo.captures'} ||= {};
+  if (!$RESERVED{$name} && exists $captures->{$name}) {
+    my $value = $captures->{$name};
+    return ref $value eq 'ARRAY' ? $value : [$value];
+  }
+
+  # Uploads or param values
+  my $req     = $self->req;
+  my $uploads = $req->every_upload($name);
+  return @$uploads ? $uploads : $req->every_param($name);
+}
+
+sub every_signed_cookie {
+  my ($self, $name) = @_;
 
-sub every_signed_cookie { _signed_cookie(@_) }
+  my $secrets = $self->stash->{'mojo.secrets'};
+  my @results;
+  for my $value (@{$self->every_cookie($name)}) {
+
+    # Check signature with rotating secrets
+    if ($value =~ s/--([^\-]+)$//) {
+      my $signature = $1;
+
+      my $valid;
+      for my $secret (@$secrets) {
+        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
+        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
+      }
+      if ($valid) { push @results, $value }
+
+      else { $self->app->log->debug(qq{Cookie "$name" has a bad signature}) }
+    }
+
+    else { $self->app->log->debug(qq{Cookie "$name" is not signed}) }
+  }
+
+  return \@results;
+}
 
 sub finish {
   my $self = shift;
@@ -116,17 +155,17 @@ sub param {
 
   # List names
   my $captures = $self->stash->{'mojo.captures'} ||= {};
-  my $req = $self->req;
   unless (defined $name) {
+    my $req  = $self->req;
+    my @keys = $req->param;
+    push @keys, map  { $_->name } @{$req->uploads};
+    push @keys, grep { !$RESERVED{$_} } keys %$captures;
     my %seen;
-    my @keys = grep { !$seen{$_}++ } $req->param;
-    push @keys, grep { !$seen{$_}++ } map { $_->name } @{$req->uploads};
-    push @keys, grep { !$RESERVED{$_} && !$seen{$_}++ } keys %$captures;
-    return sort @keys;
+    return sort grep { !$seen{$_}++ } @keys;
   }
 
   # Value
-  return _param($self, $name)->[-1] unless @_;
+  return $self->every_param($name)->[-1] unless @_;
 
   # Override values
   $captures->{$name} = @_ > 1 ? [@_] : $_[0];
@@ -158,7 +197,8 @@ sub render {
   return defined $output ? Mojo::ByteStream->new($output) : undef if $ts;
 
   # Maybe
-  return $maybe ? undef : !$self->render_not_found unless defined $output;
+  return $maybe ? undef : !$self->helpers->reply->not_found
+    unless defined $output;
 
   # Prepare response
   $plugins->emit_hook(after_render => $self, \$output, $format);
@@ -168,14 +208,10 @@ sub render {
   return !!$self->rendered($self->stash->{status});
 }
 
-sub render_exception { shift->helpers->reply->exception(@_) }
-
 sub render_later { shift->stash('mojo.rendered' => 1) }
 
 sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) }
 
-sub render_not_found { shift->helpers->reply->not_found }
-
 sub render_to_string { shift->render(@_, 'mojo.to_string' => 1) }
 
 sub rendered {
@@ -197,7 +233,7 @@ sub rendered {
       my $rps  = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed;
       my $code = $res->code;
       my $msg  = $res->message || $res->default_message($code);
-      $app->log->debug("$code $msg (${elapsed}s, $rps/s).");
+      $app->log->debug("$code $msg (${elapsed}s, $rps/s)");
     }
 
     $app->plugins->emit_hook_reverse(after_dispatch => $self);
@@ -272,7 +308,7 @@ sub signed_cookie {
   return map { $self->signed_cookie($_) } @$name if ref $name eq 'ARRAY';
 
   # Request cookie
-  return _signed_cookie($self, $name)->[-1] unless defined $value;
+  return $self->every_signed_cookie($name)->[-1] unless defined $value;
 
   # Response cookie
   my $checksum
@@ -352,48 +388,6 @@ sub write_chunk {
   return $self->rendered;
 }
 
-sub _param {
-  my ($self, $name) = @_;
-
-  # Captured unreserved values
-  my $captures = $self->stash->{'mojo.captures'} ||= {};
-  if (!$RESERVED{$name} && defined(my $value = $captures->{$name})) {
-    return ref $value eq 'ARRAY' ? $value : [$value];
-  }
-
-  # Uploads or param values
-  my $req     = $self->req;
-  my $uploads = $req->every_upload($name);
-  return @$uploads ? $uploads : $req->every_param($name);
-}
-
-sub _signed_cookie {
-  my ($self, $name) = @_;
-
-  my $secrets = $self->stash->{'mojo.secrets'};
-  my @results;
-  for my $value (@{$self->every_cookie($name)}) {
-
-    # Check signature with rotating secrets
-    if ($value =~ s/--([^\-]+)$//) {
-      my $signature = $1;
-
-      my $valid;
-      for my $secret (@$secrets) {
-        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
-        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
-      }
-      if ($valid) { push @results, $value }
-
-      else { $self->app->log->debug(qq{Cookie "$name" has bad signature.}) }
-    }
-
-    else { $self->app->log->debug(qq{Cookie "$name" not signed.}) }
-  }
-
-  return \@results;
-}
-
 1;
 
 =encoding utf8
@@ -436,7 +430,7 @@ A reference back to the application that dispatched to this controller,
 defaults to a L<Mojolicious> object.
 
   # Use application logger
-  $c->app->log->debug('Hello Mojo!');
+  $c->app->log->debug('Hello Mojo');
 
   # Generate path
   my $path = $c->app->home->rel_file('templates/foo/bar.html.ep');
@@ -471,7 +465,7 @@ underlying connection might get closed early.
   # Perform non-blocking operation without knowing the connection status
   my $tx = $c->tx;
   Mojo::IOLoop->timer(2 => sub {
-    $c->app->log->debug($tx->is_finished ? 'Finished.' : 'In progress.');
+    $c->app->log->debug($tx->is_finished ? 'Finished' : 'In progress');
   });
 
 =head1 METHODS
@@ -562,6 +556,9 @@ L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
   # Make sure to use the "title" helper and not the controller method
   $c->helpers->title('Welcome!');
 
+  # Use a nested helper instead of the "reply" controller method
+  $c->helpers->reply->not_found;
+
 =head2 on
 
   my $cb = $c->on(finish => sub {...});
@@ -574,7 +571,7 @@ status.
   # Do something after the transaction has been finished
   $c->on(finish => sub {
     my $c = shift;
-    $c->app->log->debug('We are done!');
+    $c->app->log->debug('We are done');
   });
 
   # Receive WebSocket message
@@ -593,7 +590,7 @@ status.
   $c->on(binary => sub {
     my ($c, $bytes) = @_;
     my $len = length $bytes;
-    $c->app->log->debug("Received $len bytes.");
+    $c->app->log->debug("Received $len bytes");
   });
 
 =head2 param
@@ -672,22 +669,21 @@ L</"stash">.
   # Render JSON
   $c->render(json => {test => 'I ♥ Mojolicious!'});
 
+  # Render inline template
+  $c->render(inline => '<%= 1 + 1 %>');
+
   # Render template "foo/bar.html.ep"
   $c->render(template => 'foo/bar', format => 'html', handler => 'ep');
 
   # Render template "foo/bar.*.*"
   $c->render(template => 'foo/bar');
 
+  # Render template "test.*.*" with arbitrary values "foo" and "bar"
+  $c->render(template => 'test', foo => 'test', bar => 23);
+
   # Render template "test.xml.*"
   $c->render('test', format => 'xml');
 
-=head2 render_exception
-
-  $c = $c->render_exception('Oops!');
-  $c = $c->render_exception(Mojo::Exception->new('Oops!'));
-
-Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception">.
-
 =head2 render_later
 
   $c = $c->render_later;
@@ -714,12 +710,6 @@ could be generated, takes the same arguments as L</"render">.
   # Render template "index_local" only if it exists
   $c->render_maybe('index_local') or $c->render('index');
 
-=head2 render_not_found
-
-  $c = $c->render_not_found;
-
-Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
-
 =head2 render_to_string
 
   my $output = $c->render_to_string('foo/index', format => 'pdf');
@@ -729,6 +719,9 @@ return C<undef>, all arguments get localized automatically and are only
 available during this render operation, takes the same arguments as
 L</"render">.
 
+  # Render inline template
+  my $two = $c->render_to_string(inline => '<%= 1 + 1 %>');
+
 =head2 rendered
 
   $c = $c->rendered;
@@ -779,6 +772,10 @@ Get L<Mojo::Message::Response> object from L</"tx">.
   # Force file download by setting a custom response header
   $c->res->headers->content_disposition('attachment; filename=foo.png;');
 
+  # Make sure response is cached correctly
+  $c->res->headers->cache_control('public, max-age=300');
+  $c->res->headers->append(Vary => 'Accept-Encoding');
+
 =head2 respond_to
 
   $c = $c->respond_to(
@@ -897,6 +894,9 @@ reserved for internal use.
   # Remove value
   my $foo = delete $c->stash->{foo};
 
+  # Assign multiple values at once
+  $c->stash(foo => 'test', bar => 23);
+
 =head2 url_for
 
   my $url = $c->url_for;
@@ -24,7 +24,7 @@ in the construction of more advanced web servers, but is solid and fast enough
 for small to mid sized applications.
 
   $ ./script/my_app daemon
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 It is available to every application through the command
 L<Mojolicious::Command::daemon>, which has many configuration options and is
@@ -40,14 +40,14 @@ works, but you can specify all listen locations supported by
 L<Mojo::Server::Daemon/"listen">.
 
   $ ./script/my_app daemon -l https://[::]:3000
-  Server available at https://[::]:3000.
+  Server available at https://[::]:3000
 
 On UNIX platforms you can also add preforking and switch to a multi-process
 architecture with L<Mojolicious::Command::prefork>, to take advantage of
 multiple CPU cores and copy-on-write memory management.
 
   $ ./script/my_app prefork
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 Since all built-in web servers are based on the L<Mojo::IOLoop> event loop,
 they scale best with non-blocking operations. But if your application for some
@@ -57,7 +57,7 @@ concurrent connections each worker is allowed to handle (often as low as
 C<1>).
 
   $ ./script/my_app prefork -m production -w 10 -c 1
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 Your application is preloaded in the manager process during startup, to run
 code whenever a new worker process has been forked you can use
@@ -75,7 +75,7 @@ L<Mojo::IOLoop/"next_tick">.
 
 =head2 Morbo
 
-After reading the L<Mojolicious::Lite> tutorial, you should already be
+After reading the L<Mojolicious::Guides::Tutorial>, you should already be
 familiar with L<Mojo::Server::Morbo>.
 
   Mojo::Server::Morbo
@@ -86,7 +86,7 @@ server whenever a file in your project changes, and should therefore only be
 used during development.
 
   $ morbo ./script/my_app
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 =head2 Hypnotoad
 
@@ -106,11 +106,11 @@ to L<Mojo::Server::Daemon>, but optimized specifically for production
 environments out of the box.
 
   $ hypnotoad ./script/my_app
-  Server available at http://127.0.0.1:8080.
 
-It automatically sets the operating mode to C<production> and you can tweak
-many configuration settings right from within your application with
-L<Mojo/"config">, for a full list see L<Mojo::Server::Hypnotoad/"SETTINGS">.
+It automatically listens on port C<8080>, sets the operating mode to
+C<production> and you can tweak many configuration settings right from within
+your application with L<Mojo/"config">, for a full list see
+L<Mojo::Server::Hypnotoad/"SETTINGS">.
 
   use Mojolicious::Lite;
 
@@ -154,13 +154,13 @@ C<SO_REUSEPORT> socket option, there is also another method available that
 works with all built-in web servers.
 
   $ ./script/my_app prefork -P /tmp/first.pid -l http://*:8080?reuse=1
-  Server available at http://127.0.0.1:8080.
+  Server available at http://127.0.0.1:8080
 
 All you have to do is start a second web server listening to the same port and
 stop the first web server gracefully afterwards.
 
   $ ./script/my_app prefork -P /tmp/second.pid -l http://*:8080?reuse=1
-  Server available at http://127.0.0.1:8080.
+  Server available at http://127.0.0.1:8080
   $ kill -s TERM `cat /tmp/first.pid`
 
 Just remember that both web servers need to be started with the C<reuse>
@@ -583,7 +583,7 @@ L<Mojolicious::Controller/"send">.
     my $c = shift;
 
     # Opened
-    $c->app->log->debug('WebSocket opened.');
+    $c->app->log->debug('WebSocket opened');
 
     # Increase inactivity timeout for connection a bit
     $c->inactivity_timeout(300);
@@ -597,7 +597,7 @@ L<Mojolicious::Controller/"send">.
     # Closed
     $c->on(finish => sub {
       my ($c, $code, $reason) = @_;
-      $c->app->log->debug("WebSocket closed with status $code.");
+      $c->app->log->debug("WebSocket closed with status $code");
     });
   };
 
@@ -755,7 +755,7 @@ which can be combined to solve some of hardest problems in web development.
             my ($single, $bytes) = @_;
 
             # Log size of every chunk we receive
-            app->log->debug(length($bytes) . ' bytes uploaded.');
+            app->log->debug(length($bytes) . ' bytes uploaded');
           });
         });
       });
@@ -1253,7 +1253,7 @@ the helper L<Mojolicious::Plugin::DefaultHelpers/"config">
   plugin 'Config';
 
   my $name = app->config('name');
-  app->log->debug("Welcome to $name.");
+  app->log->debug("Welcome to $name");
 
   get '/' => 'with_config';
 
@@ -1324,7 +1324,7 @@ that they will be picked up automatically by the command line interface?
   package Mojolicious::Command::spy;
   use Mojo::Base 'Mojolicious::Command';
 
-  has description => 'Spy on application.';
+  has description => 'Spy on application';
   has usage       => "Usage: APPLICATION spy [TARGET]\n";
 
   sub run {
@@ -1365,8 +1365,8 @@ L<Mojolicious::Commands/"namespaces">.
 
   1;
 
-Some options like C<-m> for the operating mode of your application are shared
-by all commands automatically.
+The options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode> are handled
+automatically by L<Mojolicious::Commands> and are shared by all commands.
 
   $ ./myapp.pl spy -m production mode
   production
@@ -78,7 +78,7 @@ Mojolicious uses many environment variables both internally and externally,
 notably (but not exclusively) those starting with the prefix C<MOJO_*>. The
 test suite expects a clean environment; testing with a non-standard
 environment is unsupported and is unlikely to succeed. Therefore when
-installing or upgrading Mojolicious and when running its tests, we highly
+installing or upgrading L<Mojolicious> and when running its tests, we highly
 recommend using an environment which does not set these variables.
 
 =head2 What is the difference between blocking and non-blocking operations?
@@ -198,7 +198,7 @@ The built-in development web server makes working on your application a lot of
 fun thanks to automatic reloading.
 
   $ morbo ./myapp.pl
-  Server available at http://127.0.0.1:3000.
+  Server available at http://127.0.0.1:3000
 
 Just save your changes and they will be automatically in effect the next time
 you refresh your browser.
@@ -858,7 +858,7 @@ to make styling with CSS easier.
   <label class="field-with-error" for="user">
     Username (required, only characters e-t)
   </label>
-  <input class="field-with-error" type="text" name="user" value="sri" />
+  <input class="field-with-error" type="text" name="user" value="sri">
 
 For a full list of available checks see also
 L<Mojolicious::Validator/"CHECKS">.
@@ -989,7 +989,7 @@ L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>static">.
 
 Most response content, static as well as dynamic, gets served through
 L<Mojo::Asset::File> and L<Mojo::Asset::Memory> objects. For somewhat static
-content, like cached JSON data or temporary file, you can create your own and
+content, like cached JSON data or temporary files, you can create your own and
 use the helper L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>asset"> to
 serve them while allowing content negotiation to be performed with C<Range>,
 C<If-Modified-Since> and C<If-None-Match> headers.
@@ -1046,8 +1046,8 @@ helper to the application you can use L<Mojolicious/"helper">.
 
   get '/' => sub {
     my $c = shift;
-    $c->debug('It works.');
-    $c->render(text => 'Hello.');
+    $c->debug('It works!');
+    $c->render(text => 'Hello!');
   };
 
   app->start;
@@ -1080,8 +1080,9 @@ plugins, even if you plan to release them to CPAN.
   $ mkdir templates
   $ echo '%= javascript "/alertassets.js"' > templates/alertassets.html.ep
 
-Just append their respective directories to the list of search paths when
-C<register> is called.
+Just give them reasonably unique names, ideally based on the name of your
+plugin, and append their respective directories to the list of search paths
+when C<register> is called.
 
   package Mojolicious::Plugin::AlertAssets;
   use Mojo::Base 'Mojolicious::Plugin';
@@ -23,7 +23,8 @@ requests with code generating the appropriate response.
 
 This black box is usually called a dispatcher. There are many implementations
 using different strategies to establish these connections, but pretty much all
-are based around mapping the requests path to some kind of response generator.
+are based around mapping the path part of the request URL to some kind of
+response generator.
 
   /user/show/2 -> $c->render(text => 'Daniel');
   /user/show/3 -> $c->render(text => 'Sara');
@@ -0,0 +1,951 @@
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Guides::Tutorial - Tutorial
+
+=head1 TUTORIAL
+
+A quick example driven introduction to the wonders of L<Mojolicious::Lite>.
+Almost everything you'll learn here also applies to full L<Mojolicious>
+applications.
+
+=head2 Hello World
+
+A simple Hello World application can look like this, L<strict>, L<warnings>,
+L<utf8> and Perl 5.10 features are automatically enabled and a few functions
+imported when you use L<Mojolicious::Lite>, turning your script into a full
+featured web application.
+
+  #!/usr/bin/env perl
+  use Mojolicious::Lite;
+
+  get '/' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  app->start;
+
+There is also a helper command to generate a small example application.
+
+  $ mojo generate lite_app myapp.pl
+
+=head2 Commands
+
+All the normal L<Mojolicious::Commands> are available from the command line.
+Note that CGI and L<PSGI> environments can usually be auto detected and will
+just work without commands.
+
+  $ ./myapp.pl daemon
+  Server available at http://127.0.0.1:3000
+
+  $ ./myapp.pl daemon -l http://*:8080
+  Server available at http://127.0.0.1:8080
+
+  $ ./myapp.pl cgi
+  ...CGI output...
+
+  $ ./myapp.pl get /
+  Hello World!
+
+  $ ./myapp.pl
+  ...List of available commands (or automatically detected environment)...
+
+The C<app-E<gt>start> call that starts the L<Mojolicious> command system
+should usually be the last expression in your application and can be
+customized to override normal C<@ARGV> use.
+
+  app->start('daemon');
+
+=head2 Reloading
+
+Your application will automatically reload itself if you start it with the
+C<morbo> development web server, so you don't have to restart the server after
+every change.
+
+  $ morbo ./myapp.pl
+  Server available at http://127.0.0.1:3000
+
+For more information about how to deploy your application see also
+L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">.
+
+=head2 Routes
+
+Routes are basically just fancy paths that can contain different kinds of
+placeholders and usually lead to an action, if they match the path part of the
+request URL. The first argument passed to all actions C<$c> is a
+L<Mojolicious::Controller> object containing both the HTTP request and
+response.
+
+  use Mojolicious::Lite;
+
+  # Route leading to an action that renders some text
+  get '/foo' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  app->start;
+
+Response content is often generated by actions with
+L<Mojolicious::Controller/"render">, but more about that later.
+
+=head2 GET/POST parameters
+
+All C<GET> and C<POST> parameters sent with the request are accessible via
+L<Mojolicious::Controller/"param">.
+
+  use Mojolicious::Lite;
+
+  # /foo?user=sri
+  get '/foo' => sub {
+    my $c    = shift;
+    my $user = $c->param('user');
+    $c->render(text => "Hello $user.");
+  };
+
+  app->start;
+
+=head2 Stash and templates
+
+The L<Mojolicious::Controller/"stash"> is used to pass data to templates,
+which can be inlined in the C<DATA> section. A few stash values like
+C<template> and C<text> are reserved and will be used by
+L<Mojolicious::Controller/"render"> to decide how a response should be
+generated.
+
+  use Mojolicious::Lite;
+
+  # Route leading to an action that renders a template
+  get '/foo' => sub {
+    my $c = shift;
+    $c->stash(one => 23);
+    $c->render(template => 'magic', two => 24);
+  };
+
+  app->start;
+  __DATA__
+
+  @@ magic.html.ep
+  The magic numbers are <%= $one %> and <%= $two %>.
+
+For more information about templates see also
+L<Mojolicious::Guides::Rendering/"Embedded Perl">.
+
+=head2 HTTP
+
+L<Mojolicious::Controller/"req"> and L<Mojolicious::Controller/"res"> give you
+full access to all HTTP features and information.
+
+  use Mojolicious::Lite;
+
+  # Access request information
+  get '/agent' => sub {
+    my $c    = shift;
+    my $host = $c->req->url->to_abs->host;
+    my $ua   = $c->req->headers->user_agent;
+    $c->render(text => "Request by $ua reached $host.");
+  };
+
+  # Echo the request body and send custom header with response
+  post '/echo' => sub {
+    my $c = shift;
+    $c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
+    $c->render(data => $c->req->body);
+  };
+
+  app->start;
+
+You can test the more advanced examples right from the command line with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get -v -M POST -c 'test' /echo
+
+=head2 Built-in C<exception> and C<not_found> pages
+
+During development you will encounter these pages whenever you make a mistake,
+they are gorgeous and contain a lot of valuable information that will aid you
+in debugging your application.
+
+  use Mojolicious::Lite;
+
+  # Not found (404)
+  get '/missing' => sub { shift->render(template => 'does_not_exist') };
+
+  # Exception (500)
+  get '/dies' => sub { die 'Intentional error' };
+
+  app->start;
+
+You can even use CSS selectors with L<Mojolicious::Command::get> to extract
+only the information you're actually interested in.
+
+  $ ./myapp.pl get /dies '#error'
+
+=head2 Route names
+
+All routes can have a name associated with them, this allows automatic
+template detection and backreferencing with
+L<Mojolicious::Controller/"url_for">, on which many methods and helpers like
+L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
+
+  use Mojolicious::Lite;
+
+  # Render the template "index.html.ep"
+  get '/' => sub {
+    my $c = shift;
+    $c->render;
+  } => 'index';
+
+  # Render the template "hello.html.ep"
+  get '/hello';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <%= link_to Hello  => 'hello' %>.
+  <%= link_to Reload => 'index' %>.
+
+  @@ hello.html.ep
+  Hello World!
+
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters.
+
+=head2 Layouts
+
+Templates can have layouts too, you just select one with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
+current template with the helper
+L<Mojolicious::Plugin::DefaultHelpers/"content">.
+
+  use Mojolicious::Lite;
+
+  get '/with_layout';
+
+  app->start;
+  __DATA__
+
+  @@ with_layout.html.ep
+  % title 'Green';
+  % layout 'green';
+  Hello World!
+
+  @@ layouts/green.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title><%= title %></title></head>
+    <body><%= content %></body>
+  </html>
+
+The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can
+be used to pass additional data to the layout.
+
+=head2 Blocks
+
+Template blocks can be used like normal Perl functions and are always
+delimited by the C<begin> and C<end> keywords, they are the foundation for
+many helpers.
+
+  use Mojolicious::Lite;
+
+  get '/with_block' => 'block';
+
+  app->start;
+  __DATA__
+
+  @@ block.html.ep
+  % my $link = begin
+    % my ($url, $name) = @_;
+    Try <%= link_to $url => begin %><%= $name %><% end %>.
+  % end
+  <!DOCTYPE html>
+  <html>
+    <head><title>Sebastians frameworks</title></head>
+    <body>
+      %= $link->('http://mojolicio.us', 'Mojolicious')
+      %= $link->('http://catalystframework.org', 'Catalyst')
+    </body>
+  </html>
+
+=head2 Helpers
+
+Helpers are little functions you can reuse throughout your whole application,
+from actions to templates.
+
+  use Mojolicious::Lite;
+
+  # A helper to identify visitors
+  helper whois => sub {
+    my $c     = shift;
+    my $agent = $c->req->headers->user_agent || 'Anonymous';
+    my $ip    = $c->tx->remote_address;
+    return "$agent ($ip)";
+  };
+
+  # Use helper in action and template
+  get '/secret' => sub {
+    my $c    = shift;
+    my $user = $c->whois;
+    $c->app->log->debug("Request from $user");
+  };
+
+  app->start;
+  __DATA__
+
+  @@ secret.html.ep
+  We know who you are <%= whois %>.
+
+A list of all built-in ones can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+=head2 Placeholders
+
+Route placeholders allow capturing parts of a request path until a C</> or
+C<.> separator occurs, results are accessible via
+L<Mojolicious::Controller/"stash"> and L<Mojolicious::Controller/"param">.
+
+  use Mojolicious::Lite;
+
+  # /foo/test
+  # /foo/test123
+  get '/foo/:bar' => sub {
+    my $c   = shift;
+    my $bar = $c->stash('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  # /testsomething/foo
+  # /test123something/foo
+  get '/(:bar)something/foo' => sub {
+    my $c   = shift;
+    my $bar = $c->param('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  app->start;
+
+=head2 Relaxed Placeholders
+
+Relaxed placeholders allow matching of everything until a C</> occurs.
+
+  use Mojolicious::Lite;
+
+  # /test/hello
+  # /test123/hello
+  # /test.123/hello
+  get '/#you/hello' => 'groovy';
+
+  app->start;
+  __DATA__
+
+  @@ groovy.html.ep
+  Your name is <%= $you %>.
+
+=head2 Wildcard placeholders
+
+Wildcard placeholders allow matching absolutely everything, including C</> and
+C<.>.
+
+  use Mojolicious::Lite;
+
+  # /hello/test
+  # /hello/test123
+  # /hello/test.123/test/123
+  get '/hello/*you' => 'groovy';
+
+  app->start;
+  __DATA__
+
+  @@ groovy.html.ep
+  Your name is <%= $you %>.
+
+=head2 HTTP methods
+
+Routes can be restricted to specific request methods with different keywords.
+
+  use Mojolicious::Lite;
+
+  # GET /hello
+  get '/hello' => sub {
+    my $c = shift;
+    $c->render(text => 'Hello World!');
+  };
+
+  # PUT /hello
+  put '/hello' => sub {
+    my $c    = shift;
+    my $size = length $c->req->body;
+    $c->render(text => "You uploaded $size bytes to /hello.");
+  };
+
+  # GET|POST|PATCH /bye
+  any [qw(GET POST PATCH)] => '/bye' => sub {
+    my $c = shift;
+    $c->render(text => 'Bye World!');
+  };
+
+  # * /whatever
+  any '/whatever' => sub {
+    my $c      = shift;
+    my $method = $c->req->method;
+    $c->render(text => "You called /whatever with $method.");
+  };
+
+  app->start;
+
+=head2 Optional placeholders
+
+All placeholders require a value, but by assigning them default values you can
+make capturing optional.
+
+  use Mojolicious::Lite;
+
+  # /hello
+  # /hello/Sara
+  get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub {
+    my $c = shift;
+    $c->render(template => 'groovy', format => 'txt');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ groovy.txt.ep
+  My name is <%= $name %> and it is <%= $day %>.
+
+Default values that don't belong to a placeholder simply get merged into the
+stash all the time.
+
+=head2 Restrictive placeholders
+
+The easiest way to make placeholders more restrictive are alternatives, you
+just make a list of possible values.
+
+  use Mojolicious::Lite;
+
+  # /test
+  # /123
+  any '/:foo' => [foo => [qw(test 123)]] => sub {
+    my $c   = shift;
+    my $foo = $c->param('foo');
+    $c->render(text => "Our :foo placeholder matched $foo");
+  };
+
+  app->start;
+
+All placeholders get compiled to a regular expression internally, this process
+can also be easily customized.
+
+  use Mojolicious::Lite;
+
+  # /1
+  # /123
+  any '/:bar' => [bar => qr/\d+/] => sub {
+    my $c   = shift;
+    my $bar = $c->param('bar');
+    $c->render(text => "Our :bar placeholder matched $bar");
+  };
+
+  app->start;
+
+Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
+placeholders become part of a larger regular expression internally, C<(?:...)>
+is fine though.
+
+=head2 Under
+
+Authentication and code shared between multiple routes can be realized easily
+with routes generated by L<Mojolicious::Lite/"under">. All following routes
+are only evaluated if the callback returned a true value.
+
+  use Mojolicious::Lite;
+
+  # Authenticate based on name parameter
+  under sub {
+    my $c = shift;
+
+    # Authenticated
+    my $name = $c->param('name') || '';
+    return 1 if $name eq 'Bender';
+
+    # Not authenticated
+    $c->render(template => 'denied');
+    return undef;
+  };
+
+  # Only reached when authenticated
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ denied.html.ep
+  You are not Bender, permission denied.
+
+  @@ index.html.ep
+  Hi Bender.
+
+Prefixing multiple routes is another good use for it.
+
+  use Mojolicious::Lite;
+
+  # /foo
+  under '/foo';
+
+  # /foo/bar
+  get '/bar' => {text => 'foo bar'};
+
+  # /foo/baz
+  get '/baz' => {text => 'foo baz'};
+
+  # / (reset)
+  under '/' => {msg => 'whatever'};
+
+  # /bar
+  get '/bar' => {inline => '<%= $msg %> works'};
+
+  app->start;
+
+You can also group related routes with L<Mojolicious::Lite/"group">, which
+allows nesting of routes generated with L<Mojolicious::Lite/"under">.
+
+  use Mojolicious::Lite;
+
+  # Global logic shared by all routes
+  under sub {
+    my $c = shift;
+    return 1 if $c->req->headers->header('X-Bender');
+    $c->render(text => "You're not Bender.");
+    return undef;
+  };
+
+  # Admin section
+  group {
+
+    # Local logic shared only by routes in this group
+    under '/admin' => sub {
+      my $c = shift;
+      return 1 if $c->req->headers->header('X-Awesome');
+      $c->render(text => "You're not awesome enough.");
+      return undef;
+    };
+
+    # GET /admin/dashboard
+    get '/dashboard' => {text => 'Nothing to see here yet.'};
+  };
+
+  # GET /welcome
+  get '/welcome' => {text => 'Hi Bender.'};
+
+  app->start;
+
+=head2 Formats
+
+Formats can be automatically detected from file extensions, they are used to
+find the right template and generate the correct C<Content-Type> header.
+
+  use Mojolicious::Lite;
+
+  # /detection
+  # /detection.html
+  # /detection.txt
+  get '/detection' => sub {
+    my $c = shift;
+    $c->render(template => 'detected');
+  };
+
+  app->start;
+  __DATA__
+
+  @@ detected.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Detected</title></head>
+    <body>HTML was detected.</body>
+  </html>
+
+  @@ detected.txt.ep
+  TXT was detected.
+
+The default format is C<html>, restrictive placeholders can be used to limit
+possible values.
+
+  use Mojolicious::Lite;
+
+  # /hello.json
+  # /hello.txt
+  get '/hello' => [format => [qw(json txt)]] => sub {
+    my $c = shift;
+    return $c->render(json => {hello => 'world'})
+      if $c->stash('format') eq 'json';
+    $c->render(text => 'hello world');
+  };
+
+  app->start;
+
+Or you can just disable format detection.
+
+  use Mojolicious::Lite;
+
+  # /hello
+  get '/hello' => [format => 0] => {text => 'No format detection.'};
+
+  # Disable detection and allow the following routes selective re-enabling
+  under [format => 0];
+
+  # /foo
+  get '/foo' => {text => 'No format detection again.'};
+
+  # /bar.txt
+  get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
+
+  app->start;
+
+=head2 Content negotiation
+
+For resources with different representations and that require truly RESTful
+content negotiation you can also use L<Mojolicious::Controller/"respond_to">.
+
+  use Mojolicious::Lite;
+
+  # /hello (Accept: application/json)
+  # /hello (Accept: application/xml)
+  # /hello.json
+  # /hello.xml
+  # /hello?format=json
+  # /hello?format=xml
+  get '/hello' => sub {
+    my $c = shift;
+    $c->respond_to(
+      json => {json => {hello => 'world'}},
+      xml  => {text => '<hello>world</hello>'},
+      any  => {data => '', status => 204}
+    );
+  };
+
+  app->start;
+
+MIME type mappings can be extended or changed easily with
+L<Mojolicious/"types">.
+
+  app->types->type(rdf => 'application/rdf+xml');
+
+=head2 Static files
+
+Similar to templates, but with only a single file extension and optional
+Base64 encoding, static files can be inlined in the C<DATA> section and are
+served automatically.
+
+  use Mojolicious::Lite;
+
+  app->start;
+  __DATA__
+
+  @@ something.js
+  alert('hello!');
+
+  @@ test.txt (base64)
+  dGVzdCAxMjMKbGFsYWxh
+
+External static files are not limited to a single file extension and will be
+served automatically from a C<public> directory if it exists.
+
+  $ mkdir public
+  $ mv something.js public/something.js
+  $ mv mojolicious.tar.gz public/mojolicious.tar.gz
+
+Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
+Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
+headers is supported as well and can be tested very easily with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
+
+=head2 External templates
+
+External templates will be searched by the renderer in a C<templates>
+directory if it exists and have a higher precedence than those in the C<DATA>
+section.
+
+  use Mojolicious::Lite;
+
+  # Render template "templates/foo/bar.html.ep"
+  any '/external' => sub {
+    my $c = shift;
+    $c->render(template => 'foo/bar');
+  };
+
+  app->start;
+
+=head2 Conditions
+
+Conditions such as C<agent> and C<host> from
+L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route
+constructs.
+
+  use Mojolicious::Lite;
+
+  # Firefox
+  get '/foo' => (agent => qr/Firefox/) => sub {
+    my $c = shift;
+    $c->render(text => 'Congratulations, you are using a cool browser.');
+  };
+
+  # Internet Explorer
+  get '/foo' => (agent => qr/Internet Explorer/) => sub {
+    my $c = shift;
+    $c->render(text => 'Dude, you really need to upgrade to Firefox.');
+  };
+
+  # http://mojolicio.us/bar
+  get '/bar' => (host => 'mojolicio.us') => sub {
+    my $c = shift;
+    $c->render(text => 'Hello Mojolicious.');
+  };
+
+  app->start;
+
+=head2 Sessions
+
+Signed cookie based sessions just work out of the box as soon as you start
+using them through the helper
+L<Mojolicious::Plugin::DefaultHelpers/"session">, just be aware that all
+session data gets serialized with L<Mojo::JSON>.
+
+  use Mojolicious::Lite;
+
+  # Access session data in action and template
+  get '/counter' => sub {
+    my $c = shift;
+    $c->session->{counter}++;
+  };
+
+  app->start;
+  __DATA__
+
+  @@ counter.html.ep
+  Counter: <%= session 'counter' %>
+
+Note that you should use custom L<Mojolicious/"secrets"> to make signed
+cookies really secure.
+
+  app->secrets(['My secret passphrase here']);
+
+=head2 File uploads
+
+All files uploaded via C<multipart/form-data> request are automatically
+available as L<Mojo::Upload> objects. And you don't have to worry about memory
+usage, because all files above 250KB will be automatically streamed into a
+temporary file.
+
+  use Mojolicious::Lite;
+
+  # Upload form in DATA section
+  get '/' => 'form';
+
+  # Multipart upload handler
+  post '/upload' => sub {
+    my $c = shift;
+
+    # Check file size
+    return $c->render(text => 'File is too big.', status => 200)
+      if $c->req->is_limit_exceeded;
+
+    # Process uploaded file
+    return $c->redirect_to('form') unless my $example = $c->param('example');
+    my $size = $example->size;
+    my $name = $example->filename;
+    $c->render(text => "Thanks for uploading $size byte file $name.");
+  };
+
+  app->start;
+  __DATA__
+
+  @@ form.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head><title>Upload</title></head>
+    <body>
+      %= form_for upload => (enctype => 'multipart/form-data') => begin
+        %= file_field 'example'
+        %= submit_button 'Upload'
+      % end
+    </body>
+  </html>
+
+To protect you from excessively large files there is also a limit of 10MB by
+default, which you can tweak with the attribute
+L<Mojo::Message/"max_message_size"> or C<MOJO_MAX_MESSAGE_SIZE> environment
+variable.
+
+  # Increase limit to 1GB
+  $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824;
+
+=head2 User agent
+
+With L<Mojo::UserAgent>, which is available through the helper
+L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and
+WebSocket user agent built right in. Especially in combination with
+L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
+
+  use Mojolicious::Lite;
+
+  # Blocking
+  get '/headers' => sub {
+    my $c   = shift;
+    my $url = $c->param('url') || 'http://mojolicio.us';
+    my $dom = $c->ua->get($url)->res->dom;
+    $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
+  };
+
+  # Non-blocking
+  get '/title' => sub {
+    my $c = shift;
+    $c->ua->get('mojolicio.us' => sub {
+      my ($ua, $tx) = @_;
+      $c->render(data => $tx->res->dom->at('title')->text);
+    });
+  };
+
+  # Concurrent non-blocking
+  get '/titles' => sub {
+    my $c = shift;
+    $c->delay(
+      sub {
+        my $delay = shift;
+        $c->ua->get('http://mojolicio.us'  => $delay->begin);
+        $c->ua->get('https://metacpan.org' => $delay->begin);
+      },
+      sub {
+        my ($delay, $mojo, $cpan) = @_;
+        $c->render(json => {
+          mojo => $mojo->res->dom->at('title')->text,
+          cpan => $cpan->res->dom->at('title')->text
+        });
+      }
+    );
+  };
+
+  app->start;
+
+For more information about the user agent see also
+L<Mojolicious::Guides::Cookbook/"USER AGENT">.
+
+=head2 WebSockets
+
+WebSocket applications have never been this simple before. Just receive
+messages by subscribing to events such as
+L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on">
+and return them with L<Mojolicious::Controller/"send">.
+
+  use Mojolicious::Lite;
+
+  websocket '/echo' => sub {
+    my $c = shift;
+    $c->on(json => sub {
+      my ($c, $hash) = @_;
+      $hash->{msg} = "echo: $hash->{msg}";
+      $c->send({json => $hash});
+    });
+  };
+
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  <!DOCTYPE html>
+  <html>
+    <head>
+      <title>Echo</title>
+      <script>
+        var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
+        ws.onmessage = function (event) {
+          document.body.innerHTML += JSON.parse(event.data).msg;
+        };
+        ws.onopen = function (event) {
+          ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'}));
+        };
+      </script>
+    </head>
+  </html>
+
+For more information about real-time web features see also
+L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">.
+
+=head2 Mode
+
+You can use the L<Mojo::Log> object from L<Mojo/"log"> to portably collect
+debug messages and automatically disable them later in a production setup by
+changing the L<Mojolicious> operating mode, which can also be retrieved from
+the attribute L<Mojolicious/"mode">.
+
+  use Mojolicious::Lite;
+
+  # Prepare mode specific message during startup
+  my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->app->log->debug('Rendering mode specific message');
+    $c->render(text => $msg);
+  };
+
+  app->log->debug('Starting application');
+  app->start;
+
+The default operating mode will usually be C<development> and can be changed
+with command line options or the C<MOJO_MODE> and C<PLACK_ENV> environment
+variables. A mode other than C<development> will raise the log level from
+C<debug> to C<info>.
+
+  $ ./myapp.pl daemon -m production
+
+All messages will be written to C<STDERR> or a C<log/$mode.log> file if a
+C<log> directory exists.
+
+  $ mkdir log
+
+Mode changes also affect a few other aspects of the framework, such as mode
+specific C<exception> and C<not_found> templates.
+
+=head2 Testing
+
+Testing your application is as easy as creating a C<t> directory and filling
+it with normal Perl tests, which can be a lot of fun thanks to L<Test::Mojo>.
+
+  use Test::More;
+  use Test::Mojo;
+
+  use FindBin;
+  require "$FindBin::Bin/../myapp.pl";
+
+  my $t = Test::Mojo->new;
+  $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
+
+  done_testing();
+
+Run all tests with the command L<Mojolicious::Command::test>.
+
+  $ ./myapp.pl test
+  $ ./myapp.pl test -v
+
+=head1 MORE
+
+You can continue with L<Mojolicious::Guides> now or take a look at the
+L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot
+more documentation and examples by many different authors.
+
+=head1 SUPPORT
+
+If you have any questions the documentation might not yet answer, don't
+hesitate to ask on the
+L<mailing-list|http://groups.google.com/group/mojolicious> or the official IRC
+channel C<#mojo> on C<irc.perl.org>.
+
+=cut
@@ -29,7 +29,10 @@ L<learn.perl.org|http://learn.perl.org/>.
 
 All web development starts with HTML, CSS and JavaScript, to learn the basics
 we recommend the
-L<Mozilla Developer Network|https://developer.mozilla.org/en-US/docs/Web>.
+L<Mozilla Developer Network|https://developer.mozilla.org/en-US/docs/Web>. And
+if you want to know more about how browsers and web servers actually
+communicate, there's also a very nice introduction to
+L<HTTP|https://developer.mozilla.org/en-US/docs/Web/HTTP>.
 
 =back
 
@@ -37,14 +40,14 @@ L<Mozilla Developer Network|https://developer.mozilla.org/en-US/docs/Web>.
 
 =over 2
 
-=item L<Mojolicious::Lite>
+=item L<Mojolicious::Guides::Tutorial>
 
-A fast and fun way to get started developing web applications with Mojolicious
-is the L<Mojolicious::Lite> tutorial. This micro web framework is only a thin
-wrapper around the normal web framework, so almost everything you learn here
-also applies to full L<Mojolicious> applications. The simplified notation
-introduced in the tutorial is commonly used throughout the guides and is
-therefore considered a prerequisite, you should definitely take a look!
+A fast and fun way to get started developing web applications with
+L<Mojolicious>. The tutorial introduces the L<Mojolicious::Lite> micro web
+framework, which is only a thin wrapper around the full web framework. The
+simplified notation introduced in the tutorial is commonly used throughout the
+guides and is therefore considered a prerequisite, you should definitely take
+a look!
 
 =back
 
@@ -62,7 +62,7 @@ sub import {
 
 =head1 NAME
 
-Mojolicious::Lite - Real-time micro web framework
+Mojolicious::Lite - Micro real-time web framework
 
 =head1 SYNOPSIS
 
@@ -84,936 +84,7 @@ Mojolicious::Lite - Real-time micro web framework
 L<Mojolicious::Lite> is a micro real-time web framework built around
 L<Mojolicious>.
 
-=head1 TUTORIAL
-
-A quick example driven introduction to the wonders of L<Mojolicious::Lite>.
-Most of what you'll learn here also applies to full L<Mojolicious>
-applications.
-
-=head2 Hello World
-
-A simple Hello World application can look like this, L<strict>, L<warnings>,
-L<utf8> and Perl 5.10 features are automatically enabled and a few
-L</"FUNCTIONS"> imported when you use L<Mojolicious::Lite>, turning your
-script into a full featured web application.
-
-  #!/usr/bin/env perl
-  use Mojolicious::Lite;
-
-  get '/' => sub {
-    my $c = shift;
-    $c->render(text => 'Hello World!');
-  };
-
-  app->start;
-
-There is also a helper command to generate a small example application.
-
-  $ mojo generate lite_app myapp.pl
-
-=head2 Commands
-
-All the normal L<Mojolicious::Commands> are available from the command line.
-Note that CGI and L<PSGI> environments can usually be auto detected and will
-just work without commands.
-
-  $ ./myapp.pl daemon
-  Server available at http://127.0.0.1:3000.
-
-  $ ./myapp.pl daemon -l http://*:8080
-  Server available at http://127.0.0.1:8080.
-
-  $ ./myapp.pl cgi
-  ...CGI output...
-
-  $ ./myapp.pl get /
-  Hello World!
-
-  $ ./myapp.pl
-  ...List of available commands (or automatically detected environment)...
-
-The C<app-E<gt>start> call that starts the L<Mojolicious> command system
-should usually be the last expression in your application and can be
-customized to override normal C<@ARGV> use.
-
-  app->start('cgi');
-
-=head2 Reloading
-
-Your application will automatically reload itself if you start it with the
-C<morbo> development web server, so you don't have to restart the server after
-every change.
-
-  $ morbo ./myapp.pl
-  Server available at http://127.0.0.1:3000.
-
-For more information about how to deploy your application see also
-L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">.
-
-=head2 Routes
-
-Routes are basically just fancy paths that can contain different kinds of
-placeholders and usually lead to an action. The first argument passed to all
-actions C<$c> is a L<Mojolicious::Controller> object containing both the HTTP
-request and response.
-
-  use Mojolicious::Lite;
-
-  # Route leading to an action
-  get '/foo' => sub {
-    my $c = shift;
-    $c->render(text => 'Hello World!');
-  };
-
-  app->start;
-
-Response content is often generated by actions with
-L<Mojolicious::Controller/"render">, but more about that later.
-
-=head2 GET/POST parameters
-
-All C<GET> and C<POST> parameters sent with the request are accessible via
-L<Mojolicious::Controller/"param">.
-
-  use Mojolicious::Lite;
-
-  # /foo?user=sri
-  get '/foo' => sub {
-    my $c    = shift;
-    my $user = $c->param('user');
-    $c->render(text => "Hello $user.");
-  };
-
-  app->start;
-
-=head2 Stash and templates
-
-The L<Mojolicious::Controller/"stash"> is used to pass data to templates,
-which can be inlined in the C<DATA> section.
-
-  use Mojolicious::Lite;
-
-  # Route leading to an action that renders a template
-  get '/bar' => sub {
-    my $c = shift;
-    $c->stash(one => 23);
-    $c->render('baz', two => 24);
-  };
-
-  app->start;
-  __DATA__
-
-  @@ baz.html.ep
-  The magic numbers are <%= $one %> and <%= $two %>.
-
-For more information about templates see also
-L<Mojolicious::Guides::Rendering/"Embedded Perl">.
-
-=head2 HTTP
-
-L<Mojolicious::Controller/"req"> and L<Mojolicious::Controller/"res"> give you
-full access to all HTTP features and information.
-
-  use Mojolicious::Lite;
-
-  # Access request information
-  get '/agent' => sub {
-    my $c    = shift;
-    my $host = $c->req->url->to_abs->host;
-    my $ua   = $c->req->headers->user_agent;
-    $c->render(text => "Request by $ua reached $host.");
-  };
-
-  # Echo the request body and send custom header with response
-  post '/echo' => sub {
-    my $c = shift;
-    $c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
-    $c->render(data => $c->req->body);
-  };
-
-  app->start;
-
-You can test the more advanced examples right from the command line with
-L<Mojolicious::Command::get>.
-
-  $ ./myapp.pl get -v -M POST -c 'test' /echo
-
-=head2 Built-in C<exception> and C<not_found> pages
-
-During development you will encounter these pages whenever you make a mistake,
-they are gorgeous and contain a lot of valuable information that will aid you
-in debugging your application.
-
-  use Mojolicious::Lite;
-
-  # Not found (404)
-  get '/missing' => sub { shift->render('does_not_exist') };
-
-  # Exception (500)
-  get '/dies' => sub { die 'Intentional error' };
-
-  app->start;
-
-You can even use CSS selectors with L<Mojolicious::Command::get> to extract
-only the information you're actually interested in.
-
-  $ ./myapp.pl get /dies '#error'
-
-=head2 Route names
-
-All routes can have a name associated with them, this allows automatic
-template detection and backreferencing with
-L<Mojolicious::Controller/"url_for">, on which many methods and helpers like
-L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
-
-  use Mojolicious::Lite;
-
-  # Render the template "index.html.ep"
-  get '/' => sub {
-    my $c = shift;
-    $c->render;
-  } => 'index';
-
-  # Render the template "hello.html.ep"
-  get '/hello';
-
-  app->start;
-  __DATA__
-
-  @@ index.html.ep
-  <%= link_to Hello  => 'hello' %>.
-  <%= link_to Reload => 'index' %>.
-
-  @@ hello.html.ep
-  Hello World!
-
-Nameless routes get an automatically generated one assigned that is simply
-equal to the route itself without non-word characters.
-
-=head2 Layouts
-
-Templates can have layouts too, you just select one with the helper
-L<Mojolicious::Plugin::DefaultHelpers/"layout"> and place the result of the
-current template with the helper
-L<Mojolicious::Plugin::DefaultHelpers/"content">.
-
-  use Mojolicious::Lite;
-
-  get '/with_layout';
-
-  app->start;
-  __DATA__
-
-  @@ with_layout.html.ep
-  % title 'Green';
-  % layout 'green';
-  Hello World!
-
-  @@ layouts/green.html.ep
-  <!DOCTYPE html>
-  <html>
-    <head><title><%= title %></title></head>
-    <body><%= content %></body>
-  </html>
-
-The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can
-be used to pass additional data to the layout.
-
-=head2 Blocks
-
-Template blocks can be used like normal Perl functions and are always
-delimited by the C<begin> and C<end> keywords, they are the foundation for
-many helpers.
-
-  use Mojolicious::Lite;
-
-  get '/with_block' => 'block';
-
-  app->start;
-  __DATA__
-
-  @@ block.html.ep
-  % my $link = begin
-    % my ($url, $name) = @_;
-    Try <%= link_to $url => begin %><%= $name %><% end %>.
-  % end
-  <!DOCTYPE html>
-  <html>
-    <head><title>Sebastians frameworks</title></head>
-    <body>
-      %= $link->('http://mojolicio.us', 'Mojolicious')
-      %= $link->('http://catalystframework.org', 'Catalyst')
-    </body>
-  </html>
-
-=head2 Helpers
-
-Helpers are little functions you can reuse throughout your whole application,
-from actions to templates.
-
-  use Mojolicious::Lite;
-
-  # A helper to identify visitors
-  helper whois => sub {
-    my $c     = shift;
-    my $agent = $c->req->headers->user_agent || 'Anonymous';
-    my $ip    = $c->tx->remote_address;
-    return "$agent ($ip)";
-  };
-
-  # Use helper in action and template
-  get '/secret' => sub {
-    my $c    = shift;
-    my $user = $c->whois;
-    $c->app->log->debug("Request from $user.");
-  };
-
-  app->start;
-  __DATA__
-
-  @@ secret.html.ep
-  We know who you are <%= whois %>.
-
-A list of all built-in ones can be found in
-L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
-
-=head2 Placeholders
-
-Route placeholders allow capturing parts of a request path until a C</> or
-C<.> separator occurs, results are accessible via
-L<Mojolicious::Controller/"stash"> and L<Mojolicious::Controller/"param">.
-
-  use Mojolicious::Lite;
-
-  # /foo/test
-  # /foo/test123
-  get '/foo/:bar' => sub {
-    my $c   = shift;
-    my $bar = $c->stash('bar');
-    $c->render(text => "Our :bar placeholder matched $bar");
-  };
-
-  # /testsomething/foo
-  # /test123something/foo
-  get '/(:bar)something/foo' => sub {
-    my $c   = shift;
-    my $bar = $c->param('bar');
-    $c->render(text => "Our :bar placeholder matched $bar");
-  };
-
-  app->start;
-
-=head2 Relaxed Placeholders
-
-Relaxed placeholders allow matching of everything until a C</> occurs.
-
-  use Mojolicious::Lite;
-
-  # /test/hello
-  # /test123/hello
-  # /test.123/hello
-  get '/#you/hello' => 'groovy';
-
-  app->start;
-  __DATA__
-
-  @@ groovy.html.ep
-  Your name is <%= $you %>.
-
-=head2 Wildcard placeholders
-
-Wildcard placeholders allow matching absolutely everything, including C</> and
-C<.>.
-
-  use Mojolicious::Lite;
-
-  # /hello/test
-  # /hello/test123
-  # /hello/test.123/test/123
-  get '/hello/*you' => 'groovy';
-
-  app->start;
-  __DATA__
-
-  @@ groovy.html.ep
-  Your name is <%= $you %>.
-
-=head2 HTTP methods
-
-Routes can be restricted to specific request methods with different keywords.
-
-  use Mojolicious::Lite;
-
-  # GET /hello
-  get '/hello' => sub {
-    my $c = shift;
-    $c->render(text => 'Hello World!');
-  };
-
-  # PUT /hello
-  put '/hello' => sub {
-    my $c    = shift;
-    my $size = length $c->req->body;
-    $c->render(text => "You uploaded $size bytes to /hello.");
-  };
-
-  # GET|POST|PATCH /bye
-  any [qw(GET POST PATCH)] => '/bye' => sub {
-    my $c = shift;
-    $c->render(text => 'Bye World!');
-  };
-
-  # * /whatever
-  any '/whatever' => sub {
-    my $c      = shift;
-    my $method = $c->req->method;
-    $c->render(text => "You called /whatever with $method.");
-  };
-
-  app->start;
-
-=head2 Optional placeholders
-
-All placeholders require a value, but by assigning them default values you can
-make capturing optional.
-
-  use Mojolicious::Lite;
-
-  # /hello
-  # /hello/Sara
-  get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub {
-    my $c = shift;
-    $c->render('groovy', format => 'txt');
-  };
-
-  app->start;
-  __DATA__
-
-  @@ groovy.txt.ep
-  My name is <%= $name %> and it is <%= $day %>.
-
-Default values that don't belong to a placeholder simply get merged into the
-stash all the time.
-
-=head2 Restrictive placeholders
-
-The easiest way to make placeholders more restrictive are alternatives, you
-just make a list of possible values.
-
-  use Mojolicious::Lite;
-
-  # /test
-  # /123
-  any '/:foo' => [foo => [qw(test 123)]] => sub {
-    my $c   = shift;
-    my $foo = $c->param('foo');
-    $c->render(text => "Our :foo placeholder matched $foo");
-  };
-
-  app->start;
-
-All placeholders get compiled to a regular expression internally, this process
-can also be easily customized.
-
-  use Mojolicious::Lite;
-
-  # /1
-  # /123
-  any '/:bar' => [bar => qr/\d+/] => sub {
-    my $c   = shift;
-    my $bar = $c->param('bar');
-    $c->render(text => "Our :bar placeholder matched $bar");
-  };
-
-  app->start;
-
-Just make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
-placeholders become part of a larger regular expression internally, C<(?:...)>
-is fine though.
-
-=head2 Under
-
-Authentication and code shared between multiple routes can be realized easily
-with routes generated by the L</"under"> statement. All following routes are
-only evaluated if the callback returned a true value.
-
-  use Mojolicious::Lite;
-
-  # Authenticate based on name parameter
-  under sub {
-    my $c = shift;
-
-    # Authenticated
-    my $name = $c->param('name') || '';
-    return 1 if $name eq 'Bender';
-
-    # Not authenticated
-    $c->render('denied');
-    return undef;
-  };
-
-  # Only reached when authenticated
-  get '/' => 'index';
-
-  app->start;
-  __DATA__
-
-  @@ denied.html.ep
-  You are not Bender, permission denied.
-
-  @@ index.html.ep
-  Hi Bender.
-
-Prefixing multiple routes is another good use for L</"under">.
-
-  use Mojolicious::Lite;
-
-  # /foo
-  under '/foo';
-
-  # /foo/bar
-  get '/bar' => {text => 'foo bar'};
-
-  # /foo/baz
-  get '/baz' => {text => 'foo baz'};
-
-  # / (reset)
-  under '/' => {msg => 'whatever'};
-
-  # /bar
-  get '/bar' => {inline => '<%= $msg %> works'};
-
-  app->start;
-
-You can also L</"group"> related routes, which allows nesting of multiple
-L</"under"> statements.
-
-  use Mojolicious::Lite;
-
-  # Global logic shared by all routes
-  under sub {
-    my $c = shift;
-    return 1 if $c->req->headers->header('X-Bender');
-    $c->render(text => "You're not Bender.");
-    return undef;
-  };
-
-  # Admin section
-  group {
-
-    # Local logic shared only by routes in this group
-    under '/admin' => sub {
-      my $c = shift;
-      return 1 if $c->req->headers->header('X-Awesome');
-      $c->render(text => "You're not awesome enough.");
-      return undef;
-    };
-
-    # GET /admin/dashboard
-    get '/dashboard' => {text => 'Nothing to see here yet.'};
-  };
-
-  # GET /welcome
-  get '/welcome' => {text => 'Hi Bender.'};
-
-  app->start;
-
-=head2 Formats
-
-Formats can be automatically detected from file extensions, they are used to
-find the right template and generate the correct C<Content-Type> header.
-
-  use Mojolicious::Lite;
-
-  # /detection
-  # /detection.html
-  # /detection.txt
-  get '/detection' => sub {
-    my $c = shift;
-    $c->render('detected');
-  };
-
-  app->start;
-  __DATA__
-
-  @@ detected.html.ep
-  <!DOCTYPE html>
-  <html>
-    <head><title>Detected</title></head>
-    <body>HTML was detected.</body>
-  </html>
-
-  @@ detected.txt.ep
-  TXT was detected.
-
-The default format is C<html>, restrictive placeholders can be used to limit
-possible values.
-
-  use Mojolicious::Lite;
-
-  # /hello.json
-  # /hello.txt
-  get '/hello' => [format => [qw(json txt)]] => sub {
-    my $c = shift;
-    return $c->render(json => {hello => 'world'})
-      if $c->stash('format') eq 'json';
-    $c->render(text => 'hello world');
-  };
-
-  app->start;
-
-Or you can just disable format detection.
-
-  use Mojolicious::Lite;
-
-  # /hello
-  get '/hello' => [format => 0] => {text => 'No format detection.'};
-
-  # Disable detection and allow the following routes selective re-enabling
-  under [format => 0];
-
-  # /foo
-  get '/foo' => {text => 'No format detection again.'};
-
-  # /bar.txt
-  get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
-
-  app->start;
-
-=head2 Content negotiation
-
-For resources with different representations and that require truly RESTful
-content negotiation you can also use L<Mojolicious::Controller/"respond_to">.
-
-  use Mojolicious::Lite;
-
-  # /hello (Accept: application/json)
-  # /hello (Accept: application/xml)
-  # /hello.json
-  # /hello.xml
-  # /hello?format=json
-  # /hello?format=xml
-  get '/hello' => sub {
-    my $c = shift;
-    $c->respond_to(
-      json => {json => {hello => 'world'}},
-      xml  => {text => '<hello>world</hello>'},
-      any  => {data => '', status => 204}
-    );
-  };
-
-  app->start;
-
-MIME type mappings can be extended or changed easily with
-L<Mojolicious/"types">.
-
-  app->types->type(rdf => 'application/rdf+xml');
-
-=head2 Static files
-
-Similar to templates, but with only a single file extension and optional
-Base64 encoding, static files can be inlined in the C<DATA> section and are
-served automatically.
-
-  use Mojolicious::Lite;
-
-  app->start;
-  __DATA__
-
-  @@ something.js
-  alert('hello!');
-
-  @@ test.txt (base64)
-  dGVzdCAxMjMKbGFsYWxh
-
-External static files are not limited to a single file extension and will be
-served automatically from a C<public> directory if it exists.
-
-  $ mkdir public
-  $ mv something.js public/something.js
-  $ mv mojolicious.tar.gz public/mojolicious.tar.gz
-
-Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
-Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
-headers is supported as well and can be tested very easily with
-L<Mojolicious::Command::get>.
-
-  $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
-
-=head2 External templates
-
-External templates will be searched by the renderer in a C<templates>
-directory if it exists and have a higher precedence than those in the C<DATA>
-section.
-
-  use Mojolicious::Lite;
-
-  # Render template "templates/foo/bar.html.ep"
-  any '/external' => sub {
-    my $c = shift;
-    $c->render('foo/bar');
-  };
-
-  app->start;
-
-=head2 Conditions
-
-Conditions such as C<agent> and C<host> from
-L<Mojolicious::Plugin::HeaderCondition> allow even more powerful route
-constructs.
-
-  use Mojolicious::Lite;
-
-  # Firefox
-  get '/foo' => (agent => qr/Firefox/) => sub {
-    my $c = shift;
-    $c->render(text => 'Congratulations, you are using a cool browser.');
-  };
-
-  # Internet Explorer
-  get '/foo' => (agent => qr/Internet Explorer/) => sub {
-    my $c = shift;
-    $c->render(text => 'Dude, you really need to upgrade to Firefox.');
-  };
-
-  # http://mojolicio.us/bar
-  get '/bar' => (host => 'mojolicio.us') => sub {
-    my $c = shift;
-    $c->render(text => 'Hello Mojolicious.');
-  };
-
-  app->start;
-
-=head2 Sessions
-
-Signed cookie based sessions just work out of the box as soon as you start
-using them through the helper
-L<Mojolicious::Plugin::DefaultHelpers/"session">, just be aware that all
-session data gets serialized with L<Mojo::JSON>.
-
-  use Mojolicious::Lite;
-
-  # Access session data in action and template
-  get '/counter' => sub {
-    my $c = shift;
-    $c->session->{counter}++;
-  };
-
-  app->start;
-  __DATA__
-
-  @@ counter.html.ep
-  Counter: <%= session 'counter' %>
-
-Note that you should use custom L<Mojolicious/"secrets"> to make signed
-cookies really secure.
-
-  app->secrets(['My secret passphrase here']);
-
-=head2 File uploads
-
-All files uploaded via C<multipart/form-data> request are automatically
-available as L<Mojo::Upload> objects. And you don't have to worry about memory
-usage, because all files above 250KB will be automatically streamed into a
-temporary file.
-
-  use Mojolicious::Lite;
-
-  # Upload form in DATA section
-  get '/' => 'form';
-
-  # Multipart upload handler
-  post '/upload' => sub {
-    my $c = shift;
-
-    # Check file size
-    return $c->render(text => 'File is too big.', status => 200)
-      if $c->req->is_limit_exceeded;
-
-    # Process uploaded file
-    return $c->redirect_to('form') unless my $example = $c->param('example');
-    my $size = $example->size;
-    my $name = $example->filename;
-    $c->render(text => "Thanks for uploading $size byte file $name.");
-  };
-
-  app->start;
-  __DATA__
-
-  @@ form.html.ep
-  <!DOCTYPE html>
-  <html>
-    <head><title>Upload</title></head>
-    <body>
-      %= form_for upload => (enctype => 'multipart/form-data') => begin
-        %= file_field 'example'
-        %= submit_button 'Upload'
-      % end
-    </body>
-  </html>
-
-To protect you from excessively large files there is also a limit of 10MB by
-default, which you can tweak with the attribute
-L<Mojo::Message/"max_message_size"> or C<MOJO_MAX_MESSAGE_SIZE> environment
-variable.
-
-  # Increase limit to 1GB
-  $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824;
-
-=head2 User agent
-
-With L<Mojo::UserAgent>, which is available through the helper
-L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and
-WebSocket user agent built right in. Especially in combination with
-L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
-
-  use Mojolicious::Lite;
-
-  # Blocking
-  get '/headers' => sub {
-    my $c   = shift;
-    my $url = $c->param('url') || 'http://mojolicio.us';
-    my $dom = $c->ua->get($url)->res->dom;
-    $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
-  };
-
-  # Non-blocking
-  get '/title' => sub {
-    my $c = shift;
-    $c->ua->get('mojolicio.us' => sub {
-      my ($ua, $tx) = @_;
-      $c->render(data => $tx->res->dom->at('title')->text);
-    });
-  };
-
-  # Concurrent non-blocking
-  get '/titles' => sub {
-    my $c = shift;
-    $c->delay(
-      sub {
-        my $delay = shift;
-        $c->ua->get('http://mojolicio.us'  => $delay->begin);
-        $c->ua->get('https://metacpan.org' => $delay->begin);
-      },
-      sub {
-        my ($delay, $mojo, $cpan) = @_;
-        $c->render(json => {
-          mojo => $mojo->res->dom->at('title')->text,
-          cpan => $cpan->res->dom->at('title')->text
-        });
-      }
-    );
-  };
-
-  app->start;
-
-For more information about the user agent see also
-L<Mojolicious::Guides::Cookbook/"USER AGENT">.
-
-=head2 WebSockets
-
-WebSocket applications have never been this simple before. Just receive
-messages by subscribing to events such as
-L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on">
-and return them with L<Mojolicious::Controller/"send">.
-
-  use Mojolicious::Lite;
-
-  websocket '/echo' => sub {
-    my $c = shift;
-    $c->on(json => sub {
-      my ($c, $hash) = @_;
-      $hash->{msg} = "echo: $hash->{msg}";
-      $c->send({json => $hash});
-    });
-  };
-
-  get '/' => 'index';
-
-  app->start;
-  __DATA__
-
-  @@ index.html.ep
-  <!DOCTYPE html>
-  <html>
-    <head>
-      <title>Echo</title>
-      <script>
-        var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
-        ws.onmessage = function (event) {
-          document.body.innerHTML += JSON.parse(event.data).msg;
-        };
-        ws.onopen = function (event) {
-          ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'}));
-        };
-      </script>
-    </head>
-  </html>
-
-For more information about real-time web features see also
-L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">.
-
-=head2 Mode
-
-You can use the L<Mojo::Log> object from L<Mojo/"log"> to portably collect
-debug messages and automatically disable them later in a production setup by
-changing the L<Mojolicious> operating mode, which can also be retrieved from
-the attribute L<Mojolicious/"mode">.
-
-  use Mojolicious::Lite;
-
-  # Prepare mode specific message during startup
-  my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!';
-
-  get '/' => sub {
-    my $c = shift;
-    $c->app->log->debug('Rendering mode specific message.');
-    $c->render(text => $msg);
-  };
-
-  app->log->debug('Starting application.');
-  app->start;
-
-The default operating mode will usually be C<development> and can be changed
-with command line options or the C<MOJO_MODE> and C<PLACK_ENV> environment
-variables. A mode other than C<development> will raise the log level from
-C<debug> to C<info>.
-
-  $ ./myapp.pl daemon -m production
-
-All messages will be written to C<STDERR> or a C<log/$mode.log> file if a
-C<log> directory exists.
-
-  $ mkdir log
-
-Mode changes also affect a few other aspects of the framework, such as mode
-specific C<exception> and C<not_found> templates.
-
-=head2 Testing
-
-Testing your application is as easy as creating a C<t> directory and filling
-it with normal Perl tests, which can be a lot of fun thanks to L<Test::Mojo>.
-
-  use Test::More;
-  use Test::Mojo;
-
-  use FindBin;
-  require "$FindBin::Bin/../myapp.pl";
-
-  my $t = Test::Mojo->new;
-  $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
-
-  done_testing();
-
-Run all tests with the command L<Mojolicious::Command::test>.
-
-  $ ./myapp.pl test
-  $ ./myapp.pl test -v
-
-=head2 More
-
-You can continue with L<Mojolicious::Guides> now, and don't forget to have
-fun!
+See L<Mojolicious::Guides::Tutorial> for more!
 
 =head1 FUNCTIONS
 
@@ -1029,8 +100,8 @@ automatically exported.
   my $route = any [qw(GET POST)] => '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"any">, matching any of the
-listed HTTP request methods or all. See also the tutorial above for many more
-argument variations.
+listed HTTP request methods or all. See also L<Mojolicious::Guides::Tutorial>
+for many more argument variations.
 
 =head2 app
 
@@ -1050,8 +121,8 @@ L<Mojolicious>.
   my $route = del '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"delete">, matching only
-C<DELETE> requests. See also the tutorial above for many more argument
-variations.
+C<DELETE> requests. See also L<Mojolicious::Guides::Tutorial> for many more
+argument variations.
 
 =head2 get
 
@@ -1060,7 +131,8 @@ variations.
   my $route = get '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"get">, matching only C<GET>
-requests. See also the tutorial above for many more argument variations.
+requests. See also L<Mojolicious::Guides::Tutorial> for many more argument
+variations.
 
 =head2 group
 
@@ -1087,8 +159,8 @@ Share code with L<Mojolicious/"hook">.
   my $route = options '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"options">, matching only
-C<OPTIONS> requests. See also the tutorial above for many more argument
-variations.
+C<OPTIONS> requests. See also L<Mojolicious::Guides::Tutorial> for many more
+argument variations.
 
 =head2 patch
 
@@ -1097,8 +169,8 @@ variations.
   my $route = patch '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"patch">, matching only
-C<PATCH> requests. See also the tutorial above for many more argument
-variations.
+C<PATCH> requests. See also L<Mojolicious::Guides::Tutorial> for many more
+argument variations.
 
 =head2 plugin
 
@@ -1113,8 +185,8 @@ Load a plugin with L<Mojolicious/"plugin">.
   my $route = post '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"post">, matching only
-C<POST> requests. See also the tutorial above for many more argument
-variations.
+C<POST> requests. See also L<Mojolicious::Guides::Tutorial> for many more
+argument variations.
 
 =head2 put
 
@@ -1123,7 +195,8 @@ variations.
   my $route = put '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"put">, matching only C<PUT>
-requests. See also the tutorial above for many more argument variations.
+requests. See also L<Mojolicious::Guides::Tutorial> for many more argument
+variations.
 
 =head2 under
 
@@ -1134,8 +207,8 @@ requests. See also the tutorial above for many more argument variations.
   my $route = under [format => 0];
 
 Generate nested route with L<Mojolicious::Routes::Route/"under">, to which all
-following routes are automatically appended. See also the tutorial above for
-more argument variations.
+following routes are automatically appended. See also
+L<Mojolicious::Guides::Tutorial> for more argument variations.
 
 =head2 websocket
 
@@ -1144,8 +217,8 @@ more argument variations.
   my $route = websocket '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"websocket">, matching only
-WebSocket handshakes. See also the tutorial above for many more argument
-variations.
+WebSocket handshakes. See also L<Mojolicious::Guides::Tutorial> for many more
+argument variations.
 
 =head1 ATTRIBUTES
 
@@ -6,7 +6,7 @@ use Mojo::Util qw(decode slurp);
 
 sub load {
   my ($self, $file, $conf, $app) = @_;
-  $app->log->debug(qq{Reading configuration file "$file".});
+  $app->log->debug(qq{Reading configuration file "$file"});
   return $self->parse(decode('UTF-8', slurp $file), $file, $conf, $app);
 }
 
@@ -5,11 +5,15 @@ use Mojo::ByteStream;
 use Mojo::Collection;
 use Mojo::Exception;
 use Mojo::IOLoop;
-use Mojo::Util qw(dumper sha1_sum steady_time);
+use Mojo::Util qw(deprecated dumper sha1_sum steady_time);
 
 sub register {
   my ($self, $app) = @_;
 
+  # DEPRECATED in Tiger Face!
+  $app->helper(render_exception => sub { _render('exception', @_) });
+  $app->helper(render_not_found => sub { _render('not_found', @_) });
+
   # Controller alias helpers
   for my $name (qw(app flash param stash session url_for validation)) {
     $app->helper($name => sub { shift->$name(@_) });
@@ -80,12 +84,20 @@ sub _delay {
   my $c     = shift;
   my $tx    = $c->render_later->tx;
   my $delay = Mojo::IOLoop->delay(@_);
-  $delay->catch(sub { $c->render_exception(pop) and undef $tx })->wait;
+  $delay->catch(sub { $c->helpers->reply->exception(pop) and undef $tx })
+    ->wait;
 }
 
 sub _development {
   my ($page, $c, $e) = @_;
 
+  # DEPRECATED in Tiger Face!
+  if (my $sub = $c->can("render_$page")) {
+    deprecated "Mojolicious::Controller::render_$page is DEPRECATED in favor"
+      . " of the reply->$page helper";
+    return $c->$sub($page eq 'exception' ? $e : ());
+  }
+
   my $app = $c->app;
   $app->log->error($e = Mojo::Exception->new($e)) if $page eq 'exception';
 
@@ -137,11 +149,19 @@ sub _is_fresh {
   return $c->app->static->is_fresh($c, \%options);
 }
 
+# DEPRECATED in Tiger Face!
+sub _render {
+  my $page = shift;
+  deprecated "Mojolicious::Controller::render_$page is DEPRECATED in favor of"
+    . " the reply->$page helper";
+  shift->helpers->reply->$page(@_);
+}
+
 sub _static {
   my ($c, $file) = @_;
   return !!$c->rendered if $c->app->static->serve($c, $file);
-  $c->app->log->debug(qq{File "$file" not found, public directory missing?});
-  return !$c->render_not_found;
+  $c->app->log->debug(qq{Static file "$file" not found});
+  return !$c->helpers->reply->not_found;
 }
 
 sub _url_with {
@@ -13,7 +13,7 @@ sub _epl {
   my $mt = delete $options->{'mojo.template'} || Mojo::Template->new;
   my $log = $c->app->log;
   if ($mt->compiled) {
-    $log->debug("Rendering cached @{[$mt->name]}.");
+    $log->debug("Rendering cached @{[$mt->name]}");
     $$output = $mt->interpret($c);
   }
 
@@ -25,7 +25,7 @@ sub _epl {
 
     # Inline
     if (defined $inline) {
-      $log->debug(qq{Rendering inline template "$name".});
+      $log->debug(qq{Rendering inline template "$name"});
       $$output = $mt->name(qq{inline template "$name"})->render($inline, $c);
     }
 
@@ -35,19 +35,19 @@ sub _epl {
 
       # Try template
       if (defined(my $path = $renderer->template_path($options))) {
-        $log->debug(qq{Rendering template "$name".});
+        $log->debug(qq{Rendering template "$name"});
         $$output = $mt->name(qq{template "$name"})->render_file($path, $c);
       }
 
       # Try DATA section
       elsif (my $d = $renderer->get_data_template($options)) {
-        $log->debug(qq{Rendering template "$name" from DATA section.});
+        $log->debug(qq{Rendering template "$name" from DATA section});
         $$output
           = $mt->name(qq{template "$name" from DATA section})->render($d, $c);
       }
 
       # No template
-      else { $log->debug(qq{Template "$name" not found.}) and return undef }
+      else { $log->debug(qq{Template "$name" not found}) and return undef }
     }
   }
 
@@ -2,7 +2,7 @@ package Mojolicious::Plugin::TagHelpers;
 use Mojo::Base 'Mojolicious::Plugin';
 
 use Mojo::ByteStream;
-use Mojo::Util 'xss_escape';
+use Mojo::DOM::HTML;
 use Scalar::Util 'blessed';
 
 sub register {
@@ -84,29 +84,22 @@ sub _input {
     my $value = $attrs{value} // '';
     if ($type eq 'checkbox' || $type eq 'radio') {
       $attrs{value} = $value;
-      $attrs{checked} = 'checked' if grep { $_ eq $value } @values;
+      $attrs{checked} = undef if grep { $_ eq $value } @values;
     }
 
     # Others
     else { $attrs{value} = $values[0] }
   }
 
-  return _validation($c, $name, 'input', %attrs, name => $name);
+  return _validation($c, $name, 'input', name => $name, %attrs);
 }
 
 sub _javascript {
   my $c = shift;
-
-  # CDATA
-  my $cb = sub {''};
-  if (ref $_[-1] eq 'CODE' && (my $old = pop)) {
-    $cb = sub { "//<![CDATA[\n" . $old->() . "\n//]]>" }
-  }
-
-  # URL
-  my $src = @_ % 2 ? $c->url_for(shift) : undef;
-
-  return _tag('script', @_, $src ? (src => $src) : (), $cb);
+  my $content
+    = ref $_[-1] eq 'CODE' ? "//<![CDATA[\n" . pop->() . "\n//]]>" : '';
+  my @src = @_ % 2 ? (src => $c->url_for(shift)) : ();
+  return _tag('script', @src, @_, sub {$content});
 }
 
 sub _label_for {
@@ -134,18 +127,14 @@ sub _link_to {
 sub _option {
   my ($values, $pair) = @_;
   $pair = [$pair => $pair] unless ref $pair eq 'ARRAY';
-
-  # Attributes
   my %attrs = (value => $pair->[1]);
-  $attrs{selected} = 'selected' if exists $values->{$pair->[1]};
-  %attrs = (%attrs, @$pair[2 .. $#$pair]);
-
-  return _tag('option', %attrs, $pair->[0]);
+  $attrs{selected} = undef if exists $values->{$pair->[1]};
+  return _tag('option', %attrs, @$pair[2 .. $#$pair], $pair->[0]);
 }
 
 sub _password_field {
   my ($c, $name) = (shift, shift);
-  return _validation($c, $name, 'input', @_, name => $name,
+  return _validation($c, $name, 'input', name => $name, @_,
     type => 'password');
 }
 
@@ -168,24 +157,16 @@ sub _select_field {
     else { $groups .= _option(\%values, $group) }
   }
 
-  return _validation($c, $name, 'select', %attrs, name => $name,
+  return _validation($c, $name, 'select', name => $name, %attrs,
     sub {$groups});
 }
 
 sub _stylesheet {
   my $c = shift;
-
-  # CDATA
-  my $cb;
-  if (ref $_[-1] eq 'CODE' && (my $old = pop)) {
-    $cb = sub { "/*<![CDATA[*/\n" . $old->() . "\n/*]]>*/" }
-  }
-
-  # "link" or "style" tag
-  my $href = @_ % 2 ? $c->url_for(shift) : undef;
-  return $href
-    ? _tag('link', rel => 'stylesheet', href => $href, @_)
-    : _tag('style', @_, $cb);
+  my $content
+    = ref $_[-1] eq 'CODE' ? "/*<![CDATA[*/\n" . pop->() . "\n/*]]>*/" : '';
+  return _tag('style', @_, sub {$content}) unless @_ % 2;
+  return _tag('link', rel => 'stylesheet', href => $c->url_for(shift), @_);
 }
 
 sub _submit_button {
@@ -194,34 +175,23 @@ sub _submit_button {
 }
 
 sub _tag {
-  my $name = shift;
+  my $tree = ['tag', shift, undef, undef];
 
   # Content
-  my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
-  my $content = @_ % 2 ? pop : undef;
-
-  # Start tag
-  my $tag = "<$name";
+  if (ref $_[-1] eq 'CODE') { push @$tree, ['raw', pop->()] }
+  elsif (@_ % 2) { push @$tree, ['text', pop] }
 
   # Attributes
-  my %attrs = @_;
-  if ($attrs{data} && ref $attrs{data} eq 'HASH') {
-    while (my ($key, $value) = each %{$attrs{data}}) {
+  my $attrs = $tree->[2] = {@_};
+  if ($attrs->{data} && ref $attrs->{data} eq 'HASH') {
+    while (my ($key, $value) = each %{$attrs->{data}}) {
       $key =~ y/_/-/;
-      $attrs{lc("data-$key")} = $value;
+      $attrs->{lc "data-$key"} = $value;
     }
-    delete $attrs{data};
+    delete $attrs->{data};
   }
-  $tag .= qq{ $_="} . xss_escape($attrs{$_} // '') . '"' for sort keys %attrs;
-
-  # Empty element
-  unless ($cb || defined $content) { $tag .= ' />' }
-
-  # End tag
-  else { $tag .= '>' . ($cb ? $cb->() : xss_escape $content) . "</$name>" }
 
-  # Prevent escaping
-  return Mojo::ByteStream->new($tag);
+  return Mojo::ByteStream->new(Mojo::DOM::HTML::_render($tree));
 }
 
 sub _tag_with_error {
@@ -238,7 +208,7 @@ sub _text_area {
   my $content = @_ % 2 ? shift : undef;
   $content = $c->param($name) // $content // $cb // '';
 
-  return _validation($c, $name, 'textarea', @_, name => $name, $content);
+  return _validation($c, $name, 'textarea', name => $name, @_, $content);
 }
 
 sub _validation {
@@ -279,10 +249,10 @@ necessary attributes always be generated automatically.
   <%= radio_button country => 'uk'      %> UK
 
 For fields that failed validation with L<Mojolicious::Controller/"validation">
-the C<field-with-error> class will be automatically added through the
-C<tag_with_error> helper, to make styling with CSS easier.
+the C<field-with-error> class will be automatically added through
+L</"tag_with_error">, to make styling with CSS easier.
 
-  <input class="field-with-error" name="age" type="text" value="250" />
+  <input class="field-with-error" name="age" type="text" value="250">
 
 This is a core plugin, that means it is always enabled and its code a good
 example for learning how to build new plugins, you're welcome to fork it.
@@ -302,8 +272,8 @@ L<Mojolicious::Plugin::TagHelpers> implements the following helpers.
 Generate C<input> tag of type C<checkbox>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="employed" type="checkbox" value="1" />
-  <input disabled="disabled" name="employed" type="checkbox" value="1" />
+  <input name="employed" type="checkbox" value="1">
+  <input disabled="disabled" name="employed" type="checkbox" value="1">
 
 =head2 color_field
 
@@ -314,9 +284,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<color>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="background" type="color" />
-  <input name="background" type="color" value="#ffffff" />
-  <input id="foo" name="background" type="color" value="#ffffff" />
+  <input name="background" type="color">
+  <input name="background" type="color" value="#ffffff">
+  <input id="foo" name="background" type="color" value="#ffffff">
 
 =head2 csrf_field
 
@@ -325,7 +295,7 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<hidden> with
 L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">.
 
-  <input name="csrf_token" type="hidden" value="fa6a08..." />
+  <input name="csrf_token" type="hidden" value="fa6a08...">
 
 =head2 date_field
 
@@ -336,9 +306,9 @@ L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">.
 Generate C<input> tag of type C<date>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="end" type="date" />
-  <input name="end" type="date" value="2012-12-21" />
-  <input id="foo" name="end" type="date" value="2012-12-21" />
+  <input name="end" type="date">
+  <input name="end" type="date" value="2012-12-21">
+  <input id="foo" name="end" type="date" value="2012-12-21">
 
 =head2 datetime_field
 
@@ -349,9 +319,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<datetime>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="end" type="datetime" />
-  <input name="end" type="datetime" value="2012-12-21T23:59:59Z" />
-  <input id="foo" name="end" type="datetime" value="2012-12-21T23:59:59Z" />
+  <input name="end" type="datetime">
+  <input name="end" type="datetime" value="2012-12-21T23:59:59Z">
+  <input id="foo" name="end" type="datetime" value="2012-12-21T23:59:59Z">
 
 =head2 email_field
 
@@ -362,9 +332,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<email>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="notify" type="email" />
-  <input name="notify" type="email" value="nospam@example.com" />
-  <input id="foo" name="notify" type="email" value="nospam@example.com" />
+  <input name="notify" type="email">
+  <input name="notify" type="email" value="nospam@example.com">
+  <input id="foo" name="notify" type="email" value="nospam@example.com">
 
 =head2 file_field
 
@@ -373,8 +343,8 @@ automatically get picked up and shown as default.
 
 Generate C<input> tag of type C<file>.
 
-  <input name="avatar" type="file" />
-  <input id="foo" name="avatar" type="file" />
+  <input name="avatar" type="file">
+  <input id="foo" name="avatar" type="file">
 
 =head2 form_for
 
@@ -400,20 +370,20 @@ routes that allow C<POST> but not C<GET>, a C<method> attribute will be
 automatically added.
 
   <form action="/path/to/login">
-    <input name="first_name" type="text" />
-    <input value="Ok" type="submit" />
+    <input name="first_name" type="text">
+    <input value="Ok" type="submit">
   </form>
   <form action="/path/to/login.txt" method="POST">
-    <input name="first_name" type="text" />
-    <input value="Ok" type="submit" />
+    <input name="first_name" type="text">
+    <input value="Ok" type="submit">
   </form>
   <form action="/path/to/login" enctype="multipart/form-data">
-    <input disabled="disabled" name="first_name" type="text" />
-    <input value="Ok" type="submit" />
+    <input disabled="disabled" name="first_name" type="text">
+    <input value="Ok" type="submit">
   </form>
   <form action="http://example.com/login" method="POST">
-    <input name="first_name" type="text" />
-    <input value="Ok" type="submit" />
+    <input name="first_name" type="text">
+    <input value="Ok" type="submit">
   </form>
 
 =head2 hidden_field
@@ -423,8 +393,8 @@ automatically added.
 
 Generate C<input> tag of type C<hidden>.
 
-  <input name="foo" type="hidden" value="bar" />
-  <input id="bar" name="foo" type="hidden" value="bar" />
+  <input name="foo" type="hidden" value="bar">
+  <input id="bar" name="foo" type="hidden" value="bar">
 
 =head2 image
 
@@ -433,8 +403,8 @@ Generate C<input> tag of type C<hidden>.
 
 Generate portable C<img> tag.
 
-  <img src="/path/to/images/foo.png" />
-  <img alt="Foo" src="/path/to/images/foo.png" />
+  <img src="/path/to/images/foo.png">
+  <img alt="Foo" src="/path/to/images/foo.png">
 
 =head2 input_tag
 
@@ -445,9 +415,9 @@ Generate portable C<img> tag.
 Generate C<input> tag. Previous input values will automatically get picked up
 and shown as default.
 
-  <input name="first_name" />
-  <input name="first_name" value="Default name" />
-  <input name="employed" type="checkbox" />
+  <input name="first_name">
+  <input name="first_name" value="Default name">
+  <input name="employed" type="checkbox">
 
 =head2 javascript
 
@@ -458,7 +428,7 @@ and shown as default.
 
 Generate portable C<script> tag for JavaScript asset.
 
-  <script src="/path/to/script.js" />
+  <script src="/path/to/script.js"></script>
   <script><![CDATA[
     var a = 'b';
   ]]></script>
@@ -521,9 +491,9 @@ to using the capitalized link target as content.
 Generate C<input> tag of type C<month>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="vacation" type="month" />
-  <input name="vacation" type="month" value="2012-12" />
-  <input id="foo" name="vacation" type="month" value="2012-12" />
+  <input name="vacation" type="month">
+  <input name="vacation" type="month" value="2012-12">
+  <input id="foo" name="vacation" type="month" value="2012-12">
 
 =head2 number_field
 
@@ -534,9 +504,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<number>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="age" type="number" />
-  <input name="age" type="number" value="25" />
-  <input id="foo" max="200" min="0" name="age" type="number" value="25" />
+  <input name="age" type="number">
+  <input name="age" type="number" value="25">
+  <input id="foo" max="200" min="0" name="age" type="number" value="25">
 
 =head2 password_field
 
@@ -545,8 +515,8 @@ automatically get picked up and shown as default.
 
 Generate C<input> tag of type C<password>.
 
-  <input name="pass" type="password" />
-  <input id="foo" name="pass" type="password" />
+  <input name="pass" type="password">
+  <input id="foo" name="pass" type="password">
 
 =head2 radio_button
 
@@ -556,8 +526,8 @@ Generate C<input> tag of type C<password>.
 Generate C<input> tag of type C<radio>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="country" type="radio" value="germany" />
-  <input id="foo" name="country" type="radio" value="germany" />
+  <input name="country" type="radio" value="germany">
+  <input id="foo" name="country" type="radio" value="germany">
 
 =head2 range_field
 
@@ -568,9 +538,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<range>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="age" type="range" />
-  <input name="age" type="range" value="25" />
-  <input id="foo" max="200" min="200" name="age" type="range" value="25" />
+  <input name="age" type="range">
+  <input name="age" type="range" value="25">
+  <input id="foo" max="200" min="200" name="age" type="range" value="25">
 
 =head2 search_field
 
@@ -581,9 +551,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<search>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="q" type="search" />
-  <input name="q" type="search" value="perl" />
-  <input id="foo" name="q" type="search" value="perl" />
+  <input name="q" type="search">
+  <input name="q" type="search" value="perl">
+  <input id="foo" name="q" type="search" value="perl">
 
 =head2 select_field
 
@@ -635,7 +605,7 @@ automatically get picked up and shown as default.
 
 Generate portable C<style> or C<link> tag for CSS asset.
 
-  <link href="/path/to/foo.css" rel="stylesheet" />
+  <link href="/path/to/foo.css" rel="stylesheet">
   <style><![CDATA[
     body {color: #000}
   ]]></style>
@@ -647,8 +617,8 @@ Generate portable C<style> or C<link> tag for CSS asset.
 
 Generate C<input> tag of type C<submit>.
 
-  <input type="submit" value="Ok" />
-  <input id="foo" type="submit" value="Ok!" />
+  <input type="submit" value="Ok">
+  <input id="foo" type="submit" value="Ok!">
 
 =head2 t
 
@@ -660,8 +630,9 @@ Alias for L</"tag">.
 
 =head2 tag
 
+  %= tag 'br'
   %= tag 'div'
-  %= tag 'div', id => 'foo'
+  %= tag 'div', id => 'foo', hidden => undef
   %= tag div => 'test & 123'
   %= tag div => (id => 'foo') => 'test & 123'
   %= tag div => (data => {my_id => 1, Name => 'test'}) => 'test & 123'
@@ -670,10 +641,12 @@ Alias for L</"tag">.
   % end
   <%= tag div => (id => 'foo') => begin %>test & 123<% end %>
 
-HTML/XML tag generator.
+HTML tag generator, the C<data> attribute may contain a hash reference with
+pairs to generate attributes from.
 
-  <div />
-  <div id="foo" />
+  <br>
+  <div></div>
+  <div id="foo" hidden></div>
   <div>test &amp; 123</div>
   <div id="foo">test &amp; 123</div>
   <div data-my-id="1" data-name="test">test &amp; 123</div>
@@ -684,8 +657,8 @@ HTML/XML tag generator.
 
 Very useful for reuse in more specific tag helpers.
 
-  my $output = $c->tag('div');
-  my $output = $c->tag('div', id => 'foo');
+  my $output = $c->tag('meta');
+  my $output = $c->tag('meta', charset => 'UTF-8');
   my $output = $c->tag(div => '<p>This will be escaped</p>');
   my $output = $c->tag(div => sub { '<p>This will not be escaped</p>' });
 
@@ -698,7 +671,7 @@ accidental double escaping in C<ep> templates.
 
 Same as L</"tag">, but adds the class C<field-with-error>.
 
-  <input class="foo field-with-error" />
+  <input class="foo field-with-error">
 
 =head2 tel_field
 
@@ -709,9 +682,9 @@ Same as L</"tag">, but adds the class C<field-with-error>.
 Generate C<input> tag of type C<tel>. Previous input values will automatically
 get picked up and shown as default.
 
-  <input name="work" type="tel" />
-  <input name="work" type="tel" value="123456789" />
-  <input id="foo" name="work" type="tel" value="123456789" />
+  <input name="work" type="tel">
+  <input name="work" type="tel" value="123456789">
+  <input id="foo" name="work" type="tel" value="123456789">
 
 =head2 text_area
 
@@ -741,9 +714,9 @@ up and shown as default.
 Generate C<input> tag of type C<text>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="first_name" type="text" />
-  <input name="first_name" type="text" value="Default name" />
-  <input class="user" name="first_name" type="text" value="Default name" />
+  <input name="first_name" type="text">
+  <input name="first_name" type="text" value="Default name">
+  <input class="user" name="first_name" type="text" value="Default name">
 
 =head2 time_field
 
@@ -754,9 +727,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<time>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="start" type="time" />
-  <input name="start" type="time" value="23:59:59" />
-  <input id="foo" name="start" type="time" value="23:59:59" />
+  <input name="start" type="time">
+  <input name="start" type="time" value="23:59:59">
+  <input id="foo" name="start" type="time" value="23:59:59">
 
 =head2 url_field
 
@@ -767,9 +740,9 @@ automatically get picked up and shown as default.
 Generate C<input> tag of type C<url>. Previous input values will automatically
 get picked up and shown as default.
 
-  <input name="address" type="url" />
-  <input name="address" type="url" value="http://mojolicio.us" />
-  <input id="foo" name="address" type="url" value="http://mojolicio.us" />
+  <input name="address" type="url">
+  <input name="address" type="url" value="http://mojolicio.us">
+  <input id="foo" name="address" type="url" value="http://mojolicio.us">
 
 =head2 week_field
 
@@ -780,9 +753,9 @@ get picked up and shown as default.
 Generate C<input> tag of type C<week>. Previous input values will
 automatically get picked up and shown as default.
 
-  <input name="vacation" type="week" />
-  <input name="vacation" type="week" value="2012-W17" />
-  <input id="foo" name="vacation" type="week" value="2012-W17" />
+  <input name="vacation" type="week">
+  <input name="vacation" type="week" value="2012-W17">
+  <input id="foo" name="vacation" type="week" value="2012-W17">
 
 =head1 METHODS
 
@@ -233,7 +233,7 @@ sub _render_template {
   }
 
   # No handler
-  else { $c->app->log->error(qq{No handler for "$handler" available.}) }
+  else { $c->app->log->error(qq{No handler for "$handler" available}) }
   return undef;
 }
 
@@ -32,7 +32,12 @@ sub add_child {
 
 sub any { shift->_generate_route(ref $_[0] eq 'ARRAY' ? shift : [], @_) }
 
-sub bridge { shift->route(@_)->inline(1) }
+# DEPRECATED in Tiger Face!
+sub bridge {
+  Mojo::Util::deprecated 'Mojolicious::Routes::Route::bridge is DEPRECATED in'
+    . ' favor of Mojolicious::Routes::Route::under';
+  shift->route(@_)->inline(1);
+}
 
 sub delete { shift->_generate_route(DELETE => @_) }
 
@@ -297,25 +302,11 @@ current parent if necessary.
   my $route = $r->any([qw(GET POST)] => '/:foo' => [foo => qr/\w+/]);
 
 Generate L<Mojolicious::Routes::Route> object matching any of the listed HTTP
-request methods or all. See also the L<Mojolicious::Lite> tutorial for many
+request methods or all. See also L<Mojolicious::Guides::Tutorial> for many
 more argument variations.
 
   $r->any('/user')->to('user#whatever');
 
-=head2 bridge
-
-  my $route = $r->bridge;
-  my $route = $r->bridge('/:action');
-  my $route = $r->bridge('/:action', action => qr/\w+/);
-  my $route = $r->bridge(format => 0);
-
-Low-level generator for nested routes with their own intermediate destination,
-returns a L<Mojolicious::Routes::Route> object.
-
-  my $auth = $r->bridge('/user')->to('user#auth');
-  $auth->get('/show')->to('#show');
-  $auth->post('/create')->to('#create');
-
 =head2 delete
 
   my $route = $r->delete('/:foo');
@@ -324,7 +315,7 @@ returns a L<Mojolicious::Routes::Route> object.
   my $route = $r->delete('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<DELETE>
-requests. See also the L<Mojolicious::Lite> tutorial for many more argument
+requests. See also L<Mojolicious::Guides::Tutorial> for many more argument
 variations.
 
   $r->delete('/user')->to('user#remove');
@@ -356,7 +347,7 @@ generated ones.
   my $route = $r->get('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<GET> requests.
-See also the L<Mojolicious::Lite> tutorial for many more argument variations.
+See also L<Mojolicious::Guides::Tutorial> for many more argument variations.
 
   $r->get('/user')->to('user#show');
 
@@ -399,7 +390,9 @@ the current route.
 =head2 new
 
   my $r = Mojolicious::Routes::Route->new;
-  my $r = Mojolicious::Routes::Route->new('/:controller/:action');
+  my $r = Mojolicious::Routes::Route->new('/:action');
+  my $r = Mojolicious::Routes::Route->new('/:action', action => qr/\w+/);
+  my $r = Mojolicious::Routes::Route->new(format => 0);
 
 Construct a new L<Mojolicious::Routes::Route> object and L</"parse"> pattern
 if necessary.
@@ -412,7 +405,7 @@ if necessary.
   my $route = $r->options('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<OPTIONS>
-requests. See also the L<Mojolicious::Lite> tutorial for many more argument
+requests. See also L<Mojolicious::Guides::Tutorial> for many more argument
 variations.
 
   $r->options('/user')->to('user#overview');
@@ -445,7 +438,7 @@ Parse pattern.
   my $route = $r->patch('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<PATCH> requests.
-See also the L<Mojolicious::Lite> tutorial for many more argument variations.
+See also L<Mojolicious::Guides::Tutorial> for many more argument variations.
 
   $r->patch('/user')->to('user#update');
 
@@ -457,7 +450,7 @@ See also the L<Mojolicious::Lite> tutorial for many more argument variations.
   my $route = $r->post('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<POST> requests.
-See also the L<Mojolicious::Lite> tutorial for many more argument variations.
+See also L<Mojolicious::Guides::Tutorial> for many more argument variations.
 
   $r->post('/user')->to('user#create');
 
@@ -469,7 +462,7 @@ See also the L<Mojolicious::Lite> tutorial for many more argument variations.
   my $route = $r->put('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only C<PUT> requests.
-See also the L<Mojolicious::Lite> tutorial for many more argument variations.
+See also L<Mojolicious::Guides::Tutorial> for many more argument variations.
 
   $r->put('/user')->to('user#replace');
 
@@ -539,7 +532,7 @@ Stringify the whole route.
   my $route = $r->under([format => 0]);
 
 Generate L<Mojolicious::Routes::Route> object for a nested route with its own
-intermediate destination. See also the L<Mojolicious::Lite> tutorial for many
+intermediate destination. See also L<Mojolicious::Guides::Tutorial> for many
 more argument variations.
 
   my $auth = $r->under('/user')->to('user#auth');
@@ -566,7 +559,7 @@ restrictions.
   my $route = $r->websocket('/:foo' => [foo => qr/\w+/] => sub {...});
 
 Generate L<Mojolicious::Routes::Route> object matching only WebSocket
-handshakes. See also the L<Mojolicious::Lite> tutorial for many more argument
+handshakes. See also L<Mojolicious::Guides::Tutorial> for many more argument
 variations.
 
   $r->websocket('/echo')->to('example#echo');
@@ -21,7 +21,7 @@ sub auto_render {
   my ($self, $c) = @_;
   my $stash = $c->stash;
   return if $stash->{'mojo.rendered'};
-  $c->render_maybe or $stash->{'mojo.routed'} or $c->render_not_found;
+  $c->render_maybe or $stash->{'mojo.routed'} or $c->helpers->reply->not_found;
 }
 
 sub continue {
@@ -111,9 +111,9 @@ sub _add {
 
 sub _callback {
   my ($self, $c, $cb, $last) = @_;
-  $c->stash->{'mojo.routed'}++ if $last;
+  $c->stash->{'mojo.routed'} = 1 if $last;
   my $app = $c->app;
-  $app->log->debug('Routing to a callback.');
+  $app->log->debug('Routing to a callback');
   return _action($app, $c, $cb, $last);
 }
 
@@ -143,7 +143,7 @@ sub _class {
 
     # Failed
     next unless defined(my $found = $self->_load($class));
-    return !$log->debug(qq{Class "$class" is not a controller.}) unless $found;
+    return !$log->debug(qq{Class "$class" is not a controller}) unless $found;
 
     # Success
     my $new = $class->new(%$c);
@@ -152,7 +152,7 @@ sub _class {
   }
 
   # Nothing found
-  $log->debug(qq{Controller "$classes[-1]" does not exist.}) if @classes;
+  $log->debug(qq{Controller "$classes[-1]" does not exist}) if @classes;
   return @classes ? undef : 0;
 }
 
@@ -168,7 +168,7 @@ sub _controller {
   my $app   = $old->app;
   my $log   = $app->log;
   if ($new->isa('Mojo')) {
-    $log->debug(qq{Routing to application "$class".});
+    $log->debug(qq{Routing to application "$class"});
 
     # Try to connect routes
     if (my $sub = $new->can('routes')) {
@@ -176,22 +176,22 @@ sub _controller {
       weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent;
     }
     $new->handler($old);
-    $old->stash->{'mojo.routed'}++;
+    $old->stash->{'mojo.routed'} = 1;
   }
 
   # Action
   elsif (my $method = $field->{action}) {
     if (!$self->is_hidden($method)) {
-      $log->debug(qq{Routing to controller "$class" and action "$method".});
+      $log->debug(qq{Routing to controller "$class" and action "$method"});
 
       if (my $sub = $new->can($method)) {
-        $old->stash->{'mojo.routed'}++ if $last;
+        $old->stash->{'mojo.routed'} = 1 if $last;
         return 1 if _action($app, $new, $sub, $last);
       }
 
-      else { $log->debug('Action not found in controller.') }
+      else { $log->debug('Action not found in controller') }
     }
-    else { $log->debug(qq{Action "$method" is not allowed.}) }
+    else { $log->debug(qq{Action "$method" is not allowed}) }
   }
 
   return undef;
@@ -206,7 +206,7 @@ sub _load {
 
   # Check base classes
   return 0 unless first { $app->isa($_) } @{$self->base_classes};
-  return ++$self->{loaded}{$app};
+  return $self->{loaded}{$app} = 1;
 }
 
 1;
@@ -34,7 +34,7 @@ sub dispatch {
 
   # Serve static file and prevent directory traversal
   return undef if $parts[0] eq '..' || !$self->serve($c, join('/', @parts));
-  $stash->{'mojo.static'}++;
+  $stash->{'mojo.static'} = 1;
   return !!$c->rendered;
 }
 
@@ -14,9 +14,8 @@ sub AUTOLOAD {
   Carp::croak "Undefined subroutine &${package}::$method called"
     unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
-  Carp::croak qq{Can't locate object method "$method" via package "$package"}
-    unless $self->validator->checks->{$method};
-  return $self->check($method => @_);
+  return $self->check($method => @_) if $self->validator->checks->{$method};
+  Carp::croak qq{Can't locate object method "$method" via package "$package"};
 }
 
 sub check {
@@ -54,7 +53,10 @@ sub error {
   return $self;
 }
 
-sub every_param { shift->_param(@_) }
+sub every_param {
+  return [] unless defined(my $value = shift->output->{shift()});
+  return [ref $value eq 'ARRAY' ? @$value : $value];
+}
 
 sub has_data { !!keys %{shift->input} }
 
@@ -82,7 +84,7 @@ sub param {
   # List names
   return sort keys %{$self->output} unless defined $name;
 
-  return $self->_param($name)->[-1];
+  return $self->every_param($name)->[-1];
 }
 
 sub required {
@@ -91,11 +93,6 @@ sub required {
   return $self->error($name => ['required']);
 }
 
-sub _param {
-  return [] unless defined(my $value = shift->output->{shift()});
-  return [ref $value eq 'ARRAY' ? @$value : $value];
-}
-
 1;
 
 =encoding utf8
@@ -142,7 +142,7 @@
         <div id="nothing" class="box spaced"></div>
         % my $cv = begin
           % my ($key, $value, $i) = @_;
-          %= tag 'tr', $i ? (class => 'important') : undef, begin
+          %= tag 'tr', $i ? (class => 'important') : (), begin
             <td class="key"><%= $key %></td>
             <td class="value wide">
               <pre class="prettyprint"><%= $value %></pre>
@@ -32,7 +32,7 @@ has secrets  => sub {
   my $self = shift;
 
   # Warn developers about insecure default
-  $self->log->debug('Your secret passphrase needs to be changed!!!');
+  $self->log->debug('Your secret passphrase needs to be changed');
 
   # Default to moniker
   return [$self->moniker];
@@ -43,7 +43,7 @@ has types     => sub { Mojolicious::Types->new };
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Tiger Face';
-our $VERSION  = '5.72';
+our $VERSION  = '5.75';
 
 sub AUTOLOAD {
   my $self = shift;
@@ -102,13 +102,13 @@ sub dispatch {
     my $req    = $c->req;
     my $method = $req->method;
     my $path   = $req->url->path->to_abs_string;
-    $self->log->debug(qq{$method "$path".});
+    $self->log->debug(qq{$method "$path"});
     $stash->{'mojo.started'} = [Time::HiRes::gettimeofday];
   }
 
   # Routes
   $plugins->emit_hook(before_routes => $c);
-  $c->render_not_found
+  $c->helpers->reply->not_found
     unless $tx->res->code || $self->routes->dispatch($c) || $tx->res->code;
 }
 
@@ -127,14 +127,14 @@ sub handler {
   $self->plugins->emit_chain(around_dispatch => $c);
 
   # Delayed response
-  $self->log->debug('Nothing has been rendered, expecting delayed response.')
+  $self->log->debug('Nothing has been rendered, expecting delayed response')
     unless $c->tx->is_writing;
 }
 
 sub helper {
   my ($self, $name, $cb) = @_;
   my $r = $self->renderer;
-  $self->log->debug(qq{Helper "$name" already exists, replacing.})
+  $self->log->debug(qq{Helper "$name" already exists, replacing})
     if exists $r->helpers->{$name};
   $r->add_helper($name => $cb);
 }
@@ -154,10 +154,9 @@ sub new {
   # Hide controller attributes/methods
   $r->hide(qw(app continue cookie every_cookie every_param));
   $r->hide(qw(every_signed_cookie finish flash helpers match on param));
-  $r->hide(qw(redirect_to render render_exception render_later render_maybe));
-  $r->hide(qw(render_not_found render_to_string rendered req res respond_to));
-  $r->hide(qw(send session signed_cookie stash tx url_for validation write));
-  $r->hide(qw(write_chunk));
+  $r->hide(qw(redirect_to render render_later render_maybe render_to_string));
+  $r->hide(qw(rendered req res respond_to send session signed_cookie stash));
+  $r->hide(qw(tx url_for validation write write_chunk));
 
   # Check if we have a log directory that is writable
   my $mode = $self->mode;
@@ -195,7 +194,7 @@ sub _exception {
   my ($next, $c) = @_;
   local $SIG{__DIE__}
     = sub { ref $_[0] ? CORE::die($_[0]) : Mojo::Exception->throw(@_) };
-  $c->render_exception($@) unless eval { $next->(); 1 };
+  $c->helpers->reply->exception($@) unless eval { $next->(); 1 };
 }
 
 1;
@@ -563,6 +562,9 @@ request.
   # Remove value
   my $foo = delete $app->defaults->{foo};
 
+  # Assign multiple values at once
+  $app->defaults(foo => 'test', bar => 23);
+
 =head2 dispatch
 
   $app->dispatch(Mojolicious::Controller->new);
@@ -646,9 +648,11 @@ L<Mojolicious> distribution see L<Mojolicious::Plugins/"PLUGINS">.
   $app->start(@ARGV);
 
 Start the command line interface for your application, for a full list of
-commands available by default see L<Mojolicious::Commands/"COMMANDS">.
+commands available by default see L<Mojolicious::Commands/"COMMANDS">. Note
+that the options C<-h>/C<--help>, C<--home> and C<-m>/C<--mode>, which are
+shared by all commands, will be parsed from C<@ARGV> during compile time.
 
-  # Always start daemon and ignore @ARGV
+  # Always start daemon
   $app->start('daemon', '-l', 'http://*:8080');
 
 =head2 startup
@@ -328,7 +328,7 @@ sub _message {
   if (ref $value eq 'HASH') {
     my $expect = exists $value->{text} ? 'text' : 'binary';
     $value = $value->{$expect};
-    $msg = '' unless $type eq $expect;
+    $msg = '' unless ($type // '') eq $expect;
   }
 
   # Decode text frame if there is no type check
@@ -93,7 +93,7 @@ L<ojo> implements the following functions, which are automatically exported.
 
 Create a route with L<Mojolicious::Lite/"any"> and return the current
 L<Mojolicious::Lite> object. The current controller object is also available
-to actions as C<$_>. See also the L<Mojolicious::Lite> tutorial for more
+to actions as C<$_>. See also L<Mojolicious::Guides::Tutorial> for more
 argument variations.
 
   $ perl -Mojo -E 'a("/hello" => {text => "Hello Mojo!"})->start' daemon
@@ -35,10 +35,10 @@ hypnotoad - Hypnotoad HTTP and WebSocket server
     hypnotoad -f ./myapp.pl
 
   Options:
-    -f, --foreground   Keep manager process in foreground.
-    -h, --help         Show this message.
-    -s, --stop         Stop server gracefully.
-    -t, --test         Test application and exit.
+    -f, --foreground   Keep manager process in foreground
+    -h, --help         Show this message
+    -s, --stop         Stop server gracefully
+    -t, --test         Test application and exit
 
 =head1 DESCRIPTION
 
@@ -41,20 +41,20 @@ morbo - Morbo HTTP and WebSocket development server
     morbo -w /usr/local/lib -w public ./myapp.pl
 
   Options:
-    -h, --help                     Show this message.
+    -h, --help                     Show this message
     -l, --listen <location>        One or more locations you want to listen
                                    on, defaults to the value of MOJO_LISTEN or
-                                   "http://*:3000".
+                                   "http://*:3000"
     -m, --mode <name>              Operating mode for your application,
                                    defaults to the value of
-                                   MOJO_MODE/PLACK_ENV or "development".
+                                   MOJO_MODE/PLACK_ENV or "development"
     -v, --verbose                  Print details about what files changed to
-                                   STDOUT.
+                                   STDOUT
     -w, --watch <directory/file>   One or more directories and files to watch
                                    for changes, defaults to the application
                                    script as well as the "lib" and "templates"
                                    directories in the current working
-                                   directory.
+                                   directory
 
 =head1 DESCRIPTION
 
@@ -232,4 +232,20 @@ ok -e $path, 'file exists';
 unlink $path;
 ok !-e $path, 'file has been cleaned up';
 
+# Abstract methods
+eval { Mojo::Asset->add_chunk };
+like $@, qr/Method "add_chunk" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->contains };
+like $@, qr/Method "contains" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->get_chunk };
+like $@, qr/Method "get_chunk" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->move_to };
+like $@, qr/Method "move_to" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->mtime };
+like $@, qr/Method "mtime" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->size };
+like $@, qr/Method "size" not implemented by subclass/, 'right error';
+eval { Mojo::Asset->slurp };
+like $@, qr/Method "slurp" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -152,4 +152,7 @@ $file = catfile $dir, 'test.txt';
 is b("just\nworks!")->spurt($file)->quote, qq{"just\nworks!"}, 'right result';
 is b($file)->slurp, "just\nworks!", 'successful roundtrip';
 
+# term_escape
+is b("\t\b\r\n\f")->term_escape, "\\x09\\x08\\x0d\n\\x0c", 'right result';
+
 done_testing();
@@ -88,4 +88,13 @@ $content->parse(
   "Content-Length: 18446744073709551616\x0d\x0a\x0d\x0aHello World!");
 is $content->asset->size, 12, 'right size';
 
+# Abstract methods
+eval { Mojo::Content->body_contains };
+like $@, qr/Method "body_contains" not implemented by subclass/, 'right error';
+eval { Mojo::Content->body_size };
+like $@, qr/Method "body_size" not implemented by subclass/, 'right error';
+eval { Mojo::Content->get_body_chunk };
+like $@, qr/Method "get_body_chunk" not implemented by subclass/,
+  'right error';
+
 done_testing();
@@ -448,4 +448,10 @@ is $cookies->[0]->expires->epoch, 942189160, 'right expires epoch value';
 is $cookies->[0]->secure, 1, 'right secure flag';
 is $cookies->[1], undef, 'no more cookies';
 
+# Abstract methods
+eval { Mojo::Cookie->parse };
+like $@, qr/Method "parse" not implemented by subclass/, 'right error';
+eval { Mojo::Cookie->to_string };
+like $@, qr/Method "to_string" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -271,4 +271,8 @@ ok !!Mojo::IOLoop->acceptor($id), 'acceptor has been added';
 undef $daemon;
 ok !Mojo::IOLoop->acceptor($id), 'acceptor has been removed';
 
+# Abstract methods
+eval { Mojo::Server->run };
+like $@, qr/Method "run" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -222,9 +222,9 @@ sleep 1 while _port($port2);
 
 # Check log
 $log = slurp $log;
-like $log, qr/Worker \d+ started\./,                      'right message';
-like $log, qr/Starting zero downtime software upgrade\./, 'right message';
-like $log, qr/Upgrade successful, stopping $old\./,       'right message';
+like $log, qr/Worker \d+ started/,                      'right message';
+like $log, qr/Starting zero downtime software upgrade/, 'right message';
+like $log, qr/Upgrade successful, stopping $old/,       'right message';
 
 sub _pid {
   return undef unless open my $file, '<', catdir($dir, 'hypnotoad.pid');
@@ -27,9 +27,10 @@ is ref $loop->reactor, 'MyReactor', 'right class';
 my $err;
 Mojo::IOLoop->next_tick(
   sub {
-    eval { Mojo::IOLoop->start };
+    my $loop = shift;
+    eval { $loop->start };
     $err = $@;
-    Mojo::IOLoop->stop;
+    $loop->stop;
   }
 );
 Mojo::IOLoop->start;
@@ -47,6 +47,7 @@ is $pointer->new([{'foo~/bar' => 'bar'}])->get('/0/foo~0~1bar'), 'bar',
 is $pointer->new([{'f~o~o~/b~' => {'a~' => {'r' => 'baz'}}}])
   ->get('/0/f~0o~0o~0~1b~0/a~0/r'), 'baz',
   '"/0/f~0o~0o~0~1b~0/a~0/r" is "baz"';
+is $pointer->new({'~1' => 'foo'})->get('/~01'), 'foo', '"/~01" is "foo"';
 
 # Unicode
 is $pointer->new({'☃' => 'snowman'})->get('/☃'), 'snowman',
@@ -10,14 +10,14 @@ use Mojo::Util qw(decode slurp);
 my $dir = tempdir CLEANUP => 1;
 my $path = catdir $dir, 'test.log';
 my $log = Mojo::Log->new(level => 'error', path => $path);
-$log->error('Just works.');
-$log->fatal('I ♥ Mojolicious.');
-$log->debug('Does not work.');
+$log->error('Just works');
+$log->fatal('I ♥ Mojolicious');
+$log->debug('Does not work');
 undef $log;
 my $content = decode 'UTF-8', slurp($path);
-like $content, qr/\[.*\] \[error\] Just works\./,        'right error message';
-like $content, qr/\[.*\] \[fatal\] I ♥ Mojolicious\./, 'right fatal message';
-unlike $content, qr/\[.*\] \[debug\] Does not work\./, 'no debug message';
+like $content,   qr/\[.*\] \[error\] Just works/,        'right error message';
+like $content,   qr/\[.*\] \[fatal\] I ♥ Mojolicious/, 'right fatal message';
+unlike $content, qr/\[.*\] \[debug\] Does not work/,     'no debug message';
 
 # Logging to STDERR
 my $buffer = '';
@@ -25,24 +25,23 @@ my $buffer = '';
   open my $handle, '>', \$buffer;
   local *STDERR = $handle;
   my $log = Mojo::Log->new;
-  $log->error('Just works.');
-  $log->fatal('I ♥ Mojolicious.');
-  $log->debug('Works too.');
+  $log->error('Just works');
+  $log->fatal('I ♥ Mojolicious');
+  $log->debug('Works too');
 }
 $content = decode 'UTF-8', $buffer;
-like $content, qr/\[.*\] \[error\] Just works\.\n/, 'right error message';
-like $content, qr/\[.*\] \[fatal\] I ♥ Mojolicious\.\n/,
-  'right fatal message';
-like $content, qr/\[.*\] \[debug\] Works too\.\n/, 'right debug message';
+like $content, qr/\[.*\] \[error\] Just works\n/,        'right error message';
+like $content, qr/\[.*\] \[fatal\] I ♥ Mojolicious\n/, 'right fatal message';
+like $content, qr/\[.*\] \[debug\] Works too\n/,         'right debug message';
 
 # Formatting
 $log = Mojo::Log->new;
-like $log->format->(time, 'debug', 'Test 123.'),
-  qr/^\[.*\] \[debug\] Test 123\.\n$/, 'right format';
+like $log->format->(time, 'debug', 'Test 123'),
+  qr/^\[.*\] \[debug\] Test 123\n$/, 'right format';
 like $log->format->(time, 'debug', qw(Test 1 2 3)),
   qr/^\[.*\] \[debug\] Test\n1\n2\n3\n$/, 'right format';
-like $log->format->(time, 'error', 'I ♥ Mojolicious.'),
-  qr/^\[.*\] \[error\] I ♥ Mojolicious\.\n$/, 'right format';
+like $log->format->(time, 'error', 'I ♥ Mojolicious'),
+  qr/^\[.*\] \[error\] I ♥ Mojolicious\n$/, 'right format';
 $log->format(
   sub {
     my ($time, $level, @lines) = @_;
@@ -86,22 +85,22 @@ my $history;
   open my $handle, '>', \$buffer;
   local *STDERR = $handle;
   my $log = Mojo::Log->new->max_history_size(2)->level('info');
-  $log->error('First.');
-  $log->fatal('Second.');
-  $log->debug('Third.');
-  $log->info('Fourth.', 'Fifth.');
+  $log->error('First');
+  $log->fatal('Second');
+  $log->debug('Third');
+  $log->info('Fourth', 'Fifth');
   $history = $log->history;
 }
 $content = decode 'UTF-8', $buffer;
-like $content,   qr/\[.*\] \[error\] First\.\n/,         'right error message';
-like $content,   qr/\[.*\] \[info\] Fourth\.\nFifth.\n/, 'right info message';
-unlike $content, qr/debug/,                              'no debug message';
+like $content,   qr/\[.*\] \[error\] First\n/,        'right error message';
+like $content,   qr/\[.*\] \[info\] Fourth\nFifth\n/, 'right info message';
+unlike $content, qr/debug/,                           'no debug message';
 like $history->[0][0], qr/^\d+$/, 'right epoch time';
 is $history->[0][1],   'fatal',   'right level';
-is $history->[0][2],   'Second.', 'right message';
+is $history->[0][2],   'Second',  'right message';
 is $history->[1][1],   'info',    'right level';
-is $history->[1][2],   'Fourth.', 'right message';
-is $history->[1][3],   'Fifth.',  'right message';
+is $history->[1][2],   'Fourth',  'right message';
+is $history->[1][3],   'Fifth',   'right message';
 ok !$history->[2], 'no more messages';
 
 # "debug"
@@ -66,17 +66,17 @@ is $params->append($params2)->to_string, 'bar=bar&foo=&x=1&y=2',
 is $params2->to_string, 'x=1&y=2', 'right format';
 
 # "0"
-$params = Mojo::Parameters->new(foo => 0);
-is $params->param('foo'), 0, 'right value';
-is_deeply $params->every_param('foo'), [0], 'right value';
-is_deeply $params->every_param('bar'), [], 'no values';
-is $params->to_string, 'foo=0', 'right format';
+$params = Mojo::Parameters->new(0 => 0);
+is $params->param(0), 0, 'right value';
+is_deeply $params->every_param(0), [0], 'right value';
+is_deeply $params->every_param('foo'), [], 'no values';
+is $params->to_string, '0=0', 'right format';
 $params = Mojo::Parameters->new($params->to_string);
-is $params->param('foo'), 0, 'right value';
-is_deeply $params->every_param('foo'), [0], 'right value';
-is $params->to_hash->{foo}, 0, 'right value';
-is_deeply $params->to_hash, {foo => 0}, 'right structure';
-is $params->to_string, 'foo=0', 'right format';
+is $params->param(0), 0, 'right value';
+is_deeply $params->every_param(0), [0], 'right value';
+is $params->to_hash->{0}, 0, 'right value';
+is_deeply $params->to_hash, {0 => 0}, 'right structure';
+is $params->to_string, '0=0', 'right format';
 
 # Semicolon
 $params = Mojo::Parameters->new('foo=bar;baz');
@@ -41,13 +41,14 @@ $prefork->on(
   }
 );
 is $prefork->workers, 4, 'start with four workers';
-my (@spawn, @reap, $worker, $tx, $graceful);
+my (@spawn, @reap, $worker, $tx, $graceful, $healthy);
 $prefork->on(spawn => sub { push @spawn, pop });
 $prefork->once(
   heartbeat => sub {
     my ($prefork, $pid) = @_;
-    $worker = $pid;
-    $tx     = Mojo::UserAgent->new->get("http://127.0.0.1:$port");
+    $worker  = $pid;
+    $healthy = $prefork->healthy;
+    $tx      = Mojo::UserAgent->new->get("http://127.0.0.1:$port");
     kill 'QUIT', $$;
   }
 );
@@ -55,7 +56,9 @@ $prefork->on(reap => sub { push @reap, pop });
 $prefork->on(finish => sub { $graceful = pop });
 my $log = '';
 my $cb = $prefork->app->log->on(message => sub { $log .= pop });
+is $prefork->healthy, 0, 'no healthy workers';
 $prefork->run;
+ok $healthy >= 1, 'healthy workers';
 is scalar @spawn, 4, 'four workers spawned';
 is scalar @reap,  4, 'four workers reaped';
 ok !!grep { $worker eq $_ } @spawn, 'worker has a heartbeat';
@@ -63,11 +66,11 @@ ok $graceful, 'server has been stopped gracefully';
 is_deeply [sort @spawn], [sort @reap], 'same process ids';
 is $tx->res->code, 200,           'right status';
 is $tx->res->body, 'just works!', 'right content';
-like $log, qr/Listening at/,                                 'right message';
-like $log, qr/Manager $$ started\./,                         'right message';
-like $log, qr/Creating process id file/,                     'right message';
-like $log, qr/Trying to stop worker $spawn[0] gracefully\./, 'right message';
-like $log, qr/Worker $spawn[0] stopped\./,                   'right message';
+like $log, qr/Listening at/,                         'right message';
+like $log, qr/Manager $$ started/,                   'right message';
+like $log, qr/Creating process id file/,             'right message';
+like $log, qr/Stopping worker $spawn[0] gracefully/, 'right message';
+like $log, qr/Worker $spawn[0] stopped/,             'right message';
 $prefork->app->log->unsubscribe(message => $cb);
 
 # Process id and lock files
@@ -269,4 +269,28 @@ like $client_err, qr/^Mojo::IOLoop already running/, 'right error';
 ok $server_running, 'loop is running';
 ok $client_running, 'loop is running';
 
+# Abstract methods
+eval { Mojo::Reactor->again };
+like $@, qr/Method "again" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->io };
+like $@, qr/Method "io" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->is_running };
+like $@, qr/Method "is_running" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->one_tick };
+like $@, qr/Method "one_tick" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->recurring };
+like $@, qr/Method "recurring" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->remove };
+like $@, qr/Method "remove" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->reset };
+like $@, qr/Method "reset" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->start };
+like $@, qr/Method "start" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->stop };
+like $@, qr/Method "stop" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->timer };
+like $@, qr/Method "timer" not implemented by subclass/, 'right error';
+eval { Mojo::Reactor->watch };
+like $@, qr/Method "watch" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -78,7 +78,6 @@ $req = Mojo::Message::Request->new;
 $req->parse("12345\x0d\x0a");
 ok $req->is_finished, 'request is finished';
 is $req->error->{message}, 'Bad request start-line', 'right error';
-is $req->error->{advice}, 400, 'right advice';
 ok !$req->is_limit_exceeded, 'limit is not exceeded';
 
 # Parse broken HTTP 1.1 message with header exceeding line limit
@@ -89,7 +88,6 @@ ok !$req->is_limit_exceeded, 'limit is not exceeded';
 $req->parse("Foo: @{['a' x 10240]}");
 ok $req->is_finished, 'request is finished';
 is $req->error->{message}, 'Maximum header size exceeded', 'right error';
-is $req->error->{advice}, 431, 'right advice';
 ok $req->is_limit_exceeded, 'limit is exceeded';
 is $req->method,            'GET', 'right method';
 is $req->version,           '1.1', 'right version';
@@ -352,12 +350,10 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse('GET /foo/bar/baz.html HTTP/1');
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum start-line size exceeded', 'right error';
-  is $req->error->{advice}, 431, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
   ok $limit, 'limit is exceeded';
   $req->error({message => 'Nothing important.'});
   is $req->error->{message}, 'Nothing important.', 'right error';
-  is $req->error->{advice}, undef, 'no advice';
   ok $req->is_limit_exceeded, 'limit is still exceeded';
 }
 
@@ -370,7 +366,6 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse("Content-Type: text/plain\x0d\x0a");
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum header size exceeded', 'right error';
-  is $req->error->{advice}, 431, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
 }
 
@@ -384,7 +379,6 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse('GET /foo/bar/baz.html HTTP/1');
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum message size exceeded', 'right error';
-  is $req->error->{advice}, 413, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
   ok $limit, 'limit is exceeded';
 }
@@ -397,7 +391,6 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse("Content-Type: text/plain\x0d\x0a");
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum message size exceeded', 'right error';
-  is $req->error->{advice}, 413, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
 }
 
@@ -411,7 +404,6 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse('Hello World!');
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum message size exceeded', 'right error';
-  is $req->error->{advice}, 413, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
 }
 
@@ -426,7 +418,6 @@ is $req->headers->content_length, undef,        'no "Content-Length" value';
   $req->parse("D: d\x0d\x0a\x0d\x0a");
   ok $req->is_finished, 'request is finished';
   is $req->error->{message}, 'Maximum header size exceeded', 'right error';
-  is $req->error->{advice}, 431, 'right advice';
   ok $req->is_limit_exceeded, 'limit is exceeded';
   is $req->method,            'GET', 'right method';
   is $req->version,           '1.1', 'right version';
@@ -2089,4 +2080,14 @@ is $req->version,     '1.1', 'right version';
 is $req->url,         '/#09azAZ!$%&\'()*+,-./:;=?@%5B%5C%5D%5E_%60%7B%7C%7D~',
   'right URL';
 
+# Abstract methods
+eval { Mojo::Message->cookies };
+like $@, qr/Method "cookies" not implemented by subclass/, 'right error';
+eval { Mojo::Message->extract_start_line };
+like $@, qr/Method "extract_start_line" not implemented by subclass/,
+  'right error';
+eval { Mojo::Message->get_start_line_chunk };
+like $@, qr/Method "get_start_line_chunk" not implemented by subclass/,
+  'right error';
+
 done_testing();
@@ -373,7 +373,6 @@ is $res->headers->content_length, undef, 'right "Content-Length" value';
   ok $res->is_finished, 'response is finished';
   ok $res->content->is_finished, 'content is finished';
   is $res->error->{message}, 'Maximum buffer size exceeded', 'right error';
-  is $res->error->{advice}, 400, 'right advice';
   ok $res->is_limit_exceeded, 'limit is not exceeded';
   is $res->code,              200, 'right status';
   is $res->message,           'OK', 'right message';
@@ -396,7 +395,6 @@ is $res->headers->content_length, undef, 'right "Content-Length" value';
   ok $res->is_finished, 'response is finished';
   ok $res->content->is_finished, 'content is finished';
   is $res->error->{message}, 'Maximum buffer size exceeded', 'right error';
-  is $res->error->{advice}, 400, 'right advice';
   is $res->code,    200,   'right status';
   is $res->message, 'OK',  'right message';
   is $res->version, '1.1', 'right version';
@@ -420,7 +418,6 @@ is $res->headers->content_length, undef, 'right "Content-Length" value';
   ok $res->is_finished, 'response is finished';
   ok $res->content->is_finished, 'content is finished';
   is $res->error->{message}, 'Maximum buffer size exceeded', 'right error';
-  is $res->error->{advice}, 400, 'right advice';
   is $res->code,    200,   'right status';
   is $res->message, 'OK',  'right message';
   is $res->version, '1.1', 'right version';
@@ -859,4 +859,14 @@ is $tx->req->body, '',    'no content';
 is $tx->res->code, undef, 'no status';
 is $tx->res->headers->location, undef, 'no "Location" value';
 
+# Abstract methods
+eval { Mojo::Transaction->client_read };
+like $@, qr/Method "client_read" not implemented by subclass/, 'right error';
+eval { Mojo::Transaction->client_write };
+like $@, qr/Method "client_write" not implemented by subclass/, 'right error';
+eval { Mojo::Transaction->server_read };
+like $@, qr/Method "server_read" not implemented by subclass/, 'right error';
+eval { Mojo::Transaction->server_write };
+like $@, qr/Method "server_write" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -294,7 +294,7 @@ app->log->unsubscribe(message => $msg);
 ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Premature connection close', 'right error';
 is $timeout, 1, 'finish event has been emitted';
-like $log, qr/Inactivity timeout\./, 'right log message';
+like $log, qr/Inactivity timeout/, 'right log message';
 
 # Client times out
 $ua->once(
@@ -335,8 +335,7 @@ $ua->once(
 $tx = $ua->get('/echo' => 'Hello World!');
 ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Maximum message size exceeded', 'right error';
-is $tx->error->{advice},  413,                             'right advice';
-is $tx->error->{code},    undef,                           'no status';
+is $tx->error->{code}, undef, 'no status';
 ok $tx->res->is_limit_exceeded, 'limit is exceeded';
 
 # 404 response
@@ -16,8 +16,8 @@ use Mojo::Util
   qw(decode dumper encode hmac_sha1_sum html_unescape md5_bytes md5_sum),
   qw(monkey_patch punycode_decode punycode_encode quote secure_compare),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
-  qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode xss_escape);
+  qw(steady_time tablify term_escape trim unindent unquote url_escape),
+  qw(url_unescape xml_escape xor_encode xss_escape);
 
 # camelize
 is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result';
@@ -417,7 +417,8 @@ is MojoMonkeyTest::yang(), 'yang', 'right result';
 
 # monkey_patch (with name)
 SKIP: {
-  skip 'Sub::Util required!', 2 unless eval { require Sub::Util; 1 };
+  skip 'Sub::Util required!', 2
+    unless eval { require Sub::Util; !!Sub::Util->can('set_subname') };
   is Sub::Util::subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo',
     'right name';
   is Sub::Util::subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar',
@@ -453,4 +454,10 @@ is tablify([['a', '', 'b'], ['c', '', 'd']]), "a    b\nc    d\n",
 # dumper
 is dumper([1, 2]), "[\n  1,\n  2\n]\n", 'right result';
 
+# term_escape
+is term_escape("Accept: */*\x0d\x0a"), "Accept: */*\\x0d\x0a", 'right result';
+is term_escape("\t\b\r\n\f"), "\\x09\\x08\\x0d\n\\x0c", 'right result';
+is term_escape("\x00\x09\x0b\x1f\x7f\x80\x9f"),
+  '\x00\x09\x0b\x1f\x7f\x80\x9f', 'right result';
+
 done_testing();
@@ -448,7 +448,7 @@ $ua->websocket(
 );
 Mojo::IOLoop->start;
 is $stash->{finished}, 1, 'finish event has been emitted once';
-like $log, qr/Inactivity timeout\./, 'right log message';
+like $log, qr/Inactivity timeout/, 'right log message';
 app->log->unsubscribe(message => $msg);
 
 # Ping/pong
@@ -115,10 +115,8 @@ ok $t->app->routes->is_hidden('on'),                  'is hidden';
 ok $t->app->routes->is_hidden('param'),               'is hidden';
 ok $t->app->routes->is_hidden('redirect_to'),         'is hidden';
 ok $t->app->routes->is_hidden('render'),              'is hidden';
-ok $t->app->routes->is_hidden('render_exception'),    'is hidden';
 ok $t->app->routes->is_hidden('render_later'),        'is hidden';
 ok $t->app->routes->is_hidden('render_maybe'),        'is hidden';
-ok $t->app->routes->is_hidden('render_not_found'),    'is hidden';
 ok $t->app->routes->is_hidden('render_to_string'),    'is hidden';
 ok $t->app->routes->is_hidden('rendered'),            'is hidden';
 ok $t->app->routes->is_hidden('req'),                 'is hidden';
@@ -146,7 +144,7 @@ my $log = '';
 my $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->app->helper(replaced_helper => sub { });
 $t->app->helper(replaced_helper => sub { });
-like $log, qr/Helper "replaced_helper" already exists, replacing\./,
+like $log, qr/Helper "replaced_helper" already exists, replacing/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -176,7 +174,7 @@ $t->get_ok('/plugin-test-some_plugin2/register')->status_isnt(500)
   ->status_is(404)->header_is(Server => 'Mojolicious (Perl)')
   ->content_unlike(qr/Something/)->content_like(qr/Page not found/);
 like $log,
-  qr/Class "MojoliciousTest::Plugin::Test::SomePlugin2" is not a controller\./,
+  qr/Class "MojoliciousTest::Plugin::Test::SomePlugin2" is not a controller/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -196,7 +194,7 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/foo/baz')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_unlike(qr/Something/)
   ->content_like(qr/Page not found/);
-like $log, qr/Action not found in controller\./, 'right message';
+like $log, qr/Action not found in controller/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
 # Foo::render (action not allowed)
@@ -205,7 +203,7 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/foo/render')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_like(qr/Page not found/);
-like $log, qr/Action "render" is not allowed\./, 'right message';
+like $log, qr/Action "render" is not allowed/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
 # Foo::yada (action-less template)
@@ -224,11 +222,11 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/foo/syntaxerror')->status_is(500)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_like(qr/Missing right curly/);
-like $log, qr/Rendering template "syntaxerror.html.epl"\./, 'right message';
+like $log, qr/Rendering template "syntaxerror.html.epl"/, 'right message';
 like $log, qr/Missing right curly/, 'right message';
-like $log, qr/Template "exception.development.html.ep" not found\./,
+like $log, qr/Template "exception.development.html.ep" not found/,
   'right message';
-like $log, qr/Rendering template "exception.html.epl"\./, 'right message';
+like $log, qr/Rendering template "exception.html.epl"/, 'right message';
 like $log, qr/500 Internal Server Error/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -268,8 +266,7 @@ $url->path('/fun/time');
 $t->get_ok($url => {'X-Test' => 'Hi there!'})->status_is(200)
   ->header_is('X-Bender' => undef)->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('Have fun!');
-like $log,
-  qr!Rendering cached template "foo/fun\.html\.ep" from DATA section\.!,
+like $log, qr!Rendering cached template "foo/fun\.html\.ep" from DATA section!,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -372,7 +369,7 @@ $log = '';
 $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/another')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)');
-like $log, qr/Controller "MojoliciousTest::Another" does not exist\./,
+like $log, qr/Controller "MojoliciousTest::Another" does not exist/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -498,7 +495,7 @@ $log = '';
 $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/redispatch')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('Redispatch!');
-like $log, qr/Routing to application "SingleFileTestApp::Redispatch"\./,
+like $log, qr/Routing to application "SingleFileTestApp::Redispatch"/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -538,13 +535,13 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/suspended')->status_is(200)
   ->header_is(Server        => 'Mojolicious (Perl)')
   ->header_is('X-Suspended' => '0, 1, 1, 2')->content_is('Have fun!');
-like $log, qr!GET "/suspended"\.!, 'right message';
+like $log, qr!GET "/suspended"!, 'right message';
 like $log,
-  qr/Routing to controller "MojoliciousTest::Foo" and action "suspended"\./,
+  qr/Routing to controller "MojoliciousTest::Foo" and action "suspended"/,
   'right message';
-like $log, qr/Routing to controller "MojoliciousTest::Foo" and action "fun"\./,
+like $log, qr/Routing to controller "MojoliciousTest::Foo" and action "fun"/,
   'right message';
-like $log, qr!Rendering template "foo/fun.html.ep" from DATA section\.!,
+like $log, qr!Rendering template "foo/fun.html.ep" from DATA section!,
   'right message';
 like $log, qr/200 OK/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
@@ -579,4 +576,8 @@ $t->get_ok('/foo/session')->status_is(200)
 $t->get_ok('/rss.xml')->status_is(200)->content_type_is('application/rss+xml')
   ->content_like(qr!<\?xml version="1.0" encoding="UTF-8"\?><rss />!);
 
+# Abstract methods
+eval { Mojolicious::Plugin->register };
+like $@, qr/Method "register" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -9,7 +9,7 @@ use File::Temp 'tempdir';
 use Mojolicious::Command;
 
 # Application
-my $command = Mojolicious::Command->new;
+my $command = Mojolicious::Command->new(quiet => 1);
 isa_ok $command->app, 'Mojo',        'right application';
 isa_ok $command->app, 'Mojolicious', 'right application';
 
@@ -33,4 +33,8 @@ open my $xml, '<', $command->rel_file('123.xml');
 is join('', <$xml>), "seems\nto\nwork", 'right result';
 chdir $cwd;
 
+# Abstract methods
+eval { Mojolicious::Command->run };
+like $@, qr/Method "run" not implemented by subclass/, 'right error';
+
 done_testing();
@@ -10,6 +10,9 @@ use Test::More;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
+use Cwd 'cwd';
+use File::Temp 'tempdir';
+
 # Make sure @ARGV is not changed
 {
   local $ENV{MOJO_MODE};
@@ -33,6 +36,16 @@ my $commands = Mojolicious::Commands->new;
   local $ENV{GATEWAY_INTERFACE} = 'CGI/1.1';
   is $commands->detect, 'cgi', 'right environment';
 }
+{
+  local @ENV{qw(PLACK_ENV PATH_INFO GATEWAY_INTERFACE)};
+  is $commands->detect, undef, 'no environment';
+}
+{
+  local $ENV{PLACK_ENV} = 'production';
+  is ref Mojolicious::Commands->new->run, 'CODE', 'right reference';
+  local $ENV{MOJO_NO_DETECT} = 1;
+  isnt ref Mojolicious::Commands->new->run, 'CODE', 'not a CODE reference';
+}
 
 # Run command
 is ref Mojolicious::Commands->new->run('psgi'), 'CODE', 'right reference';
@@ -76,6 +89,40 @@ is $app->start('test_command'), 'works!', 'right result';
 ok $commands->description, 'has a description';
 like $commands->message,   qr/COMMAND/, 'has a message';
 like $commands->hint,      qr/help/, 'has a hint';
+my $buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  local $ENV{HARNESS_ACTIVE} = 0;
+  $commands->run;
+}
+like $buffer, qr/Usage: APPLICATION COMMAND \[OPTIONS\].*daemon.*version/s,
+  'right output';
+
+# help
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $commands->run('help', 'generate', 'lite_app');
+}
+like $buffer, qr/Usage: APPLICATION generate lite_app \[NAME\]/,
+  'right output';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $commands->run('generate', 'app', '-h');
+}
+like $buffer, qr/Usage: APPLICATION generate app \[NAME\]/, 'right output';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $commands->run('generate', 'lite_app', '--help');
+}
+like $buffer, qr/Usage: APPLICATION generate lite_app \[NAME\]/,
+  'right output';
 
 # cgi
 require Mojolicious::Command::cgi;
@@ -100,43 +147,149 @@ require Mojolicious::Command::eval;
 my $eval = Mojolicious::Command::eval->new;
 ok $eval->description, 'has a description';
 like $eval->usage, qr/eval/, 'has usage information';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $eval->run('-v', 'app->controller_class');
+}
+like $buffer, qr/Mojolicious::Controller/, 'right output';
 
 # generate
 require Mojolicious::Command::generate;
 my $generator = Mojolicious::Command::generate->new;
 ok $generator->description, 'has a description';
 like $generator->message,   qr/generate/, 'has a message';
-like $commands->hint,       qr/help/, 'has a hint';
+like $generator->hint,      qr/help/, 'has a hint';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  local $ENV{HARNESS_ACTIVE} = 0;
+  $generator->run;
+}
+like $buffer,
+  qr/Usage: APPLICATION generate GENERATOR \[OPTIONS\].*lite_app.*plugin/s,
+  'right output';
 
 # generate app
 require Mojolicious::Command::generate::app;
 $app = Mojolicious::Command::generate::app->new;
 ok $app->description, 'has a description';
 like $app->usage, qr/app/, 'has usage information';
+my $cwd = cwd;
+my $dir = tempdir CLEANUP => 1;
+chdir $dir;
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $app->run;
+}
+like $buffer, qr/my_app/, 'right output';
+ok -e $app->rel_file('my_app/script/my_app'), 'script exists';
+ok -e $app->rel_file('my_app/lib/MyApp.pm'),  'application class exists';
+ok -e $app->rel_file('my_app/lib/MyApp/Controller/Example.pm'),
+  'controller exists';
+ok -e $app->rel_file('my_app/t/basic.t'),         'test exists';
+ok -e $app->rel_file('my_app/public/index.html'), 'static file exists';
+ok -e $app->rel_file('my_app/templates/layouts/default.html.ep'),
+  'layout exists';
+ok -e $app->rel_file('my_app/templates/example/welcome.html.ep'),
+  'template exists';
+chdir $cwd;
 
 # generate lite_app
 require Mojolicious::Command::generate::lite_app;
 $app = Mojolicious::Command::generate::lite_app->new;
 ok $app->description, 'has a description';
 like $app->usage, qr/lite_app/, 'has usage information';
+$dir = tempdir CLEANUP => 1;
+chdir $dir;
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $app->run;
+}
+like $buffer, qr/myapp\.pl/, 'right output';
+ok -e $app->rel_file('myapp.pl'), 'app exists';
+chdir $cwd;
 
 # generate makefile
 require Mojolicious::Command::generate::makefile;
 my $makefile = Mojolicious::Command::generate::makefile->new;
 ok $makefile->description, 'has a description';
 like $makefile->usage, qr/makefile/, 'has usage information';
+$dir = tempdir CLEANUP => 1;
+chdir $dir;
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $makefile->run;
+}
+like $buffer, qr/Makefile\.PL/, 'right output';
+ok -e $app->rel_file('Makefile.PL'), 'Makefile.PL exists';
+chdir $cwd;
 
 # generate plugin
 require Mojolicious::Command::generate::plugin;
 my $plugin = Mojolicious::Command::generate::plugin->new;
 ok $plugin->description, 'has a description';
 like $plugin->usage, qr/plugin/, 'has usage information';
+$dir = tempdir CLEANUP => 1;
+chdir $dir;
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $plugin->run;
+}
+like $buffer, qr/MyPlugin\.pm/, 'right output';
+ok -e $app->rel_file(
+  'Mojolicious-Plugin-MyPlugin/lib/Mojolicious/Plugin/MyPlugin.pm'),
+  'class exists';
+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';
+chdir $cwd;
 
 # get
 require Mojolicious::Command::get;
 my $get = Mojolicious::Command::get->new;
 ok $get->description, 'has a description';
 like $get->usage, qr/get/, 'has usage information';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $get->run('/');
+}
+like $buffer, qr/Your Mojo is working!/, 'right output';
+$get->app->hook(
+  before_dispatch => sub {
+    my $c = shift;
+    return $c->render(text => '<p>works</p>')
+      if $c->req->url->path->contains('/html');
+    $c->render(json => {works => 'too'})
+      if $c->req->url->path->contains('/json');
+  }
+);
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $get->run('/html', 'p', 'text');
+}
+like $buffer, qr/works/, 'right output';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $get->run('/json', '/works');
+}
+like $buffer, qr/too/, 'right output';
 
 # inflate
 require Mojolicious::Command::inflate;
@@ -161,6 +314,22 @@ require Mojolicious::Command::routes;
 my $routes = Mojolicious::Command::routes->new;
 ok $routes->description, 'has a description';
 like $routes->usage, qr/routes/, 'has usage information';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $routes->run;
+}
+like $buffer,   qr!/\*whatever!, 'right output';
+unlike $buffer, qr!/\(\.\+\)\?!, 'not verbose';
+$buffer = '';
+{
+  open my $handle, '>', \$buffer;
+  local *STDOUT = $handle;
+  $routes->run('-v');
+}
+like $buffer, qr!/\*whatever!, 'right output';
+like $buffer, qr!/\(\.\+\)\?!, 'verbose';
 
 # test
 require Mojolicious::Command::test;
@@ -41,8 +41,6 @@ $t->get_ok('/hello')->status_is(200)->content_is("Hello from the main app!\n");
 
 # Session
 $t->get_ok('/primary')->status_is(200)->content_is(1);
-
-# Session again
 $t->get_ok('/primary')->status_is(200)->content_is(2);
 
 # Session in external app
@@ -177,7 +177,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/x/1/">Test</a>
 <form action="/x/1/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -210,7 +210,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/x/%E2%99%A5/">Test</a>
 <form action="/x/%E2%99%A5/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -278,7 +278,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/">Test</a>
 <form action="/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -295,7 +295,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/">Test</a>
 <form action="/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -312,7 +312,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/">Test</a>
 <form action="/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -337,7 +337,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/%E2%99%A5/123/">Test</a>
 <form action="/%E2%99%A5/123/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -22,7 +22,7 @@ works ♥!Insecure!Insecure!
 too!works!!!Mojolicious::Plugin::Config::Sandbox
 <a href="/">Test</a>
 <form action="/%E2%98%83">
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -213,11 +213,11 @@ my $log = '';
 my $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/suspended?ok=1')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('suspended!');
-like $log, qr!GET "/suspended"\.!,      'right message';
-like $log, qr/Routing to a callback\./, 'right message';
-like $log, qr/Nothing has been rendered, expecting delayed response\./,
+like $log, qr!GET "/suspended"!,      'right message';
+like $log, qr/Routing to a callback/, 'right message';
+like $log, qr/Nothing has been rendered, expecting delayed response/,
   'right message';
-like $log, qr/Rendering inline template "f75d6f5993c626fa8049366389f77928"\./,
+like $log, qr/Rendering inline template "f75d6f5993c626fa8049366389f77928"/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -271,8 +271,8 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/bridge2stash')->status_is(200)
   ->content_is(
   "stash too!cookie!!signed_cookie!!bad_cookie--12345678!session!flash!\n");
-like $log, qr/Cookie "foo" not signed\./,        'right message';
-like $log, qr/Cookie "bad" has bad signature\./, 'right message';
+like $log, qr/Cookie "foo" is not signed/,       'right message';
+like $log, qr/Cookie "bad" has a bad signature/, 'right message';
 ok $t->tx->res->cookie('mojolicious')->httponly,
   'session cookie has HttpOnly flag';
 $t->app->log->unsubscribe(message => $cb);
@@ -28,7 +28,7 @@ my $log = '';
 my $cb = app->log->on(message => sub { $log .= pop });
 $path = abs_path catfile(dirname(__FILE__), 'json_config_lite_app_abs.json');
 plugin JSONConfig => {file => $path};
-like $log, qr/Reading configuration file "\Q$path\E"\./, 'right message';
+like $log, qr/Reading configuration file "\Q$path\E"/, 'right message';
 app->log->unsubscribe(message => $cb);
 is $config->{foo},          'bar',            'right value';
 is $config->{hello},        'there',          'right value';
@@ -68,8 +68,8 @@ sub startup {
   $r->route('/exceptional/:action')->to('exceptional#');
 
   # /exceptional_too/*
-  $r->bridge('/exceptional_too')->to('exceptional#this_one_might_die')
-    ->route('/:action');
+  $r->route('/exceptional_too')->inline(1)
+    ->to('exceptional#this_one_might_die')->route('/:action');
 
   # /fun/time
   $r->fun('/time')->to('foo#fun');
@@ -128,13 +128,13 @@ sub startup {
   # /withblock (template with blocks)
   $r->route('/withblock')->to('foo#withBlock');
 
-  # /staged (authentication with bridges)
-  my $b = $r->bridge('/staged')->to('foo#stage1', return => 1);
+  # /staged (authentication with intermediate destination)
+  my $b = $r->route('/staged')->inline(1)->to('foo#stage1', return => 1);
   $b->route->to(action => 'stage2');
 
-  # /suspended (suspended bridge)
-  $r->bridge('/suspended')->to('foo#suspended')->bridge->to('foo#suspended')
-    ->route->to('foo#fun');
+  # /suspended (suspended intermediate destination)
+  $r->route('/suspended')->inline(1)->to('foo#suspended')->route->inline(1)
+    ->to('foo#suspended')->route->to('foo#fun');
 
   # /longpoll (long polling)
   $r->route('/longpoll')->to('foo#longpoll');
@@ -24,7 +24,7 @@ app->defaults(default => 23);
 my $log = '';
 my $cb = app->log->on(message => sub { $log .= pop });
 is app->secrets->[0], app->moniker, 'secret defaults to moniker';
-like $log, qr/Your secret passphrase needs to be changed!!!/, 'right message';
+like $log, qr/Your secret passphrase needs to be changed/, 'right message';
 app->log->unsubscribe(message => $cb);
 
 # Test helpers
@@ -80,6 +80,9 @@ get '/alternatives/:char' => [char => [qw(☃ ♥)]] => sub {
 get '/optional/:middle/placeholder' =>
   {middle => 'none', inline => '<%= $middle %>-<%= url_for =%>'};
 
+get '/optional/:param' =>
+  {param => undef, inline => '%= param("param") // "undef"'};
+
 get '/alterformat' => [format => ['json']] => {format => 'json'} => sub {
   my $c = shift;
   $c->render(text => $c->stash('format'));
@@ -479,11 +482,7 @@ $t->get_ok('/☃')->status_is(200)
 
 # Umlaut
 $t->get_ok('/uni/aäb')->status_is(200)->content_is('/uni/a%C3%A4b');
-
-# Escaped umlaut
 $t->get_ok('/uni/a%E4b')->status_is(200)->content_is('/uni/a%C3%A4b');
-
-# Escaped umlaut again
 $t->get_ok('/uni/a%C3%A4b')->status_is(200)->content_is('/uni/a%C3%A4b');
 
 # Captured snowman
@@ -525,21 +524,13 @@ $t->post_ok('/')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
 $t->get_ok('/alternatives/☃')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('/alternatives/%E2%98%83');
-
-# Different Unicode alternative
 $t->get_ok('/alternatives/♥')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('/alternatives/%E2%99%A5');
-
-# Invalid alternative
 $t->get_ok('/alternatives/☃23')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is("Oops!\n");
-
-# Invalid alternative
 $t->get_ok('/alternatives')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is("Oops!\n");
-
-# Invalid alternative
 $t->get_ok('/alternatives/test')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is("Oops!\n");
 
@@ -547,29 +538,29 @@ $t->get_ok('/alternatives/test')->status_is(404)
 $t->get_ok('/optional/test/placeholder')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('test-/optional/test/placeholder');
-
-# Optional placeholder in the middle without value
 $t->get_ok('/optional/placeholder')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('none-/optional/none/placeholder');
 
+# Optional placeholder
+$t->get_ok('/optional')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is("undef\n");
+$t->get_ok('/optional/test')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is("test\n");
+$t->get_ok('/optional?param=test')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is("undef\n");
+
 # No format
 $t->get_ok('/alterformat')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('json');
-
-# Format alternative
 $t->get_ok('/alterformat.json')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('json');
-
-# Invalid format alternative
 $t->get_ok('/alterformat.html')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is("Oops!\n");
 
 # No format
 $t->get_ok('/noformat')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('xml/noformat');
-
-# Invalid format
 $t->get_ok('/noformat.xml')->status_is(404)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is("Oops!\n");
 
@@ -604,11 +595,9 @@ $t->get_ok('/multi/B?foo=A&foo=E&baz=C&yada=D&yada=text&yada=fail')
 $t->get_ok('/multi/B?baz=C')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('BC');
 
-# Reserved stash value
+# Reserved stash values
 $t->get_ok('/reserved?data=just-works')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('just-worksdata');
-
-# More reserved stash values
 $t->get_ok('/reserved?data=just-works&json=test')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('just-worksdata,json');
@@ -689,11 +678,9 @@ $t->get_ok('//sri:foo@/stream' => form => {foo => 'bar'})->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->content_like(qr!^foobarsri:foohttp://127\.0\.0\.1:\d+/stream$!);
 
-# Not ajax
+# Ajax
 $t->get_ok('/maybe/ajax')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('not ajax');
-
-# Ajax
 $t->get_ok('/maybe/ajax' => {'X-Requested-With' => 'XMLHttpRequest'})
   ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('is ajax');
@@ -780,8 +767,7 @@ $log = '';
 $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/source?fail=1')->status_is(404)->header_is('X-Missing' => 1)
   ->content_is("Oops!\n");
-like $log, qr/File "does_not_exist.txt" not found, public directory missing\?/,
-  'right message';
+like $log, qr/Static file "does_not_exist.txt" not found/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
 # With body and max message size
@@ -789,8 +775,8 @@ $t->app->log->unsubscribe(message => $cb);
   local $ENV{MOJO_MAX_MESSAGE_SIZE} = 1024;
   $t->get_ok('/', '1234' x 1024)->status_is(200)
     ->header_is(Connection => 'close')
-    ->content_is(
-    "413\n/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
+    ->content_is("Maximum message size exceeded\n"
+      . "/root.html\n/root.html\n/root.html\n/root.html\n/root.html\n");
 }
 
 # Relaxed placeholder
@@ -810,19 +796,13 @@ $t->get_ok('/foo_wildcard_too/')->status_is(404);
 $t->get_ok('/with/header/condition',
   {'X-Secret-Header' => 'bar', 'X-Another-Header' => 'baz'})->status_is(200)
   ->content_is("Test ok!\n");
-
-# Missing headers
 $t->get_ok('/with/header/condition')->status_is(404)->content_like(qr/Oops!/);
-
-# Missing header
 $t->get_ok('/with/header/condition' => {'X-Secret-Header' => 'bar'})
   ->status_is(404)->content_like(qr/Oops!/);
 
 # Single header condition
 $t->post_ok('/with/header/condition' => {'X-Secret-Header' => 'bar'} => 'bar')
   ->status_is(200)->content_is('foo bar');
-
-# Missing header
 $t->post_ok('/with/header/condition' => {} => 'bar')->status_is(404)
   ->content_like(qr/Oops!/);
 
@@ -1102,7 +1082,7 @@ Test ok!
 
 @@ root.html.epl
 % my $c = shift;
-<% if (my $err = $c->req->error) { =%><%= "$err->{advice}\n" %><% } =%>
+<% if (my $err = $c->req->error) { =%><%= "$err->{message}\n" %><% } =%>
 %== $c->url_for('root_path')
 %== $c->url_for('root_path')
 %== $c->url_for('root_path')
@@ -248,7 +248,7 @@ ok !$t->tx->kept_alive, 'connection was not kept alive';
 ok !$t->tx->keep_alive, 'connection will not be kept alive';
 is $stash->{finished}, 1, 'finish event has been emitted once';
 ok $stash->{destroyed}, 'controller has been destroyed';
-unlike $log, qr/Nothing has been rendered, expecting delayed response\./,
+unlike $log, qr/Nothing has been rendered, expecting delayed response/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -393,7 +393,7 @@ $t->get_ok('/longpoll/static/delayed')->status_is(200)
   ->content_is("Hello Mojo from a static file!\n");
 is $stash->{finished}, 1, 'finish event has been emitted once';
 ok $stash->{destroyed}, 'controller has been destroyed';
-like $log, qr/Nothing has been rendered, expecting delayed response\./,
+like $log, qr/Nothing has been rendered, expecting delayed response/,
   'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -43,8 +43,8 @@ $t->get_ok('/')->status_is(200)->header_is('X-Route' => 'root')
   ->content_is(<<'EOF');
 http://example.com/rebased/
 <script src="/rebased/mojo/jquery/jquery.js"></script>
-<img src="/rebased/images/test.png" />
-<link href="//example.com/base.css" rel="stylesheet" />
+<img src="/rebased/images/test.png">
+<link href="//example.com/base.css" rel="stylesheet">
 <a href="mailto:sri@example.com">Contact</a>
 http://example.com/rebased
 http://example.com/rebased/foo
@@ -58,8 +58,8 @@ EOF
 $t->get_ok('/foo')->status_is(200)->header_is('X-Route' => 'foo')
   ->content_is(<<EOF);
 http://example.com/rebased/
-<link href="/rebased/b.css" media="test" rel="stylesheet" />
-<img alt="Test" src="/rebased/images/test.png" />
+<link href="/rebased/b.css" media="test" rel="stylesheet">
+<img alt="Test" src="/rebased/images/test.png">
 http://example.com/rebased/foo
 http://example.com/rebased
 /rebased
@@ -78,8 +78,8 @@ ok $t->ua->cookie_jar->find($t->ua->server->url->path('/foo')),
 # Rebased route with message from flash
 $t->get_ok('/foo')->status_is(200)->content_is(<<EOF);
 http://example.com/rebased/works!too!
-<link href="/rebased/b.css" media="test" rel="stylesheet" />
-<img alt="Test" src="/rebased/images/test.png" />
+<link href="/rebased/b.css" media="test" rel="stylesheet">
+<img alt="Test" src="/rebased/images/test.png">
 http://example.com/rebased/foo
 http://example.com/rebased
 /rebased
@@ -92,8 +92,8 @@ $t->get_ok('/baz')->status_is(200)->header_is('X-Route' => 'baz')
   ->content_is(<<'EOF');
 http://example.com/rebased/
 <script src="/rebased/mojo/jquery/jquery.js"></script>
-<img src="/rebased/images/test.png" />
-<link href="//example.com/base.css" rel="stylesheet" />
+<img src="/rebased/images/test.png">
+<link href="//example.com/base.css" rel="stylesheet">
 <a href="mailto:sri@example.com">Contact</a>
 http://example.com/rebased/baz
 http://example.com/rebased/foo
@@ -48,7 +48,7 @@ my $log = '';
 my $cb = $c->app->log->on(message => sub { $log .= pop });
 $c->stash->{handler} = 'not_defined';
 is $renderer->render($c), undef, 'return undef for unrecognized handler';
-like $log, qr/No handler for "not_defined" available\./, 'right message';
+like $log, qr/No handler for "not_defined" available/, 'right message';
 $c->app->log->unsubscribe(message => $cb);
 
 # Default template name
@@ -59,7 +59,7 @@ is $c->app->renderer->template_for($c), 'foo/bar', 'right template name';
 $log = '';
 $cb = $c->app->log->on(message => sub { $log .= pop });
 $c->cookie(foo => 'x' x 4097);
-like $log, qr/Cookie "foo" is bigger than 4096 bytes\./, 'right message';
+like $log, qr/Cookie "foo" is bigger than 4096 bytes/, 'right message';
 $c->app->log->unsubscribe(message => $cb);
 
 # Nested helpers
@@ -57,10 +57,10 @@ $r->route('/:controller/testedit')->to(action => 'testedit');
 $test->route('/delete/(id)', id => qr/\d+/)->to(action => 'delete', id => 23);
 
 # /test2
-my $test2 = $r->bridge('/test2/')->to(controller => 'test2');
+my $test2 = $r->route('/test2/')->inline(1)->to(controller => 'test2');
 
 # /test2 (inline)
-my $test4 = $test2->bridge('/')->to(controller => 'index');
+my $test4 = $test2->route('/')->inline(1)->to(controller => 'index');
 
 # /test2/foo
 $test4->route('/foo')->to(controller => 'baz');
@@ -122,10 +122,10 @@ $r->route('/format7', format => [qw(foo foobar)])->to('perl#rocks');
 
 # /articles/1/edit
 # /articles/1/delete
-my $bridge = $r->bridge('/articles/:id')
+my $inline = $r->route('/articles/:id')->inline(1)
   ->to(controller => 'articles', action => 'load', format => 'html');
-$bridge->route('/edit')->to(controller => 'articles', action => 'edit');
-$bridge->route('/delete')
+$inline->route('/edit')->to(controller => 'articles', action => 'edit');
+$inline->route('/delete')
   ->to(controller => 'articles', action => 'delete', format => undef)
   ->name('articles_delete');
 
@@ -213,7 +213,7 @@ $r->route('/partial')->detour('foo#bar');
 
 # GET  /similar/*
 # POST /similar/too
-my $similar = $r->bridge('/similar');
+my $similar = $r->route('/similar')->inline(1);
 $similar->route('/:something')->via('GET')->to('similar#get');
 $similar->route('/too')->via('POST')->to('similar#post');
 
@@ -711,7 +711,7 @@ is_deeply $m->stack, [{controller => 'test-test', action => 'test'}],
 is $m->path_for->{path}, '/simple/form', 'right path';
 is $m->path_for('current')->{path}, '/simple/form', 'right path';
 
-# Special edge case with nested bridges (regex)
+# Special edge case with intermediate destinations (regex)
 $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/regex/alternatives/foo'});
 is_deeply $m->stack,
@@ -12,8 +12,6 @@ patch 'more_tags';
 
 get 'small_tags';
 
-get 'circle';
-
 get 'tags_with_error';
 
 any [qw(GET POST)] => 'links';
@@ -53,8 +51,8 @@ is app->select_field(country => $values),
 
 # Basic tags
 $t->options_ok('/tags')->status_is(200)->content_is(<<EOF);
-<foo />
-<foo bar="baz" />
+<foo></foo>
+<foo bar="baz"></foo>
 <foo one="t&lt;wo" three="four">Hello</foo>
 <div data-my-test-id="1" data-name="test">some content</div>
 <div data="bar">some content</div>
@@ -69,7 +67,6 @@ EOF
 # Shortcut
 $t->get_ok('/small_tags')->status_is(200)->content_is(<<EOF);
 <div id="&amp;lt;">test &amp; 123</div>
-<div id="&lt;">test&nbsp;321</div>
 <div>
   <p id="0">just</p>
   <p>0</p>
@@ -77,13 +74,6 @@ $t->get_ok('/small_tags')->status_is(200)->content_is(<<EOF);
 <div>works</div>
 EOF
 
-# XML
-$t->get_ok('/circle.svg')->status_is(200)->content_is(<<EOF);
-<svg>
-<circle cx="75" cy="55" fill="red" r="35" stroke="black" stroke-width="2" />
-</svg>
-EOF
-
 # Tags with error
 $t->get_ok('/tags_with_error')->status_is(200)->content_is(<<EOF);
 <bar class="field-with-error">0</bar>
@@ -132,7 +122,7 @@ EOF
 
 # Stylesheets
 $t->get_ok('/style')->status_is(200)->content_is(<<EOF);
-<link href="/foo.css" rel="stylesheet" />
+<link href="/foo.css" rel="stylesheet">
 <style>/*<![CDATA[*/
 
   body {color: #000}
@@ -149,33 +139,33 @@ EOF
 $t->get_ok('/basicform')->status_is(200)->content_is(<<EOF);
 <form action="/links">
   <label for="foo">&lt;Foo&gt;</label>
-  <input name="foo" type="text" value="bar" />
+  <input name="foo" type="text" value="bar">
   <label for="bar">
     Bar<br>
   </label>
-  <input class="test" name="bar" type="text" value="baz" />
-  <input name="yada" type="text" value="" />
-  <input class="tset" name="baz" value="yada" />
-  <input type="submit" value="Ok" />
+  <input class="test" name="bar" type="text" value="baz">
+  <input name="yada" type="text" value="">
+  <input class="tset" name="baz" value="yada">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Text input fields
 $t->post_ok('/text')->status_is(200)->content_is(<<'EOF');
 <form action="/text" method="POST">
-  <input class="foo" name="color" type="color" value="#ffffff" />
-  <input class="foo" name="date" type="date" value="2012-12-12" />
-  <input class="foo" name="dt" type="datetime" value="2012-12-12T23:59:59Z" />
-  <input class="foo" name="email" type="email" value="nospam@example.com" />
-  <input class="foo" name="month" type="month" value="2012-12" />
-  <input class="foo" name="number" type="number" value="23" />
-  <input class="foo" name="range" type="range" value="24" />
-  <input class="foo" name="search" type="search" value="perl" />
-  <input class="foo" name="tel" type="tel" value="123456789" />
-  <input class="foo" name="time" type="time" value="23:59:59" />
-  <input class="foo" name="url" type="url" value="http://mojolicio.us" />
-  <input class="foo" name="week" type="week" value="2012-W16" />
-  <input type="submit" value="Ok" />
+  <input class="foo" name="color" type="color" value="#ffffff">
+  <input class="foo" name="date" type="date" value="2012-12-12">
+  <input class="foo" name="dt" type="datetime" value="2012-12-12T23:59:59Z">
+  <input class="foo" name="email" type="email" value="nospam@example.com">
+  <input class="foo" name="month" type="month" value="2012-12">
+  <input class="foo" name="number" type="number" value="23">
+  <input class="foo" name="range" type="range" value="24">
+  <input class="foo" name="search" type="search" value="perl">
+  <input class="foo" name="tel" type="tel" value="123456789">
+  <input class="foo" name="time" type="time" value="23:59:59">
+  <input class="foo" name="url" type="url" value="http://mojolicio.us">
+  <input class="foo" name="week" type="week" value="2012-W16">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
@@ -197,64 +187,64 @@ $t->post_ok(
   }
 )->status_is(200)->content_is(<<'EOF');
 <form action="/text" method="POST">
-  <input class="foo" name="color" type="color" value="#000000" />
-  <input class="foo" name="date" type="date" value="2012-12-13" />
-  <input class="foo" name="dt" type="datetime" value="2012-12-13T23:59:59Z" />
-  <input class="foo" name="email" type="email" value="spam@example.com" />
-  <input class="foo" name="month" type="month" value="2012-11" />
-  <input class="foo" name="number" type="number" value="25" />
-  <input class="foo" name="range" type="range" value="26" />
-  <input class="foo" name="search" type="search" value="c" />
-  <input class="foo" name="tel" type="tel" value="987654321" />
-  <input class="foo" name="time" type="time" value="23:59:58" />
-  <input class="foo" name="url" type="url" value="http://example.com" />
-  <input class="foo" name="week" type="week" value="2012-W17" />
-  <input type="submit" value="Ok" />
+  <input class="foo" name="color" type="color" value="#000000">
+  <input class="foo" name="date" type="date" value="2012-12-13">
+  <input class="foo" name="dt" type="datetime" value="2012-12-13T23:59:59Z">
+  <input class="foo" name="email" type="email" value="spam@example.com">
+  <input class="foo" name="month" type="month" value="2012-11">
+  <input class="foo" name="number" type="number" value="25">
+  <input class="foo" name="range" type="range" value="26">
+  <input class="foo" name="search" type="search" value="c">
+  <input class="foo" name="tel" type="tel" value="987654321">
+  <input class="foo" name="time" type="time" value="23:59:58">
+  <input class="foo" name="url" type="url" value="http://example.com">
+  <input class="foo" name="week" type="week" value="2012-W17">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Checkboxes
 $t->get_ok('/multibox')->status_is(200)->content_is(<<EOF);
 <form action="/multibox">
-  <input name="foo" type="checkbox" value="one" />
-  <input name="foo" type="checkbox" value="two" />
-  <input type="submit" value="Ok" />
+  <input name="foo" type="checkbox" value="one">
+  <input name="foo" type="checkbox" value="two">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Checkboxes with one value
 $t->get_ok('/multibox?foo=two')->status_is(200)->content_is(<<EOF);
 <form action="/multibox">
-  <input name="foo" type="checkbox" value="one" />
-  <input checked="checked" name="foo" type="checkbox" value="two" />
-  <input type="submit" value="Ok" />
+  <input name="foo" type="checkbox" value="one">
+  <input checked name="foo" type="checkbox" value="two">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Checkboxes with one right and one wrong value
 $t->get_ok('/multibox?foo=one&foo=three')->status_is(200)->content_is(<<EOF);
 <form action="/multibox">
-  <input checked="checked" name="foo" type="checkbox" value="one" />
-  <input name="foo" type="checkbox" value="two" />
-  <input type="submit" value="Ok" />
+  <input checked name="foo" type="checkbox" value="one">
+  <input name="foo" type="checkbox" value="two">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Checkboxes with wrong value
 $t->get_ok('/multibox?foo=bar')->status_is(200)->content_is(<<EOF);
 <form action="/multibox">
-  <input name="foo" type="checkbox" value="one" />
-  <input name="foo" type="checkbox" value="two" />
-  <input type="submit" value="Ok" />
+  <input name="foo" type="checkbox" value="one">
+  <input name="foo" type="checkbox" value="two">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
 # Checkboxes with two values
 $t->get_ok('/multibox?foo=two&foo=one')->status_is(200)->content_is(<<EOF);
 <form action="/multibox">
-  <input checked="checked" name="foo" type="checkbox" value="one" />
-  <input checked="checked" name="foo" type="checkbox" value="two" />
-  <input type="submit" value="Ok" />
+  <input checked name="foo" type="checkbox" value="one">
+  <input checked name="foo" type="checkbox" value="two">
+  <input type="submit" value="Ok">
 </form>
 EOF
 
@@ -262,63 +252,63 @@ EOF
 $t->get_ok('/form/lala?a=2&b=0&c=2&d=3&escaped=1%22+%222')->status_is(200)
   ->content_is(<<EOF);
 <form action="/links" method="post">
-  <input name="foo" />
+  <input name="foo">
 </form>
 <form action="/form/24" method="post">
-  <input name="foo" type="text" />
-  <input data-id="1" data-name="test" name="foo" type="text" value="1" />
-  <input data="ok" name="foo" type="text" value="1" />
-  <input name="foo" type="checkbox" value="1" />
-  <input checked="checked" name="a" type="checkbox" value="2" />
-  <input name="b" type="radio" value="1" />
-  <input checked="checked" name="b" type="radio" value="0" />
-  <input name="c" type="hidden" value="foo" />
-  <input name="d" type="file" />
+  <input name="foo" type="text">
+  <input data-id="1" data-name="test" name="foo" type="text" value="1">
+  <input data="ok" name="foo" type="text" value="1">
+  <input name="foo" type="checkbox" value="1">
+  <input checked name="a" type="checkbox" value="2">
+  <input name="b" type="radio" value="1">
+  <input checked name="b" type="radio" value="0">
+  <input name="c" type="hidden" value="foo">
+  <input name="d" type="file">
   <textarea cols="40" name="e" rows="50">
     default!
   </textarea>
   <textarea name="f"></textarea>
-  <input name="g" type="password" />
-  <input id="foo" name="h" type="password" />
-  <input type="submit" value="Ok!" />
-  <input id="bar" type="submit" value="Ok too!" />
+  <input name="g" type="password">
+  <input id="foo" name="h" type="password">
+  <input type="submit" value="Ok!">
+  <input id="bar" type="submit" value="Ok too!">
 </form>
 <form action="/">
-  <input name="foo" />
+  <input name="foo">
 </form>
-<input name="escaped" value="1&quot; &quot;2" />
-<input name="a" value="2" />
-<input name="a" value="2" />
+<input name="escaped" value="1&quot; &quot;2">
+<input name="a" value="2">
+<input name="a" value="2">
 EOF
 
 # Advanced form with different values
 $t->get_ok('/form/lala?c=b&d=3&e=4&f=<5')->status_is(200)->content_is(<<EOF);
 <form action="/links" method="post">
-  <input name="foo" />
+  <input name="foo">
 </form>
 <form action="/form/24" method="post">
-  <input name="foo" type="text" />
-  <input data-id="1" data-name="test" name="foo" type="text" value="1" />
-  <input data="ok" name="foo" type="text" value="1" />
-  <input name="foo" type="checkbox" value="1" />
-  <input name="a" type="checkbox" value="2" />
-  <input name="b" type="radio" value="1" />
-  <input name="b" type="radio" value="0" />
-  <input name="c" type="hidden" value="foo" />
-  <input name="d" type="file" />
+  <input name="foo" type="text">
+  <input data-id="1" data-name="test" name="foo" type="text" value="1">
+  <input data="ok" name="foo" type="text" value="1">
+  <input name="foo" type="checkbox" value="1">
+  <input name="a" type="checkbox" value="2">
+  <input name="b" type="radio" value="1">
+  <input name="b" type="radio" value="0">
+  <input name="c" type="hidden" value="foo">
+  <input name="d" type="file">
   <textarea cols="40" name="e" rows="50">4</textarea>
   <textarea name="f">&lt;5</textarea>
-  <input name="g" type="password" />
-  <input id="foo" name="h" type="password" />
-  <input type="submit" value="Ok!" />
-  <input id="bar" type="submit" value="Ok too!" />
+  <input name="g" type="password">
+  <input id="foo" name="h" type="password">
+  <input type="submit" value="Ok!">
+  <input id="bar" type="submit" value="Ok too!">
 </form>
 <form action="/">
-  <input name="foo" />
+  <input name="foo">
 </form>
-<input name="escaped" />
-<input name="a" />
-<input name="a" value="c" />
+<input name="escaped">
+<input name="a">
+<input name="a" value="c">
 EOF
 
 # Empty selection
@@ -347,7 +337,7 @@ $t->put_ok('/selection')->status_is(200)
     . '<option value="b">b</option>'
     . '</optgroup>'
     . "</select>\n  "
-    . '<input type="submit" value="Ok" />'
+    . '<input type="submit" value="Ok">'
     . "\n</form>\n");
 
 # Selection with values
@@ -357,26 +347,26 @@ $t->put_ok('/selection?a=e&foo=bar&bar=baz&yada=b')->status_is(200)
     . '<option value="b">b</option>'
     . '<optgroup label="c">'
     . '<option value="&lt;d">&lt;d</option>'
-    . '<option selected="selected" value="e">E</option>'
+    . '<option selected value="e">E</option>'
     . '<option value="f">f</option>'
     . '</optgroup>'
     . '<option value="g">g</option>'
     . "</select>\n  "
     . '<select multiple="multiple" name="foo">'
-    . '<option selected="selected" value="bar">bar</option>'
+    . '<option selected value="bar">bar</option>'
     . '<option value="baz">baz</option>'
     . "</select>\n  "
     . '<select name="bar">'
     . '<option disabled="disabled" value="d">D</option>'
-    . '<option selected="selected" value="baz">baz</option>'
+    . '<option selected value="baz">baz</option>'
     . "</select>\n  "
     . '<select name="yada">'
     . '<optgroup class="x" label="test">'
     . '<option value="a">a</option>'
-    . '<option selected="selected" value="b">b</option>'
+    . '<option selected value="b">b</option>'
     . '</optgroup>'
     . "</select>\n  "
-    . '<input type="submit" value="Ok" />'
+    . '<input type="submit" value="Ok">'
     . "\n</form>\n");
 
 # Selection with multiple values
@@ -387,39 +377,39 @@ $t->put_ok('/selection?foo=bar&a=e&foo=baz&bar=d&yada=a&yada=b')
     . '<option value="b">b</option>'
     . '<optgroup label="c">'
     . '<option value="&lt;d">&lt;d</option>'
-    . '<option selected="selected" value="e">E</option>'
+    . '<option selected value="e">E</option>'
     . '<option value="f">f</option>'
     . '</optgroup>'
     . '<option value="g">g</option>'
     . "</select>\n  "
     . '<select multiple="multiple" name="foo">'
-    . '<option selected="selected" value="bar">bar</option>'
-    . '<option selected="selected" value="baz">baz</option>'
+    . '<option selected value="bar">bar</option>'
+    . '<option selected value="baz">baz</option>'
     . "</select>\n  "
     . '<select name="bar">'
-    . '<option disabled="disabled" selected="selected" value="d">D</option>'
+    . '<option disabled="disabled" selected value="d">D</option>'
     . '<option value="baz">baz</option>'
     . "</select>\n  "
     . '<select name="yada">'
     . '<optgroup class="x" label="test">'
-    . '<option selected="selected" value="a">a</option>'
-    . '<option selected="selected" value="b">b</option>'
+    . '<option selected value="a">a</option>'
+    . '<option selected value="b">b</option>'
     . '</optgroup>'
     . "</select>\n  "
-    . '<input type="submit" value="Ok" />'
+    . '<input type="submit" value="Ok">'
     . "\n</form>\n");
 
 # Selection with multiple values preselected
 $t->put_ok('/selection?preselect=1')->status_is(200)
   ->content_is("<form action=\"/selection\">\n  "
     . '<select name="a">'
-    . '<option selected="selected" value="b">b</option>'
+    . '<option selected value="b">b</option>'
     . '<optgroup label="c">'
     . '<option value="&lt;d">&lt;d</option>'
     . '<option value="e">E</option>'
     . '<option value="f">f</option>'
     . '</optgroup>'
-    . '<option selected="selected" value="g">g</option>'
+    . '<option selected value="g">g</option>'
     . "</select>\n  "
     . '<select multiple="multiple" name="foo">'
     . '<option value="bar">bar</option>'
@@ -435,14 +425,14 @@ $t->put_ok('/selection?preselect=1')->status_is(200)
     . '<option value="b">b</option>'
     . '</optgroup>'
     . "</select>\n  "
-    . '<input type="submit" value="Ok" />'
+    . '<input type="submit" value="Ok">'
     . "\n</form>\n");
 
 # Snowman form
 $t->post_ok('/☃')->status_is(200)->content_is(<<'EOF');
 <form action="/%E2%98%83" method="POST">
   <textarea cols="40" name="foo">b&lt;a&gt;r</textarea>
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -450,7 +440,7 @@ EOF
 $t->post_ok('/☃?foo=ba<z')->status_is(200)->content_is(<<'EOF');
 <form action="/%E2%98%83" method="POST">
   <textarea cols="40" name="foo">ba&lt;z</textarea>
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -458,7 +448,7 @@ EOF
 $t->patch_ok('/☃?foo=')->status_is(200)->content_is(<<'EOF');
 <form action="/%E2%98%83" method="POST">
   <textarea cols="40" name="foo"></textarea>
-  <input type="submit" value="☃" />
+  <input type="submit" value="☃">
 </form>
 EOF
 
@@ -466,7 +456,7 @@ EOF
 $t->post_ok('/no_snowman')->status_is(200)->content_is(<<'EOF');
 <form action="/%E2%98%83" method="POST">
   <textarea cols="40" name="bar"></textarea>
-  <input type="submit" value="whatever" />
+  <input type="submit" value="whatever">
 </form>
 EOF
 
@@ -474,7 +464,7 @@ EOF
 $t->post_ok('/no_snowman?foo=1')->status_is(200)->content_is(<<'EOF');
 <form action="/%E2%98%83" method="PATCH">
   <textarea cols="40" name="bar"></textarea>
-  <input type="submit" value="whatever" />
+  <input type="submit" value="whatever">
 </form>
 EOF
 
@@ -496,7 +486,6 @@ __DATA__
 
 @@ small_tags.html.ep
 %=t div => (id => '&lt;') => 'test & 123'
-%=t div => (id => b('&lt;')) => b('test&nbsp;321')
 %=t div => begin
   %=t p => (id => 0) => 'just'
   %=t p => 0
@@ -510,14 +499,6 @@ __DATA__
   0
 %= end
 
-@@ circle.svg.ep
-%= tag svg => begin
-<%=
-  tag 'circle', cx => 75, cy => 55, r => 35, stroke => 'black',
-    'stroke-width' => 2, fill => 'red'
-%>
-%= end
-
 @@ links.html.ep
 <%= link_to 'Pa<th' => '/path' %>
 <%= link_to 'http://example.com/', title => 'Foo', sub { 'Foo' } %>
@@ -554,7 +535,7 @@ __DATA__
     Bar<br>
   %= end
   %= text_field bar => 'baz', class => 'test'
-  %= text_field yada => undef
+  %= text_field yada => ''
   %= input_tag baz => 'yada', class => 'tset'
   %= submit_button
 %= end
@@ -128,9 +128,9 @@ is_deeply $validation->error('foo'), ['required'], 'right error';
 # "0"
 $validation = $t->app->validation->input({0 => 0});
 ok $validation->has_data, 'has data';
-ok $validation->required('0')->size(1, 1)->is_valid, 'valid';
+ok $validation->required(0)->size(1, 1)->is_valid, 'valid';
 is_deeply $validation->output, {0 => 0}, 'right result';
-is $validation->param('0'), 0, 'right value';
+is $validation->param(0), 0, 'right value';
 
 # Custom error
 $validation = $t->app->validation->input({foo => 'bar'});
@@ -10,7 +10,7 @@ use Test::Mojo;
 
 websocket '/echo' => sub {
   my $c = shift;
-  $c->tx->max_websocket_size(262145)->with_compression;
+  $c->tx->max_websocket_size(65538)->with_compression;
   $c->on(binary => sub { shift->send({binary => shift}) });
   $c->on(
     text => sub {
@@ -139,21 +139,25 @@ $t->websocket_ok('/echo')->send_ok(0)->message_ok->message_is('echo: 0')
   ->send_ok(0)->message_ok->message_like({text => qr/0/})->finish_ok(1000)
   ->finished_ok(1000);
 
-# 64-bit binary message (extended limit)
+# 64-bit binary message
 $t->request_ok($t->ua->build_websocket_tx('/echo'));
 is $t->tx->max_websocket_size, 262144, 'right size';
-$t->tx->max_websocket_size(262145);
-$t->send_ok({binary => 'a' x 262145})
-  ->message_ok->message_is({binary => 'a' x 262145})
+$t->tx->max_websocket_size(65538);
+$t->send_ok({binary => 'a' x 65538})
+  ->message_ok->message_is({binary => 'a' x 65538})
   ->finish_ok->finished_ok(1005);
 
-# 64-bit binary message (too large)
-$t->websocket_ok('/echo')->send_ok({binary => 'b' x 262145})
-  ->finished_ok(1009);
+# 64-bit binary message (too large for server)
+$t->websocket_ok('/echo')->send_ok({binary => 'b' x 65539})->finished_ok(1009);
 
-# Binary message in two 64-bit frames without FIN bit (too large)
-$t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'c' x 100000])
-  ->send_ok([0, 0, 0, 0, 0, 'c' x 162146])->finished_ok(1009);
+# 64-bit binary message (too large for client)
+$t->websocket_ok('/echo');
+$t->tx->max_websocket_size(65536);
+$t->send_ok({binary => 'c' x 65537})->finished_ok(1009);
+
+# Binary message in two frames without FIN bit (too large for server)
+$t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'd' x 30000])
+  ->send_ok([0, 0, 0, 0, 0, 'd' x 35539])->finished_ok(1009);
 
 # Plain alternative
 $t->get_ok('/echo')->status_is(200)->content_is('plain echo!');
@@ -171,7 +175,7 @@ $t->send_ok({binary => 'a' x 500})
 $t->websocket_ok(
   '/echo' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'});
 ok $t->tx->compressed, 'WebSocket has compression';
-$t->send_ok({binary => 'a' x 50000})
+$t->send_ok({binary => 'a' x 10000})
   ->header_is('Sec-WebSocket-Extensions' => 'permessage-deflate');
 is $t->tx->req->headers->sec_websocket_extensions, 'permessage-deflate',
   'right "Sec-WebSocket-Extensions" value';
@@ -182,8 +186,8 @@ $t->tx->once(
     $payload = $frame->[5];
   }
 );
-$t->message_ok->message_is({binary => 'a' x 50000});
-ok length $payload < 50000, 'message has been compressed';
+$t->message_ok->message_is({binary => 'a' x 10000});
+ok length $payload < 10000, 'message has been compressed';
 $t->finish_ok->finished_ok(1005);
 
 # Compressed message exceeding the limit when decompressed
@@ -193,7 +197,7 @@ $t->websocket_ok(
   ->send_ok({binary => 'a' x 1000000})->finished_ok(1009);
 
 # Huge message that doesn't compress very well
-my $huge = join '', map { int rand(9) } 1 .. 262144;
+my $huge = join '', map { int rand(9) } 1 .. 65538;
 $t->websocket_ok(
   '/echo' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
   ->send_ok({binary => $huge})->message_ok->message_is({binary => $huge})
@@ -8,6 +8,6 @@ plan skip_all => 'Test::Pod::Coverage 1.04 required for this test!'
   unless eval 'use Test::Pod::Coverage 1.04; 1';
 
 # DEPRECATED in Tiger Face!
-my @tiger = qw(decode encode error new pluck siblings val);
+my @tiger = qw(bridge siblings);
 
 all_pod_coverage_ok({also_private => [@tiger]});