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 )