@@ -1,4 +1,14 @@
+4.93 2014-04-13
+ - Fixed bug where Mojolicious::Static would not use the correct default MIME
+ type.
+
+4.92 2014-04-08
+ - Removed deprecated use of hash references for optgroup generation with
+ select_field helper.
+ - Improved dumper helper to escape unprintable characters.
+ - Fixed small handler detection bug in Mojolicious::Renderer.
+
4.91 2014-03-29
- Added daemonize method to Mojo::Server.
- Added ensure_pid_file method to Mojo::Server::Prefork.
@@ -4,7 +4,7 @@
"Sebastian Riedel <sri@cpan.org>"
],
"dynamic_config" : 1,
- "generated_by" : "ExtUtils::MakeMaker version 6.94, CPAN::Meta::Converter version 2.140640",
+ "generated_by" : "ExtUtils::MakeMaker version 6.96, CPAN::Meta::Converter version 2.140640",
"license" : [
"artistic_2"
],
@@ -51,5 +51,5 @@
},
"x_MailingList" : "http://groups.google.com/group/mojolicious"
},
- "version" : "4.91"
+ "version" : "4.93"
}
@@ -7,7 +7,7 @@ build_requires:
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.94, CPAN::Meta::Converter version 2.140640'
+generated_by: 'ExtUtils::MakeMaker version 6.96, CPAN::Meta::Converter version 2.140640'
license: artistic_2
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -26,4 +26,4 @@ resources:
homepage: http://mojolicio.us
license: http://www.opensource.org/licenses/artistic-license-2.0
repository: http://github.com/kraih/mojo
-version: '4.91'
+version: '4.93'
@@ -24,7 +24,7 @@ __DATA__
<html>
<head>
<title>WebSocket Test</title>
- %= javascript begin
+ <script>
var ws;
if ("WebSocket" in window) {
ws = new WebSocket('<%= url_for('test')->to_abs %>');
@@ -40,7 +40,7 @@ __DATA__
else {
document.body.innerHTML += 'Browser does not support WebSockets.';
}
- % end
+ </script>
</head>
<body>Testing WebSockets: </body>
</html>
@@ -107,11 +107,11 @@ C<auto_upgrade> enabled.
=head2 auto_upgrade
- my $upgrade = $single->auto_upgrade;
- $single = $single->auto_upgrade(0);
+ my $bool = $single->auto_upgrade;
+ $single = $single->auto_upgrade($bool);
Try to detect multipart content and automatically upgrade to a
-L<Mojo::Content::MultiPart> object, defaults to C<1>.
+L<Mojo::Content::MultiPart> object, defaults to a true value.
=head1 METHODS
@@ -48,7 +48,8 @@ sub from_hash {
delete $self->{headers} if keys %{$hash} == 0;
# Merge
- while (my ($header, $value) = each %$hash) {
+ for my $header (keys %$hash) {
+ my $value = $hash->{$header};
$self->add($header => ref $value eq 'ARRAY' ? @$value : $value);
}
@@ -14,7 +14,7 @@ my $START_LINE_RE = qr/
([a-zA-Z]+) # Method
\s+
([0-9a-zA-Z!#\$\%&'()*+,\-.\/:;=?\@[\\\]^_`\{|\}~]+) # URL
- (?:\s+HTTP\/(\d\.\d))? # Version
+ \s+HTTP\/(\d\.\d) # Version
$
/x;
@@ -196,7 +196,8 @@ sub _parse_env {
my $headers = $self->headers;
my $url = $self->url;
my $base = $url->base;
- while (my ($name, $value) = each %$env) {
+ for my $name (keys %$env) {
+ my $value = $env->{$name};
next unless $name =~ s/^HTTP_//i;
$name =~ y/_/-/;
$headers->header($name => $value);
@@ -88,7 +88,7 @@ sub cookies {
return $self;
}
-sub default_message { $MESSAGES{$_[1] || $_[0]->code || 404} || '' }
+sub default_message { $MESSAGES{$_[1] || $_[0]->code // 404} || '' }
sub extract_start_line {
my ($self, $bufref) = @_;
@@ -128,7 +128,7 @@ sub get_start_line_chunk {
sub is_empty {
my $self = shift;
return undef unless my $code = $self->code;
- return $self->is_status_class(100) || $code eq 204 || $code eq 304;
+ return $self->is_status_class(100) || $code == 204 || $code == 304;
}
sub is_status_class {
@@ -113,7 +113,7 @@ sub _finish {
if (my $ws = $c->{tx} = delete $c->{ws}) {
# Successful upgrade
- if ($ws->res->code eq '101') {
+ if ($ws->res->code == 101) {
weaken $self;
$ws->on(resume => sub { $self->_write($id) });
}
@@ -30,7 +30,7 @@ sub run {
# PSGI response
my $io = Mojo::Server::PSGI::_IO->new(tx => $tx, empty => $tx->is_empty);
- return [$res->code || 404, \@headers, $io];
+ return [$res->code // 404, \@headers, $io];
}
sub to_psgi_app {
@@ -70,8 +70,8 @@ sub redirect {
# Commonly used codes
my $res = $old->res;
- my $code = $res->code // '';
- return undef unless grep { $_ eq $code } 301, 302, 303, 307, 308;
+ my $code = $res->code // 0;
+ return undef unless grep { $_ == $code } 301, 302, 303, 307, 308;
# Fix broken location without authority and/or scheme
return unless my $location = $res->headers->location;
@@ -81,7 +81,7 @@ sub redirect {
# Clone request if necessary
my $new = Mojo::Transaction::HTTP->new;
my $req = $old->req;
- if ($code eq 307 || $code eq 308) {
+ if ($code == 307 || $code == 308) {
return undef unless my $clone = $req->clone;
$new->req($clone);
}
@@ -126,8 +126,8 @@ sub tx {
sub upgrade {
my ($self, $tx) = @_;
- my $code = $tx->res->code // '';
- return undef unless $tx->req->is_handshake && $code eq '101';
+ my $code = $tx->res->code // 0;
+ return undef unless $tx->req->is_handshake && $code == 101;
my $ws = Mojo::Transaction::WebSocket->new(handshake => $tx, masked => 1);
return $ws->client_challenge ? $ws : undef;
}
@@ -111,7 +111,9 @@ sub deprecated {
$ENV{MOJO_FATAL_DEPRECATIONS} ? croak(@_) : carp(@_);
}
-sub dumper { Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Dump }
+sub dumper {
+ Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Useqq(1)->Dump;
+}
sub encode { _encoding($_[0])->encode("$_[1]") }
@@ -28,10 +28,10 @@ sub run {
);
unless ($tx->success) {
- my $code = $tx->res->code || '';
+ my $code = $tx->res->code // 0;
my $msg = $tx->error;
- if ($code eq '401') { $msg = 'Wrong username or password.' }
- elsif ($code eq '409') { $msg = 'File already exists on CPAN.' }
+ if ($code == 401) { $msg = 'Wrong username or password.' }
+ elsif ($code == 409) { $msg = 'File already exists on CPAN.' }
die qq{Problem uploading file "$file". ($msg)\n};
}
@@ -166,7 +166,8 @@ consistent if not.
The master source code repository should always be kept in a stable state, use
feature branches for actual development.
-Code has to be run through L<Perl::Tidy> with the included C<.perltidyrc>, and
+Code has to be run through L<Perl::Tidy> with the included
+L<.perltidyrc|https://github.com/kraih/mojo/blob/master/.perltidyrc>, and
everything should look like it was written by a single person.
Functions and methods should be as short as possible, no spaghetti code.
@@ -1220,8 +1220,8 @@ that you can use or overload.
$ ./myapp.pl spy secrets
secr3t
-And to make your commands application specific, just put them in a different
-namespace.
+And to make your commands application specific, just add a custom namespace to
+L<Mojolicious::Commands/"namespaces">.
# Application
package MyApp;
@@ -71,6 +71,15 @@ In addition we will also keep the distribution installable up to a certain
legacy version that we deem worthy of supporting, but not specifically
optimize for it, this is currently 5.10.1.
+=head2 Do I need to clean my environment before testing Mojolicious?
+
+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
+recommend using an environment which does not set these variables.
+
=head2 What is the difference between blocking and non-blocking operations?
A I<blocking> operation is a subroutine that blocks the execution of the
@@ -309,8 +309,8 @@ Since everything is just Perl normal control structures just work.
<%= $framework %> was written by <%= $author %>.
% }
- % while (my ($app, $description) = each %$examples) {
- <%= $app %> is a <%= $description %>.
+ % if (my $description = $examples->{tweetylicious}) {
+ Tweetylicious is a <%= $description %>.
% }
=head2 Content negotiation
@@ -705,10 +705,10 @@ with methods like L<Mojolicious::Validator::Validation/"is_valid">.
<!DOCTYPE html>
<html>
<head>
- %= stylesheet begin
+ <style>
label.field-with-error { color: #dd7e5e }
input.field-with-error { background-color: #fd9e7e }
- % end
+ </style>
</head>
<body>
%= form_for index => begin
@@ -610,8 +610,8 @@ operations to finish before reaching the next dispatch cycle.
From the tutorial you should already know L<Mojolicious::Lite> routes, which
are in fact just a small convenience layer around everything described above
-and accessible through methods like L<Mojolicious::Routes::Route/"get"> as
-part of the normal router.
+and accessible through methods like L<Mojolicious::Routes::Route/"get"> and
+L<Mojolicious::Routes::Route/"any"> as part of the normal router.
# POST /foo -> {controller => 'foo', action => 'abc'}
$r->post('/foo')->to(controller => 'foo', action => 'abc');
@@ -133,6 +133,10 @@ designed to be used with it and are being developed under the same umbrella.
Pure-Perl non-blocking I/O MongoDB driver.
+=item L<Minion>
+
+Job queue.
+
=back
=head1 REFERENCE
@@ -254,6 +254,11 @@ in debugging your application.
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
@@ -725,7 +730,8 @@ Both have a higher precedence than routes.
=head2 External templates
External templates will be searched by the renderer in a C<templates>
-directory if it exists.
+directory if it exists and have a higher precedence than those in the C<DATA>
+section.
use Mojolicious::Lite;
@@ -917,7 +923,7 @@ and return them with L<Mojolicious::Controller/"send">.
<html>
<head>
<title>Echo</title>
- %= javascript begin
+ <script>
var ws = new WebSocket('<%= url_for('echo')->to_abs %>');
ws.onmessage = function (event) {
document.body.innerHTML += JSON.parse(event.data).msg;
@@ -925,7 +931,7 @@ and return them with L<Mojolicious::Controller/"send">.
ws.onopen = function (event) {
ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'}));
};
- % end
+ </script>
</head>
</html>
@@ -24,9 +24,8 @@ sub _headers {
# All headers need to match
my $headers = $c->req->headers;
- while (my ($name, $pattern) = each %$patterns) {
- return undef unless _check(scalar $headers->header($name), $pattern);
- }
+ _check(scalar $headers->header($_), $patterns->{$_}) || return undef
+ for keys %$patterns;
return 1;
}
@@ -2,7 +2,7 @@ package Mojolicious::Plugin::TagHelpers;
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::ByteStream;
-use Mojo::Util qw(deprecated xml_escape);
+use Mojo::Util 'xml_escape';
use Scalar::Util 'blessed';
sub register {
@@ -157,13 +157,6 @@ sub _select_field {
my $groups = '';
for my $group (@$options) {
- # DEPRECATED in Top Hat!
- if (ref $group eq 'HASH') {
- deprecated
- 'hash references are DEPRECATED in favor of Mojo::Collection objects';
- $group = Mojo::Collection->new(each %$group);
- }
-
# "optgroup" tag
if (blessed $group && $group->isa('Mojo::Collection')) {
my ($label, $values, %attrs) = @$group;
@@ -227,6 +227,7 @@ sub _render_template {
# Find handler and render
my $handler = $options->{handler} ||= $self->template_handler($options);
+ return undef unless $handler;
if (my $renderer = $self->handlers->{$handler}) {
return 1 if $renderer->($self, $c, $output, $options);
}
@@ -52,8 +52,9 @@ sub file {
sub serve {
my ($self, $c, $rel) = @_;
return undef unless my $asset = $self->file($rel);
- my $type = $rel =~ /\.(\w+)$/ ? $c->app->types->type($1) : undef;
- $c->res->headers->content_type($type || 'text/plain');
+ my $types = $c->app->types;
+ my $type = $rel =~ /\.(\w+)$/ ? $types->type($1) : undef;
+ $c->res->headers->content_type($type || $types->type('txt'));
return !!$self->serve_asset($c, $asset);
}
@@ -8,7 +8,7 @@
%= javascript '/mojo/jquery/jquery.js'
%= javascript '/mojo/prettify/run_prettify.js'
%= stylesheet '/mojo/prettify/prettify-mojo-dark.css'
- %= stylesheet begin
+ <style>
a img { border: 0 }
body {
background: url(<%= url_for '/mojo/pinstripe-light.png' %>);
@@ -70,17 +70,17 @@
}
.value { padding-left: 1em }
.wide { width: 100% }
- #footer {
- padding-top: 1em;
- text-align: center;
- }
- #nothing { padding-top: 60px }
- #showcase > pre {
+ #error {
font: 1.5em 'Helvetica Neue', Helvetica, sans-serif;
font-weight: 300;
margin: 0;
text-shadow: #333 0 1px 0;
}
+ #footer {
+ padding-top: 1em;
+ text-align: center;
+ }
+ #nothing { padding-top: 60px }
#showcase table { margin-top: 1em }
#showcase td {
padding-top: 0;
@@ -111,11 +111,11 @@
max-width: 1000px;
margin: 0 auto;
}
- % end
+ </style>
</head>
<body>
%= include inline => app->renderer->_bundled('mojobar')
- %= javascript begin
+ <script>
function mojoDrawer (handle, drawer) {
$(handle).click(function() {
$(drawer).slideToggle('slow');
@@ -129,7 +129,7 @@
mojoDrawer('#trace', '#frames');
mojoDrawer('#more', '#infos');
});
- % end
+ </script>
<div id="wrapperlicious">
% my $kv = begin
% my ($key, $value) = @_;
@@ -150,7 +150,7 @@
% end
% end
<div id="showcase" class="box code spaced">
- <pre><%= $exception->message %></pre>
+ <pre id="error"><%= $exception->message %></pre>
<div id="context" class="more">
<table>
% for my $line (@{$exception->lines_before}) {
@@ -177,7 +177,7 @@
</table>
</div>
<div class="tap">tap for more</div>
- %= javascript begin
+ <script>
var current = '#context';
$('#showcase').click(function() {
$(current).slideToggle('slow', function() {
@@ -191,7 +191,7 @@
});
});
$('#insight').toggle();
- % end
+ </script>
% }
</div>
<div id="trace" class="box spaced">
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head><title>Server error</title></head>
- %= stylesheet begin
+ <style>
body { background-color: #caecf6 }
#raptor {
background: url(<%= url_for '/mojo/failraptor.png' %>);
@@ -13,6 +13,6 @@
top: 50%;
width: 743px;
}
- % end
+ </style>
<body><div id="raptor"></div></body>
</html>
@@ -1,6 +1,6 @@
%= javascript '/mojo/jquery/jquery.js'
<div id="mojobar">
- %= stylesheet scoped => 'scoped', begin
+ <style scoped="scoped">
#mojobar {
background-color: #1a1a1a;
background: -webkit-linear-gradient(top, #2a2a2a 0%, #000 100%);
@@ -46,7 +46,7 @@
#mojobar-links input:focus { outline: 0 }
#mojobar-links form { display: inline }
.animated { transition: top 0.2s ease-in-out }
- % end
+ </style>
<div id="mojobar-logo">
%= link_to 'http://mojolicio.us' => begin
%= image '/mojo/logo-white.png', alt => 'Mojolicious logo'
@@ -67,7 +67,7 @@
%= end
</div>
</div>
-%= javascript begin
+<script>
var mojobar = $('#mojobar');
var mojobarHeight = mojobar.outerHeight();
function fixOffset() {
@@ -120,4 +120,4 @@
fixOffset();
});
});
-% end
+</script>
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head><title>Page not found</title></head>
- %= stylesheet begin
+ <style>
a img { border: 0 }
body { background-color: #caecf6 }
#noraptor {
@@ -19,7 +19,7 @@
top: 50%;
width: 306px;
}
- % end
+ </style>
<body>
%= link_to url_for->base => begin
%= image '/mojo/noraptor.png', alt => 'Bye!', id => 'noraptor'
@@ -4,7 +4,7 @@
<title><%= $title %></title>
%= javascript '/mojo/prettify/run_prettify.js'
%= stylesheet '/mojo/prettify/prettify-mojo-light.css'
- %= stylesheet begin
+ <style>
a { color: inherit }
a:hover { color: #2a2a2a }
a img { border: 0 }
@@ -75,7 +75,7 @@
h1:hover .permalink, h2:hover .permalink, h3:hover .permalink {
display: block;
}
- % end
+ </style>
</head>
<body>
%= include inline => app->renderer->_bundled('mojobar')
@@ -43,7 +43,7 @@ has types => sub { Mojolicious::Types->new };
has validator => sub { Mojolicious::Validator->new };
our $CODENAME = 'Top Hat';
-our $VERSION = '4.91';
+our $VERSION = '4.93';
sub AUTOLOAD {
my $self = shift;
@@ -988,6 +988,7 @@ the terms of the Artistic License version 2.0.
=head1 SEE ALSO
-L<Mojolicious::Guides>, L<http://mojolicio.us>.
+L<https://github.com/kraih/mojo>, L<Mojolicious::Guides>,
+L<http://mojolicio.us>.
=cut
@@ -351,7 +351,8 @@ $t->get_ok('/' => {'X-Test' => 'Hi there!'})->status_is(404)
# Static file /another/file (no extension)
$t->get_ok('/another/file')->status_is(200)
- ->header_is(Server => 'Mojolicious (Perl)')->content_type_is('text/plain')
+ ->header_is(Server => 'Mojolicious (Perl)')
+ ->content_type_is('text/plain;charset=UTF-8')
->content_like(qr/Hello Mojolicious!/);
# Static directory /another
@@ -52,7 +52,8 @@ hook before_dispatch => sub {
# Custom dispatcher /custom
hook before_dispatch => sub {
my $c = shift;
- $c->render(text => $c->param('a'), status => 205)
+ $c->render_maybe
+ or $c->render(text => $c->param('a'), status => 205)
if $c->req->url->path->contains('/custom');
};
@@ -166,7 +166,7 @@ like $log, qr/dead template with layout!/, 'right result';
$t->get_ok('/dead_action')->status_is(500)
->content_type_is('text/html;charset=UTF-8')
->content_like(qr!get '/dead_action'!)
- ->content_like(qr/dead action!/);
+ ->content_like(qr/dead action!/)->text_is('#error' => "dead action!\n");
like $log, qr/dead action!/, 'right result';
# Dead action with different format
@@ -757,7 +757,8 @@ $t->get_ok('/inline/ep/partial')->status_is(200)
->content_is("♥just ♥\nworks!\n");
# Render static file outside of public directory
-$t->get_ok('/source')->status_is(200)->header_isnt('X-Missing' => 1)
+$t->get_ok('/source')->status_is(200)
+ ->content_type_is('text/plain;charset=UTF-8')->header_isnt('X-Missing' => 1)
->content_like(qr!get_ok\('/source!);
# File does not exist