From 5af1133ee81416c4bab8c759d13e24ad52cc7692 Mon Sep 17 00:00:00 2001 From: Sebastian Riedel Date: Fri, 17 Jun 2011 05:13:23 +0200 Subject: [PATCH] added experimental self-restarting Morbo development web server and removed old "--reload" support since there have been too many negative side effects --- Changes | 5 + lib/Mojo/Command.pm | 22 +--- lib/Mojo/Loader.pm | 99 +-------------- lib/Mojo/Message.pm | 27 +---- lib/Mojo/Server.pm | 30 ++--- lib/Mojo/Server/Daemon.pm | 3 +- lib/Mojo/Server/Hypnotoad.pm | 11 +- lib/Mojo/Server/Morbo.pm | 160 +++++++++++++++++++++++++ lib/Mojo/Template.pm | 4 +- lib/Mojolicious.pm | 31 ++--- lib/Mojolicious/Command/Daemon.pm | 3 - lib/Mojolicious/Guides/Cheatsheet.pod | 7 -- lib/Mojolicious/Guides/FAQ.pod | 6 - lib/Mojolicious/Guides/Growing.pod | 6 +- lib/Mojolicious/Lite.pm | 7 +- lib/Mojolicious/Plugin/EpRenderer.pm | 6 + lib/Mojolicious/Plugin/EplRenderer.pm | 7 +- lib/Mojolicious/Plugin/RequestTimer.pm | 16 +-- lib/Mojolicious/Plugins.pm | 7 +- script/morbo | 72 +++++++++++ t/mojo/loader.t | 29 +---- t/mojo/template.t | 4 +- 22 files changed, 305 insertions(+), 257 deletions(-) create mode 100644 lib/Mojo/Server/Morbo.pm create mode 100755 script/morbo diff --git a/Changes b/Changes index dd02d831d3..5973d065d5 100644 --- a/Changes +++ b/Changes @@ -1,14 +1,19 @@ This file documents the revision history for Perl extension Mojolicious. 1.44 2011-06-13 00:00:00 + - Added EXPERIMENTAL self-restarting Morbo development web server and + removed old "--reload" support since there have been too many + negative side effects. - Added EXPERIMENTAL application mount plugin. - Updated prettify.js to version 1-Jun-2011. - Updated WebSocket diagnostics test in Mojo::HelloWorld for latest Firefox Aurora. + - Improved inline template and static file performance. - Improved documentation. - Improved tests. - Fixed a few application embedding bugs. - Fixed link generation bug in Mojolicious::Plugin::PodRenderer. + - Fixed small embedding bug in Mojolicious::Plugin::RequestTimer. 1.43 2011-06-13 00:00:00 - Improved after_dispatch hook by allowing it to change session data. diff --git a/lib/Mojo/Command.pm b/lib/Mojo/Command.pm index e6c8676604..c24c09d1e6 100644 --- a/lib/Mojo/Command.pm +++ b/lib/Mojo/Command.pm @@ -103,18 +103,12 @@ sub get_all_data { my ($self, $class) = @_; $class ||= ref $self; - # Handle + # Refresh or use cached data my $d = do { no strict 'refs'; \*{"$class\::DATA"} }; - - # Refresh - if (fileno $d) { - seek $d, 0, 0; - $CACHE->{$class} = join '', <$d>; - close $d; - } - - # Content - return unless defined(my $content = $CACHE->{$class}); + return $CACHE->{$class} unless fileno $d; + seek $d, 0, 0; + my $content = join '', <$d>; + close $d; # Ignore everything before __DATA__ (windows will seek to start of file) $content =~ s/^.*\n__DATA__\r?\n/\n/s; @@ -127,7 +121,7 @@ sub get_all_data { shift @data; # Find data - my $all = {}; + my $all = $CACHE->{$class} = {}; while (@data) { my ($name, $content) = splice @data, 0, 2; b64_decode $content if $name =~ s/\s*\(\s*base64\s*\)$//; @@ -294,10 +288,6 @@ sub run { sub start { my $self = shift; - # Don't run commands if we are reloading - return $self if $ENV{MOJO_COMMANDS_DONE}; - $ENV{MOJO_COMMANDS_DONE} ||= 1; - # Executable $ENV{MOJO_EXE} ||= (caller)[1] if $ENV{MOJO_APP}; diff --git a/lib/Mojo/Loader.pm b/lib/Mojo/Loader.pm index afaf16dacd..dbee7a9b1e 100644 --- a/lib/Mojo/Loader.pm +++ b/lib/Mojo/Loader.pm @@ -1,20 +1,15 @@ package Mojo::Loader; use Mojo::Base -base; +# "Don't let Krusty's death get you down, boy. +# People die all the time, just like that. +# Why, you could wake up dead tomorrow! Well, good night." use Carp 'carp'; use File::Basename; use File::Spec; use Mojo::Command; use Mojo::Exception; -use constant DEBUG => $ENV{MOJO_LOADER_DEBUG} || 0; - -# Cache stats -my $STATS = {}; - -# Debugger sub tracking -BEGIN { $^P |= 0x10 } - # "Homer no function beer well without." sub load { my ($self, $module) = @_; @@ -22,16 +17,8 @@ sub load { # Check module name return 1 if !$module || $module !~ /^[\w\:\']+$/; - # Forced reload - if ($ENV{MOJO_RELOAD}) { - my $key = $module; - $key =~ s/\:\:/\//g; - $key .= '.pm'; - _unload($key); - } - # Already loaded - else { return if $module->can('new') } + return if $module->can('new'); # Load unless (eval "require $module; 1") { @@ -47,37 +34,8 @@ sub load { return; } -# "Don't let Krusty's death get you down, boy. -# People die all the time, just like that. -# Why, you could wake up dead tomorrow! Well, good night." -sub reload { - - # Cleanup script and "main" namespace - delete $INC{$0}; - $STATS->{$0} = 1; - _purge(grep { index($_, 'main::') == 0 } keys %DB::sub); - - # Reload - while (my ($key, $file) = each %INC) { - - # Modified time - next unless $file; - my $mtime = (stat $file)[9]; - - # Startup time as default - $STATS->{$file} = $^T unless defined $STATS->{$file}; - - # Modified - if ($mtime > $STATS->{$file}) { - if (my $e = _reload($key)) { return $e } - $STATS->{$file} = $mtime; - } - } - - # Force script reloading - return _reload($0); -} - +# "This is the worst thing you've ever done. +# You say that so often that it lost its meaning." sub search { my ($self, $namespace) = @_; @@ -109,35 +67,6 @@ sub search { return $modules; } -# "This is the worst thing you've ever done. -# You say that so often that it lost its meaning." -sub _purge { - for my $sub (@_) { - warn "PURGE $sub\n" if DEBUG; - carp "Can't unload sub '$sub': $@" unless eval { undef &$sub; 1 }; - delete $DB::sub{$sub}; - no strict 'refs'; - $sub =~ /^(.*::)(.*?)$/ and delete *{$1}->{$2}; - } -} - -sub _reload { - my $key = shift; - return if $key eq 'Mojo/Loader.pm'; - warn "CLEANING $key\n" if DEBUG; - _unload($key); - warn "RELOADING $key\n" if DEBUG; - return Mojo::Exception->new($@) - unless eval { package main; require $key; 1 }; - return; -} - -sub _unload { - my $key = shift; - return unless my $file = delete $INC{$key}; - _purge(grep { index($DB::sub{$_}, "$file:") == 0 } keys %DB::sub); -} - 1; __END__ @@ -153,9 +82,6 @@ Mojo::Loader - Loader my $modules = $loader->search('Some::Namespace'); $loader->load($modules->[0]); - # Reload - Mojo::Loader->reload; - =head1 DESCRIPTION L is a class loader and plugin framework. @@ -172,25 +98,12 @@ following new ones. Load a class, note that classes are checked for a C method to see if they are already loaded. -=head2 C - - my $e = Mojo::Loader->reload; - -Reload all Perl files with changes. - =head2 C my $modules = $loader->search('MyApp::Namespace'); Search modules in a namespace. -=head1 DEBUGGING - -You can set the C environment variable to get some -advanced diagnostics information printed to C. - - MOJO_LOADER_DEBUG=1 - =head1 SEE ALSO L, L, L. diff --git a/lib/Mojo/Message.pm b/lib/Mojo/Message.pm index 9725ec7170..861492eb5f 100644 --- a/lib/Mojo/Message.pm +++ b/lib/Mojo/Message.pm @@ -4,7 +4,8 @@ use Mojo::Base -base; use Carp 'croak'; use Mojo::Asset::Memory; use Mojo::Content::Single; -use Mojo::Loader; +use Mojo::DOM; +use Mojo::JSON; use Mojo::Parameters; use Mojo::Upload; use Mojo::Util qw/decode url_unescape/; @@ -191,21 +192,13 @@ sub dom { # Multipart return if $self->is_multipart; - # Load DOM class - my $class = $self->dom_class; - if (my $e = Mojo::Loader->load($class)) { - croak ref $e - ? qq/Can't load DOM class "$class": $e/ - : qq/DOM class "$class" doesn't exist./; - } - # Charset my $charset; ($self->headers->content_type || '') =~ /charset=\"?([^\"\s;]+)\"?/ and $charset = $1; # Parse - my $dom = $class->new(charset => $charset)->parse($self->body); + my $dom = $self->dom_class->new(charset => $charset)->parse($self->body); # Find right away return $dom->find(@_) if @_; @@ -327,20 +320,8 @@ sub is_multipart { shift->content->is_multipart } sub json { my $self = shift; - - # Multipart return if $self->is_multipart; - - # Load JSON class - my $class = $self->json_class; - if (my $e = Mojo::Loader->load($class)) { - croak ref $e - ? qq/Can't load JSON class "$class": $e/ - : qq/JSON class "$class" doesn't exist./; - } - - # Decode - return $class->new->decode($self->body); + return $self->json_class->new->decode($self->body); } sub leftovers { shift->content->leftovers } diff --git a/lib/Mojo/Server.pm b/lib/Mojo/Server.pm index 83193dd67a..da64824f17 100644 --- a/lib/Mojo/Server.pm +++ b/lib/Mojo/Server.pm @@ -31,13 +31,6 @@ has on_request => sub { has on_transaction => sub { sub { my $self = shift; - - # Reload - if ($self->reload) { - if (my $e = Mojo::Loader->reload) { warn $e } - delete $self->{app}; - } - $self->app->on_transaction->($self->app); }; }; @@ -47,7 +40,6 @@ has on_websocket => sub { $self->app->on_websocket->($self->app, @_)->server_handshake; }; }; -has reload => sub { $ENV{MOJO_RELOAD} || 0 }; sub load_app { my ($self, $file) = @_; @@ -56,17 +48,18 @@ sub load_app { local $ENV{MOJO_APP_LOADER} = 1; local $ENV{MOJO_APP}; local $ENV{MOJO_EXE}; - local $ENV{MOJO_COMMANDS_DONE}; - # Try to load application from script - my $class = 'Mojo::Server::_' . md5_sum($file . $$); + # Try to load application from script into sandbox + my $class = 'Mojo::Server::SandBox::' . md5_sum($file . $$); my $app; die $@ unless eval < - - my $reload = $server->reload; - $server = $server->reload(1); - -Activate automatic reloading. - =head1 METHODS L inherits all methods from L and implements the diff --git a/lib/Mojo/Server/Daemon.pm b/lib/Mojo/Server/Daemon.pm index c316c203e5..525a321023 100644 --- a/lib/Mojo/Server/Daemon.pm +++ b/lib/Mojo/Server/Daemon.pm @@ -289,9 +289,10 @@ sub _listen { } # Friendly message + return if $self->silent; $self->app->log->info("Server listening ($listen)"); $listen =~ s/^(https?\:\/\/)\*/${1}127.0.0.1/i; - print "Server available at $listen.\n" unless $self->silent; + print "Server available at $listen.\n"; } sub _read { diff --git a/lib/Mojo/Server/Hypnotoad.pm b/lib/Mojo/Server/Hypnotoad.pm index 44c77cf6d2..39ca3e2b8c 100644 --- a/lib/Mojo/Server/Hypnotoad.pm +++ b/lib/Mojo/Server/Hypnotoad.pm @@ -14,9 +14,7 @@ use POSIX qw/setsid WNOHANG/; use Scalar::Util 'weaken'; # Preload -use Mojo::DOM; use Mojo::UserAgent; -use Mojolicious::Controller; use constant DEBUG => $ENV{HYPNOTOAD_DEBUG} || 0; @@ -65,10 +63,6 @@ sub run { $ENV{HYPNOTOAD_EXE} ||= $0; $0 = $ENV{HYPNOTOAD_APP}; - # Cleanup - delete $ENV{MOJO_COMMANDS_DONE}; - delete $ENV{MOJO_RELOAD}; - # Clean start exec $ENV{HYPNOTOAD_EXE} unless $ENV{HYPNOTOAD_REV}++; @@ -396,9 +390,6 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD! use Mojo::Server::Hypnotoad; - my $toad = Mojo::Server::Hypnotoad->new; - $toad->run('myapp.pl', 'hypnotoad.conf'); - =head1 DESCRIPTION L is a full featured UNIX optimized preforking async @@ -610,7 +601,7 @@ Start server. You can set the C environment variable to get some advanced diagnostics information printed to C. - MOJO_HYPNOTOAD_DEBUG=1 + HYPNOTOAD_DEBUG=1 =head1 SEE ALSO diff --git a/lib/Mojo/Server/Morbo.pm b/lib/Mojo/Server/Morbo.pm new file mode 100644 index 0000000000..da1585b264 --- /dev/null +++ b/lib/Mojo/Server/Morbo.pm @@ -0,0 +1,160 @@ +package Mojo::Server::Morbo; +use Mojo::Base -base; + +use Carp 'croak'; +use Mojo::Home; +use Mojo::Server::Daemon; +use POSIX 'WNOHANG'; + +use constant DEBUG => $ENV{MORBO_DEBUG} || 0; + +has 'app'; +has [qw/listen watch/] => sub { [] }; + +# Cache stats +my $STATS = {}; + +# "Kittens give Morbo gas." +sub run { + my $self = shift; + warn "MANAGER STARTED $$\n" if DEBUG; + + # Manager signals + $SIG{INT} = $SIG{TERM} = sub { $self->{_done} = 1 }; + $SIG{CHLD} = sub { + while ((waitpid -1, WNOHANG) > 0) { $self->{_running} = 0 } + }; + + # Manage + $self->_manage while $self->{_running} || !$self->{_done}; + + exit 0; +} + +sub _manage { + my $self = shift; + + # Resolve directories + my @files; + for my $watch (@{$self->watch}, $self->app) { + if (-d $watch) { + push @files, @{Mojo::Home->new->parse($watch)->list_files}; + } + elsif (-r $watch) { push @files, $watch } + } + + # Check files + for my $file (@files) { + my $mtime = (stat $file)[9]; + + # Startup time as default + $STATS->{$file} = $^T unless defined $STATS->{$file}; + + # Modified + if ($mtime > $STATS->{$file}) { + warn "MODIFIED $file\n" if DEBUG; + kill 'TERM', $self->{_running} if $self->{_running}; + $STATS->{$file} = $mtime; + } + } + + # Housekeeping + unless ($self->{_done}) { + $self->_spawn if !$self->{_running}; + sleep 1; + } + kill 'TERM', $self->{_running} if $self->{_done}; +} + +# "Hello little man. I WILL DESTROY YOU!" +sub _spawn { + my $self = shift; + + # Fork + my $manager = $$; + $ENV{MORBO_REV}++; + croak "Can't fork: $!" unless defined(my $pid = fork); + + # Manager + return $self->{_running} = $pid if $pid; + + # Worker + warn "WORKER STARTED $$\n" if DEBUG; + $SIG{INT} = $SIG{TERM} = $SIG{CHLD} = 'DEFAULT'; + my $daemon = Mojo::Server::Daemon->new; + $daemon->load_app($self->app); + $daemon->silent(1) if $ENV{MORBO_REV} > 1; + $daemon->listen($self->listen) if @{$self->listen}; + $daemon->prepare_ioloop; + my $loop = $daemon->ioloop; + $loop->recurring(1 => sub { shift->stop unless kill 0, $manager }); + $loop->start; + + exit 0; +} + +1; +__END__ + +=head1 NAME + +Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM! + +=head1 SYNOPSIS + + use Mojo::Server::Morbo; + +=head1 DESCRIPTION + +L is a HTTP 1.1 and WebSocket development server. +Note that this module is EXPERIMENTAL and might change without warning! + +=head1 ATTRIBUTES + +L implements the following attributes. + +=head2 C + + my $app = $morbo->app; + $morbo = $morbo->app('/home/sri/myapp.pl'); + +Application script. + +=head2 C + + my $listen = $morbo->listen; + $morbo = $morbo->listen(['http://*:3000']); + +List of ports and files to listen on, defaults to C. + +=head2 C + + my $watch = $morbo->watch; + $morbo = $morbo->watch(['/home/sri/myapp']); + +Files and directories to watch for changes, defaults to the application +script. + +=head1 METHODS + +L inherits all methods from L and implements +the following new ones. + +=head2 C + + $morbo->run; + +Start server. + +=head1 DEBUGGING + +You can set the C environment variable to get some advanced +diagnostics information printed to C. + + MORBO_DEBUG=1 + +=head1 SEE ALSO + +L, L, L. + +=cut diff --git a/lib/Mojo/Template.pm b/lib/Mojo/Template.pm index 2e3d2c073b..54ce277bd9 100644 --- a/lib/Mojo/Template.pm +++ b/lib/Mojo/Template.pm @@ -20,7 +20,7 @@ has escape_mark => '='; has expression_mark => '='; has line_start => '%'; has name => 'template'; -has namespace => 'Mojo::Template::Context'; +has namespace => 'Mojo::Template::SandBox'; has tag_start => '<%'; has tag_end => '%>'; has template => ''; @@ -785,7 +785,7 @@ Note that this method is attribute and might change without warning! my $namespace = $mt->namespace; $mt = $mt->namespace('main'); -Namespace used to compile templates, defaults to C. +Namespace used to compile templates, defaults to C. =head2 C diff --git a/lib/Mojolicious.pm b/lib/Mojolicious.pm index 4ced70e295..4d020dd500 100644 --- a/lib/Mojolicious.pm +++ b/lib/Mojolicious.pm @@ -3,6 +3,7 @@ use Mojo::Base 'Mojo'; use Carp 'croak'; use Mojolicious::Commands; +use Mojolicious::Controller; use Mojolicious::Plugins; use Mojolicious::Renderer; use Mojolicious::Routes; @@ -46,18 +47,8 @@ sub AUTOLOAD { croak qq/Can't locate object method "$method" via package "$package"/ unless my $helper = $self->renderer->helpers->{$method}; - # Load controller class - my $class = $self->controller_class; - if (my $e = Mojo::Loader->load($class)) { - $self->log->error( - ref $e - ? qq/Can't load controller class "$class": $e/ - : qq/Controller class "$class" doesn't exist./ - ); - } - # Call helper with fresh controller - return $class->new(app => $self)->$helper(@_); + return $self->controller_class->new(app => $self)->$helper(@_); } sub DESTROY { } @@ -162,23 +153,16 @@ sub dispatch { return if $res->code; if (my $code = ($tx->req->error)[1]) { $res->code($code) } elsif ($tx->is_websocket) { $res->code(426) } - if ($self->routes->dispatch($c)) { $c->render_not_found unless $res->code } + if ($self->routes->dispatch($c)) { + $c->render_not_found + unless $res->code; + } } # "Bite my shiny metal ass!" sub handler { my ($self, $tx) = @_; - # Load controller class - my $class = $self->controller_class; - if (my $e = Mojo::Loader->load($class)) { - $self->log->error( - ref $e - ? qq/Can't load controller class "$class": $e/ - : qq/Controller class "$class" doesn't exist./ - ); - } - # Embedded application my $stash = {}; if ($tx->can('stash')) { @@ -189,7 +173,8 @@ sub handler { # Build default controller and process my $defaults = $self->defaults; @{$stash}{keys %$defaults} = values %$defaults; - my $c = $class->new(app => $self, stash => $stash, tx => $tx); + my $c = + $self->controller_class->new(app => $self, stash => $stash, tx => $tx); unless (eval { $self->on_process->($self, $c); 1 }) { $self->log->fatal("Processing request failed: $@"); $tx->res->code(500); diff --git a/lib/Mojolicious/Command/Daemon.pm b/lib/Mojolicious/Command/Daemon.pm index fa08e098c1..46302acf30 100644 --- a/lib/Mojolicious/Command/Daemon.pm +++ b/lib/Mojolicious/Command/Daemon.pm @@ -20,8 +20,6 @@ These options are available: defaults to http://*:3000. --proxy Activate reverse proxy support, defaults to the value of MOJO_REVERSE_PROXY. - --reload Automatically reload application when the source - code changes. --requests Set maximum number of requests per keep-alive connection, defaults to 100. --user Set user name for process. @@ -44,7 +42,6 @@ sub run { 'keepalive=i' => sub { $daemon->keep_alive_timeout($_[1]) }, 'listen=s' => \@listen, 'proxy' => sub { $ENV{MOJO_REVERSE_PROXY} = 1 }, - reload => sub { $ENV{MOJO_RELOAD} = 1 }, 'requests=i' => sub { $daemon->max_requests($_[1]) }, 'user=s' => sub { $daemon->user($_[1]) }, 'websocket=i' => sub { $daemon->websocket_timeout($_[1]) } diff --git a/lib/Mojolicious/Guides/Cheatsheet.pod b/lib/Mojolicious/Guides/Cheatsheet.pod index 2cb3049898..ad210a2831 100644 --- a/lib/Mojolicious/Guides/Cheatsheet.pod +++ b/lib/Mojolicious/Guides/Cheatsheet.pod @@ -287,13 +287,6 @@ security reasons this is disabled by default. MOJO_PROXY=1 -=head2 C - -Enable L application reloading, changes to your application will -be detected automatically so you don't have to restart the server manually. - - MOJO_RELOAD=1 - =head2 C Enable reverse proxy support for L application. diff --git a/lib/Mojolicious/Guides/FAQ.pod b/lib/Mojolicious/Guides/FAQ.pod index 9d002aaaec..01f76bd2a5 100644 --- a/lib/Mojolicious/Guides/FAQ.pod +++ b/lib/Mojolicious/Guides/FAQ.pod @@ -108,12 +108,6 @@ application on any platform. See L for more information about running and deploying L applications. -Note that if you run your application with the C<--reload> option Windows -will lock your files. -A simple Windows editor like C will complain that the file has -already been opened by a different proccess. -More capable editors can handle this accordingly and force the change. - =head2 Whats the easiest way to install L on UNIX? Quite possibly this oneliner. diff --git a/lib/Mojolicious/Guides/Growing.pod b/lib/Mojolicious/Guides/Growing.pod index d8fd208918..2c566a1c50 100644 --- a/lib/Mojolicious/Guides/Growing.pod +++ b/lib/Mojolicious/Guides/Growing.pod @@ -192,10 +192,10 @@ This will be the foundation for our login manager example application. app->start; -The built-in web server makes working on your application a lot of fun thanks -to automatic reloading. +The built-in development web server makes working on your application a lot +of fun thanks to automatic reloading. - % ./myapp.pl daemon --reload + % morbo myapp.pl Server available at http://127.0.0.1:3000. Just save your changes and they will be automatically in effect the next time diff --git a/lib/Mojolicious/Lite.pm b/lib/Mojolicious/Lite.pm index 7fab6681f3..faa3a2b0a2 100644 --- a/lib/Mojolicious/Lite.pm +++ b/lib/Mojolicious/Lite.pm @@ -151,10 +151,11 @@ customized to override normal C<@ARGV> use. =head2 Reloading -Your application will automatically reload itself if you set the C<--reload> -option, so you don't have to restart the server after every change. +Your application will automatically reload itself if you start it with the +C development server, so you don't have to restart the server after +every change. - % ./myapp.pl daemon --reload + % morbo myapp.pl Server available at http://127.0.0.1:3000. =head2 Routes diff --git a/lib/Mojolicious/Plugin/EpRenderer.pm b/lib/Mojolicious/Plugin/EpRenderer.pm index 27b4cbfa32..852cbe93ab 100644 --- a/lib/Mojolicious/Plugin/EpRenderer.pm +++ b/lib/Mojolicious/Plugin/EpRenderer.pm @@ -1,6 +1,7 @@ package Mojolicious::Plugin::EpRenderer; use Mojo::Base 'Mojolicious::Plugin'; +use Mojo::Loader; use Mojo::Template; use Mojo::Util 'md5_sum'; @@ -15,6 +16,11 @@ sub register { my $name = $conf->{name} || 'ep'; my $template = $conf->{template} || {}; + # Custom sandbox + $template->{namespace} = + 'Mojo::Template::SandBox::' . md5_sum(($ENV{MOJO_EXE} || ref $app) . $$) + unless defined $template->{namespace}; + # Auto escape by default to prevent XSS attacks $template->{auto_escape} = 1 unless defined $template->{auto_escape}; diff --git a/lib/Mojolicious/Plugin/EplRenderer.pm b/lib/Mojolicious/Plugin/EplRenderer.pm index 108b2f0425..c9029c8744 100644 --- a/lib/Mojolicious/Plugin/EplRenderer.pm +++ b/lib/Mojolicious/Plugin/EplRenderer.pm @@ -13,10 +13,10 @@ sub register { $app->renderer->add_handler( epl => sub { my ($r, $c, $output, $options) = @_; - my $inline = $options->{inline}; # Template - my $path = $r->template_path($options); + my $inline = $options->{inline}; + my $path = $r->template_path($options); $path = md5_sum $inline if defined $inline; return unless defined $path; @@ -25,9 +25,8 @@ sub register { my $key = delete $options->{cache} || $path; my $mt = $cache->get($key); - $mt ||= Mojo::Template->new; - # Cached + $mt ||= Mojo::Template->new; if ($mt->compiled) { $$output = $mt->interpret($c) } # Not cached diff --git a/lib/Mojolicious/Plugin/RequestTimer.pm b/lib/Mojolicious/Plugin/RequestTimer.pm index f326194317..f24157468a 100644 --- a/lib/Mojolicious/Plugin/RequestTimer.pm +++ b/lib/Mojolicious/Plugin/RequestTimer.pm @@ -13,14 +13,14 @@ sub register { after_static_dispatch => sub { my $self = shift; - # New request - my $stash = $self->stash; + # Ignore static files + my $stash = $self->stash; + return if $stash->{'mojo.static'} || $stash->{'mojo.started'}; my $req = $self->req; my $method = $req->method; my $path = $req->url->path->to_abs_string; my $ua = $req->headers->user_agent || 'Anonymojo'; - $self->app->log->debug("$method $path ($ua).") - unless $stash->{'mojo.static'}; + $self->app->log->debug("$method $path ($ua)."); $stash->{'mojo.started'} = [Time::HiRes::gettimeofday()]; } ); @@ -30,17 +30,17 @@ sub register { after_dispatch => sub { my $self = shift; - # Request done + # Ignore static files my $stash = $self->stash; - return unless my $started = $stash->{'mojo.started'}; + return unless my $started = delete $stash->{'mojo.started'}; + return if $stash->{'mojo.static'}; my $elapsed = sprintf '%f', Time::HiRes::tv_interval($started, [Time::HiRes::gettimeofday()]); my $rps = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed; my $res = $self->res; my $code = $res->code || 200; my $message = $res->message || $res->default_message($code); - $self->app->log->debug("$code $message (${elapsed}s, $rps/s).") - unless $stash->{'mojo.static'}; + $self->app->log->debug("$code $message (${elapsed}s, $rps/s)."); } ); } diff --git a/lib/Mojolicious/Plugins.pm b/lib/Mojolicious/Plugins.pm index 5c40b8dad1..742c926a39 100644 --- a/lib/Mojolicious/Plugins.pm +++ b/lib/Mojolicious/Plugins.pm @@ -71,9 +71,10 @@ sub _load { my ($self, $module) = @_; # Load - my $e = Mojo::Loader->load($module); - if (ref $e) { die $e } - return if $e; + if (my $e = Mojo::Loader->load($module)) { + die $e if ref $e; + return $e; + } # Module is a plugin return unless $module->can('new') && $module->can('register'); diff --git a/script/morbo b/script/morbo new file mode 100755 index 0000000000..ea128328f8 --- /dev/null +++ b/script/morbo @@ -0,0 +1,72 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use File::Basename 'dirname'; +use File::Spec; + +use lib join '/', File::Spec->splitdir(dirname(__FILE__)), 'lib'; +use lib join '/', File::Spec->splitdir(dirname(__FILE__)), '..', 'lib'; + +# Check if Mojo is installed +eval 'use Mojo::Server::Morbo'; +die <new; +my ($help, @listen, @watch); +GetOptions( + help => sub { $help = 1 }, + 'listen=s' => \@listen, + 'watch=s' => \@watch +); +$help = 1 unless my $app = shift; + +# Usage +die <<"EOF" if $help; +usage: $0 [OPTIONS] [APPLICATION] + + morbo script/myapp + morbo myapp.pl + +These options are available: + --help Show this message. + --listen Set one or more locations you want to listen on, + defaults to http://*:3000. + --watch Set one or more directories and files to watch + for changes, defaults to the application script. +EOF + +# "All humans are vermin in the eyes of Morbo!" +$morbo->app($app); +$morbo->listen(\@listen); +$morbo->watch(\@watch); +$morbo->run; + +__END__ + +=head1 NAME + +morbo - Morbo HTTP 1.1 And WebSocket Development Server + +=head1 SYNOPSIS + + morbo myapp.pl + +=head1 DESCRIPTION + +Start L and L applications with the +L web server. + +=head1 SEE ALSO + +L, L, L. + +=cut diff --git a/t/mojo/loader.t b/t/mojo/loader.t index 005fa5a763..71beb2f230 100644 --- a/t/mojo/loader.t +++ b/t/mojo/loader.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 44; +use Test::More tests => 34; use FindBin; use lib "$FindBin::Bin/lib"; @@ -68,30 +68,3 @@ ok !!LoaderTest::C->can('new'), 'loaded successfully'; # Class does not exist ok $loader->load('LoaderTest'), 'nothing to load'; - -# Reload -my $file = IO::File->new; -my $dir = File::Temp::tempdir(CLEANUP => 1); -my $path = File::Spec->catfile($dir, 'MojoTestReloader.pm'); -$file->open("> $path"); -$file->syswrite( - "package MojoTestReloader;\nsub test1 { 23 }\nsub test3 { 32 }\n1;"); -$file->close; -push @INC, $dir; -require MojoTestReloader; -ok my $t1 = MojoTestReloader->can('test1'), 'loaded successfully'; -ok !MojoTestReloader->can('test2'), 'package is clean'; -is $t1->(), 23, 'right result'; -ok my $t3 = MojoTestReloader->can('test3'), 'loaded successfully'; -is $t3->(), 32, 'right result'; -sleep 2; -$file->open("> $path"); -$file->syswrite( - "package MojoTestReloader;\nsub test2 { 26 }\nsub test3 { 62 }\n1;"); -$file->close; -Mojo::Loader->reload; -ok my $t2 = MojoTestReloader->can('test2'), 'loaded successfully'; -ok !MojoTestReloader->can('test1'), 'package is clean'; -is $t2->(), 26, 'right result'; -ok $t3 = MojoTestReloader->can('test3'), 'loaded successfully'; -is $t3->(), 62, 'right result'; diff --git a/t/mojo/template.t b/t/mojo/template.t index a38b8c55c2..6009ff1c08 100644 --- a/t/mojo/template.t +++ b/t/mojo/template.t @@ -529,13 +529,13 @@ $output = $mt->render(<<'EOF'); %= __PACKAGE__ %= foo EOF -is $output, "Mojo::Template::Context\nworks!\n", 'right result'; +is $output, "Mojo::Template::SandBox\nworks!\n", 'right result'; $output = $mt->render(<<'EOF'); % BEGIN { MyTemplateExporter->import } %= __PACKAGE__ %= foo EOF -is $output, "Mojo::Template::Context\nworks!\n", 'right result'; +is $output, "Mojo::Template::SandBox\nworks!\n", 'right result'; # Unusable error message (stacktrace required) $mt = Mojo::Template->new;