Mojolicious-Plugin-AutoReload
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/AutoReload.pm view on Meta::CPAN
#pod
#pod The C<auto_reload> template helper inserts the JavaScript to
#pod automatically reload the page. This helper only works when the
#pod application mode is C<development>, so you can leave this in all the
#pod time and have it only appear during local development.
#pod
#pod This is only needed if you want to control where the C<< <script> >>
#pod for automatically-reloading is rendered.
#pod
#pod =head1 ROUTES
#pod
#pod =head2 /auto_reload
#pod
#pod This plugin adds a C</auto_reload> WebSocket route to your application.
#pod
#pod =head1 SEE ALSO
#pod
#pod L<Mojolicious>
#pod
#pod =head1 THANKS
#pod
#pod Thanks to L<Grant Street Group|https://grantstreet.com> for funding
#pod continued development of this plugin!
#pod
#pod =cut
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::IOLoop;
use Mojo::Util qw( unindent trim );
sub register {
my ( $self, $app, $config ) = @_;
# This number changes every time the server restarts, so a client
# that hasn't tried pinging in a while can be told to reload.
# Need to srand because Morbo forks, otherwise we will always get
# the same nonce.
my $nonce = srand && int rand( 2**16 );
if ( $app->mode eq 'development' ) {
$app->routes->websocket( '/auto_reload' => sub {
my ( $c ) = @_;
# Start the websocket
$c->inactivity_timeout( 60 );
my $timer_id = Mojo::IOLoop->recurring( 30, sub { $c->send( 'ping' ) } );
$c->on( finish => sub {
Mojo::IOLoop->remove( $timer_id );
} );
} );
$app->hook( around_dispatch => sub {
# Using a around_dispatch hook allows us to avoid logging
# anything or doing anything when polling. This way we can
# poll faster without making debugging harder.
my ( $next, $c ) = @_;
return $next->() if $c->req->url->path ne '/auto_reload'
|| $c->req->is_handshake;
# Prevent Mojolicious from doing anything else with this
# request.
$c->stash( 'mojo.finished', 1 );
$c->render_later;
# Client is just looking for a response, but validate their
# nonce first.
if ( !$c->param( 'nonce' ) || $c->param( 'nonce' ) ne $nonce ) {
return $c->rendered( 205 );
}
return $c->rendered( 204 );
} );
$app->hook(after_render => sub {
my ( $c, $output, $format ) = @_;
return if $c->stash( 'plugin.auto_reload.disable' );
return if $format ne 'html';
if ( my $reload = $c->auto_reload ) {
# Try to add the auto-reload to the end of the body.
# Not using Mojo::DOM because it causes bizarre errors
# when trying to 'utf8::downgrade' in
# Mojo::IOLoop::Stream:
# Mojo::Reactor::Poll: I/O watcher failed: Wide
# character in subroutine entry
unless ( $$output =~ s{(</body)}{$reload$1} ) {
# Otherwise just append it, since the end will be
# the body
$$output .= $reload;
}
}
});
}
$app->helper( auto_reload => sub {
my ( $c ) = @_;
if ( $app->mode eq 'development' && !$c->stash( 'plugin.auto_reload.disable' ) ) {
$c->stash( 'plugin.auto_reload.disable' => 1 );
my $auto_reload_end_point = $c->url_for( 'auto_reload' )->path->leading_slash(1);
my $mechanism = $ENV{PLACK_ENV} ? 'poll' : 'websocket';
return unindent trim( <<"ENDHTML" );
<style>
@-webkit-keyframes auto-reload-spinner-border {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
\@keyframes auto-reload-spinner-border {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.auto-reload-modal {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
margin: 0;
padding: 0;
border: none;
/* I would prefer a blur effect, but this is as good as I can get */
background: rgba( 255, 255, 255, 0.7 );
display: flex;
( run in 3.245 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )