Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^4: Mojolicious refresh

by jo37 (Deacon)
on Mar 19, 2020 at 06:29 UTC ( [id://11114452]=note: print w/replies, xml ) Need Help??


in reply to Re^3: Mojolicious refresh
in thread Mojolicious refresh

Avoid polling. Changed content is pushed to the client. This is AJAX based anyway.

Greetings,
-jo

$gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$

Replies are listed 'Best First'.
Re^5: Mojolicious refresh
by Your Mother (Archbishop) on Mar 19, 2020 at 06:54 UTC

    I think it would be fantastic to see some demo code for that here.

      I was afraid somebody would ask :-)

      Some years ago I wrote such an application using AngularJS and Mojolicious::Lite. It's still running but I need to strip it down to a small demo. Maybe corona gives me the time to do that.

      Have some patience.

      Greetings,
      -jo

      $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$

        Here is an example using Mojolicious::Lite and AngularJS.

        The Mojolicious controller in this example watches for changes in the file data/content.pl. Only the parts therein that are modified are pushed to connected clients via a web socket.

        The index page show the usage of placeholders {{text}} and {{hash}} that will be replaced by the AngularJS module.

        The AngularJS module provides the variables text and hash in it's $rootContext for insertion into the HTML index page and has the client side web socket for receiving updates from the server.

        The config file data/content.pl defines a hash of values that are transmitted to the client. Changes of text will be visible on the client immediately. When the value of hash is modified and pushed to the client, this causes a modified url parameter in the <img> tag and thus forces a reload of the image. If hash is taken as the SHA sum of the given picture, a reload can be forced if and only if the picture has changed.

        Put any picture you like to public/image.png for this demo.

        Start the server and point your browser to http://localhost:8880. You may notice the displayed text changing from "text from root context" to the text provided in data/content.pl. Modify the content of data/content.pl and observe the change on the displayed page.

        demoserver.pl:
        #!/usr/bin/perl use strict; use warnings; use feature 'state'; use Mojolicious::Lite; use Mojo::Server::Daemon; use EV; use AnyEvent; use Linux::Inotify2; use constant FILE_NAME => 'data/content.pl'; use constant LISTEN => 'http://localhost:8880'; use constant TIMEOUT => 600; my %clients; our $content; sub getcontent{ my $init = shift; state $old; my $out = {}; local $content; do FILE_NAME; for my $key (keys %$content) { $out->{$key} = $content->{$key} if $init || $old->{$key} ne $content->{$key}; } app->log->info("read content from file"); $old = $content; return $out;; } sub push_all { my $content = shift; my $data = {msg => $content}; foreach my $client (values %clients) { app->log->info("pushing data to client"); $client->send({json => $data}); Mojo::IOLoop->stream($client->connection)->timeout(TIMEOUT); } }; my $inotify = new Linux::Inotify2 or die $!; sub app_file_changed { my $e = shift; app->log->info("got file modify event"); my $content = getcontent(); push_all($content) if %$content; if ($e->IN_ATTRIB) { $inotify->watch(FILE_NAME, IN_CLOSE_WRITE | IN_ATTRIB, \&app_file_changed); } }; $inotify->watch(FILE_NAME, IN_CLOSE_WRITE | IN_ATTRIB, \&app_file_chan +ged); my $ino_w = AnyEvent->io(fh => $inotify->fileno, poll => 'r', cb => sub {$inotify->poll}); AnyEvent->signal(signal => 'INT', cb => sub {exit 1;}); AnyEvent->signal(signal => 'QUIT', cb => sub {exit 1;}); AnyEvent->signal(signal => 'TERM', cb => sub {exit 1;}); my $cond = AnyEvent->condvar; get "/" => sub { my $self = shift; $self->reply->static("index.html"); }; websocket "/ws/data/" => sub { my $c = shift; my $tx = $c->tx; my $cid = "$tx"; $clients{$cid} = $tx; $c->inactivity_timeout(TIMEOUT); $c->on(json => sub { my ($c, $data) = @_; my $content = getcontent(1); app->log->info("sending initial data to client"); $data->{msg} = $content; $c->send({json => $data}); }); $c->on(finish => sub { delete $clients{$cid}; } ); $c->on(error => sub { delete $clients{$cid}; } ); }; app->secrets(['not so secret']); my $daemon = Mojo::Server::Daemon->new(app => app, listen => [LISTEN]); $daemon->start; $cond->recv;

        public/index.html:

        <html> <head> <title>Update Demo</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1. +3.16/angular.min.js"></script> <script src="demo.js"></script> </head> <body> <div ng-app="demoModule"> <div ng-controller="demoController"> <h1>Update Demo</h1> <h2>Text</h2> <div> {{text}} </div> <h2>Image</h2> <div> <img src="image.png?hash={{hash}}"/> </div> <div> </div> </body> </html>

        public/demo.js:

        (function() { 'use strict'; var demoModule = angular.module('demoModule', []); demoModule.controller('demoController', function($rootScope, initS +ervice) { $rootScope.text = 'text from root scope'; $rootScope.hash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; }); demoModule.service('initService', function($rootScope, data) { }); demoModule.factory('data', function($rootScope) { var Server = {}; var socketUri = 'ws://localhost:8880/ws/data/'; var ws = new WebSocket(socketUri); ws.onopen= function(evt) { ws.send(JSON.stringify({msg: 'init'})); }; ws.onmessage = function(evt) { var msg = JSON.parse(evt.data).msg; for (var name in msg) { $rootScope.$apply(set_parm(name, msg[name])); }; }; function set_parm(name, value) { $rootScope[name] = value; }; return Server; }); })();

        data/config.pl:

        $content = { text => 'text from server', hash => 'da39a3ee5e6b4b0d3255bfef95601890afd80709', };

        EDIT: removed workaround for <script> tags in html file.

        Greetings,
        -jo

        $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
Re^5: Mojolicious refresh
by LanX (Saint) on Mar 19, 2020 at 08:41 UTC
    > Avoid polling. Changed content is pushed to the client.

    Sure, but again:

    what for would one want this? Speed?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      In my case to avoid unnecessary traffic, especially for generated images.

      I can't tell how "smooth" your approach works. The push initiated update happens almost invisible.

      Greetings,
      -jo

      $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
        > push initiated update

        Probably, but logically it's a pull if you say it's initiated by the client.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11114452]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2024-04-18 05:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found