Skip to content

Instantly share code, notes, and snippets.

@jberger
Last active July 13, 2020 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jberger/5605062 to your computer and use it in GitHub Desktop.
Save jberger/5605062 to your computer and use it in GitHub Desktop.
OUTDATED (see comment): Chat using Mojo + Mojo::Redis
#!/usr/bin/env perl
use Mojolicious::Lite;
use Mojo::Redis;
use Mojo::JSON 'j';
sub handler {
my $tag = shift;
return sub {
my $message = $_[1] || "Closed";
app->log->debug( "$tag: $message" )
};
}
app->attr( _redis => sub {
my $redis = Mojo::Redis->new;
$redis->on( error => handler("Pub Error") );
$redis->on( close => handler("Pub") );
return $redis;
});
helper redis => sub { shift->app->_redis };
app->attr( _chat => sub {
my $redis = shift->redis;
my $chat = $redis->new($redis); #clone
$chat->subscribe('chat');
$chat->on( error => handler("Sub Error") );
$chat->on( close => handler("Sub") );
return $chat;
});
helper chat => sub {
my ($self, $cb) = @_;
my $chat = $self->app->_chat;
$chat->on( message => $cb );
};
any '/' => sub {
my $c = shift;
my $name = $c->session('name');
if ( my $new_name = $c->param('name') ) {
$c->session( name => $new_name );
$name = $new_name;
} elsif ( ! defined $name ) {
return $c->redirect_to('login');
}
$c->render('chat');
};
any '/login';
any '/logout' => sub {
my $c = shift;
$c->session( expires => 1 );
$c->redirect_to('/');
};
websocket '/ws/chat' => sub {
my $c = shift;
my $name = $c->session('name');
Mojo::IOLoop->stream($c->tx->connection)->timeout(60*60);
$c->chat( sub {
my ($s, $chan, $message) = @_;
$c->app->log->debug( "Got: $message" );
$c->send({ text => $message });
} );
$c->on( json => sub {
my ($ws, $data) = @_;
$data->{name} = $name;
my $json = j($data);
$ws->app->log->debug( "Publishing: $json" );
$c->redis->publish( chat => $json );
});
#my $r = Mojo::IOLoop->recurring( 1 => sub {
# state $i = 1;
# $c->send({ json => { name => 'Joel', message => $i++ }});
#});
#$c->on( finish => sub { Mojo::IOLoop->remove($r) } );
};
app->start;
__DATA__
@@ layouts/basic.html.ep
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
%= content
</body>
</html>
@@ login.html.ep
% layout 'basic';
% title 'Login';
%= form_for '/' => method => POST => begin
%= text_field 'name'
%= submit_button 'Login'
% end
@@ chat.html.ep
% layout 'basic';
% title 'Mojo+Redis Chat';
<input type="text" id="text"><button onclick="send()">Send</button><button onclick="disconnect()">Disconnect</button>
<div id="log"></div>
%= javascript begin
var ws;
var nows = 0;
function connect () {
if (!("WebSocket" in window)) {
nows = 1;
alert('Your browser does not support WebSockets!');
return;
}
ws = new WebSocket('<%= url_for('wschat')->to_abs %>');
ws.onmessage = function (e) {
console.log(e);
var data = JSON.parse(e.data);
$('#log').prepend('<p>' + data.name + ': ' + data.message + '</p>');
};
}
function send () {
var text = $('#text');
ws.send(JSON.stringify({ message: text.val() }));
text.val('');
}
function disconnect () { ws.close() }
$(function(){connect()});
% end
@pscott-au
Copy link

Just a quick note as searching for Mojo Redis chat|subscribe example in Google brings up this hist near top - the
NB - Mojo-Redis pubsub api changed since this was written specifically ->subscribe

In app defined helper:
$self->helper( redis => sub { state $r = Mojo::Redis->new } );

In Controller websocket handle

sub chat {
  my $c = shift;
  my $pubsub = $c->redis->pubsub;
  my $cb = $pubsub->listen('chat:example' => sub {  my ( $pubsub, $msg) = @_;  $c->send($msg) });

  $c->inactivity_timeout(3600);
  $c->on(finish => sub { $pubsub->unlisten('chat:example' => $cb) });
  $c->on(message => sub  { my ($c, $msg) = @_; $pubsub->notify('chat:example' => $msg) });
}

@jberger
Copy link
Author

jberger commented Jul 13, 2020

There is a long history of the Mojo::Redis module. Originally there was Mojo::Redis. Then to avoid breaking users of that module, when a new api was desired, the authors released Mojo::Redis2. Finally when yet another breaking version was wanted, the authors decided to reuse Mojo::Redis rather than make Mojo::Redis3 as anecdotally everyone had now moved to using 2 and the original name was simpler. It does leave some outliers of course and this is one. Thanks for seeing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment