僕のYak Shavingは終わらない

車輪の再発明をやめたらそこには壮大なYakの群れが

Mojolicious::Lite で WebSocket を使ったチャットを作る(現時点:2012年3月10日で動くコード)

(新しい記事書いたら間違って消した・・・からもう一度書いたorz)

Mojolicious::Lite で WebSocket を使ったチャットを作る - naoyaのはてなダイアリー
で書かれていたものを
【perlメモ】Mojolicious::Lite+WebSocketでのチャットプログラム - KUMA TYPE
で最新版のMojoliciousに対応してくださったんですけど、
現時点でコピペで動かなかったので最新版に修正してみました。

以下のIRCに解決方法が書いてありました。というかドキュメント読めと。

17:26 echoecho Hello! I am trying to get the hang of mojo websockets with this pretty simple chat-room example on Github (http://bit.ly/yOa4cZ), but whenever I send a message, my server fails with "Event "read" failed: Can't locate object method "send_message" via package "Mojo::Transaction::WebSocket"". Any ideas as to what I'm doing wrong?
17:27 sri that example is outdated, some methods have been renamed
17:27 echoecho Ah, that makes sense! Could you help me figure out which, or point to a newer example?
17:27 sri the websocket api is still experimental and changing
17:28 judofyr echoecho: https://metacpan.org/module/Mo[…]::Controller#send

超意訳
なんかGithubに乗ってるコード使ってもエラーでるんすけど?

いやそれふりーし、もうメソッド名もかわってんよ

おふ、最新の例とかねーの?

つーかまだWebSocketの仕様決まってねーしなぁー

とりまドキュメント

ってことでいろいろなおしたら以下みたいになりましたよー
on_messageとかon_finishとかsend_messageが全部名前と使い方が変わってましたね。。。

JSのWebSocketの部分のURLは自分の環境に合わせて変えてください。

実行は以下のようにやるとできました。perl app.pl --daemonとかじゃできなかったのはなんで?

morbo app.pl

ということで現時点で動くであろうコードをのっけておきます。
すぐタイムアウトしてしまうって人は以下の記事も参照。
Mojolicious::Lite で WebSocketのタイムアウトの時間を修正する - kazuphの車輪の再発明
app.pl

#!/usr/bin/env perl
use utf8;
use Mojolicious::Lite;
use DateTime;
use Mojo::JSON;
use Encode qw/from_to decode_utf8 encode_utf8/;
use Data::Dumper qw/Dumper/;

get '/' => sub {
    my $self = shift;
#    return $self->render(j => $j, f => $f);
} => 'index';

my $clients = {};
websocket '/echo' => sub {
    my $self = shift;

    app->log->debug(sprintf 'Client connected: %s', $self->tx);
    my $id = sprintf "%s", $self->tx;
    app->log->debug("id:".$id);
    $clients->{$id} = $self->tx;

#    $self->receive_message(
    $self->on(message => sub {
            my ($self, $msg) = @_;

            my ($name,$message) = split(/\t/,$msg);
            $self->app->log->debug('name: ', $name, 'message: ', $message);
            unless($name){
                $name = '名無し';
            }

            my $json = Mojo::JSON->new;
            my $dt   = DateTime->now( time_zone => 'Asia/Tokyo');

            for (keys %$clients) {
                $self->app->log->debug('clients', Dumper $clients->{$_});
                $clients->{$_}->send(
                    decode_utf8($json->encode({
                        hms  => $dt->hms,
                        name => $name,
                        text => $message,
                    }))
                );
            }
        }
    );

#   $self->finished(
    $self->on(finish => sub {
            app->log->debug('Client disconnected');
            delete $clients->{$id};
        }
    );
};

app->start;

__DATA__
@@ index.html.ep
% layout 'main';
%= javascript begin
jQuery(function($) {
  $('#msg').focus();

  var log = function (text) {
    $('#log').val( $('#log').val() + text + "\n");
  };
  var ws = new WebSocket('ws://localhost:3000/echo');
  ws.onopen = function () {
    log('Connection opened');
  };
  ws.onmessage = function (msg) {
    var res = JSON.parse(msg.data);
    log('[' + res.hms + '] (' + res.name + ') ' + res.text);
  };

  $('#msg').keydown(function (e) {
    if (e.keyCode == 13 && $('#msg').val()) {
        ws.send($('#name').val() + "\t" + $('#msg').val());
        $('#msg').val('');
    }
  });
    });
% end
<h1>Mojolicious + WebSocket</h1>

<p>name<input type="text" id="name" />msg<input type="text" id="msg" /></p>
<textarea id="log" readonly></textarea>
<div>
</div>

@@ layouts/main.html.ep
<html>
  <head>
    <meta charset="<%= app->renderer->encoding %>">
    <title>WebSocket Client</title>
    %= javascript 'https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js'
    <style type="text/css">
      textarea {
          width: 40em;
          height:10em;
      }
    </style>
  </head>
  <body><%= content %></body>
</html>