Mojolicious::LiteとData::ModelとjQueryでAJAXなチャットを作ってみた

Mojoliciousが1.01になりましたね。 まあ、それとは関係ないですが、jQueryを使ってAJAXなチャットを作ってみたので晒してみようかと思います。 AJAX自体、やった事が無いので、もっと良いやり方があるとは思います。 値の受け渡しはJSONを使ったのですが、Mojolicious側の受け取り方法がよく分からなくて$self->req->jsonとか、Mojo::JSON->newとかやっていたのですが、結果的に普通に$self->paramで受け取れるのがわかって、凄いと思いました。 JavaScript側では、Perlからのtime値をどうやって渡すのかが、調べていてもよく分からなかったので、正解に行き着くのが大変でした。 ミリセカンドで渡す、というのは気づきませんでした。 常識すぎてあまり書かれないんでしょうか…。

SEE ALSO

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env perl
#utf8
# use Acme::PerlTidy;
use utf8;

package DataModel;
use base 'Data::Model';
use Data::Model::Schema;
use Data::Model::Driver::DBI;

my $dbfile = qq{$0.db};
my $dsn = qq{dbi:SQLite:dbname=$dbfile};
my $driver = Data::Model::Driver::DBI->new(
    dsn             => $dsn,
    connect_options => { sqlite_unicode => 1 },
);
base_driver($driver);
install_model messages => schema {
    key 'id';
    column id => int => { auto_increment => 1 };
    column msg => char => { required => 1 };
    column ts => char => { required => 1 };
};

unless (-f $dbfile) {
    my $dbh = DBI->connect($dsn, '', '', { RaiseError => 1, PrintError => 0 })
        or DBI->errstr;
    for my $sql (__PACKAGE__->as_sqls) {
        $dbh->do($sql) or die $dbh->errstr;
    }
    $dbh->disconnect;
}

package main;
use Mojolicious::Lite;
use Mojo::Util qw/md5_sum/;
app->secret(md5_sum $0)->log->level('debug')->path(qq{$0.log})
    ->debug(app->secret);
app->helper(model => sub {my $dbh = DataModel->new});

get '/' => 'index';

get '/json' => sub {
    my $self = shift;
    my $model = $self->model;
    my $messages = [
        $model->get(
            'messages' => {
                where => [ id => { '>' => $self->param('from_id') } ],
                order => [ { id => 'ASC' } ],
            }
        )
    ];
    my @json;
    for my $msg (@{$messages}) {
        push @json,
            { id    => $msg->id,
                msg => $msg->msg,
                ts  => $msg->ts,
            };
    }
    $self->render(json => [ @json ]);
}           => 'json';

post '/json' => sub {
    my $self = shift;
    my $time = time;
    my $model = $self->model;
    $model->set(
        'messages' => {
            msg => $self->param('msg'), # jsonもparamで取れる
            ts  => $time,
        }
    );
};

app->start;

__DATA__
@@ index.html.ep
% layout 'main';
%= javascript begin
jQuery(function($) {
  $("#message").focus();
  var params = $.extend({
    refresh: 5,
    timer: 0,
    latest: 0
  }, params);

  var add_log = function(text) {
    $('#for_ajax').prepend("<p>" + text + "</p>");
  };

  var format_date = function(d) {
    var yyyy = d.getFullYear();
    var mm = '0' + (d.getMonth() + 1);
    var dd = '0' + d.getDate();
    var hh = '0' + d.getHours();
    var nn = '0' + d.getMinutes();
    var ss = '0' + d.getSeconds();
    return yyyy + '/' + mm.slice(-2) + '/' + dd.slice(-2) + ' ' + hh.slice(-2) + ':' + nn.slice(-2) + ':' + ss.slice(-2);
  };

  var reload_json = function() {
    $.getJSON(
      "<%= url_for 'json' %>",
      { 'from_id': params.latest },
      function(json) {
        $.each(json, function(i, val) {
          params.latest = val.id;
          var latest = new Date();
          latest.setTime(val.ts * 1000);
          add_log(val.id + ". " + val.msg + " <small>" + format_date(latest) + "</small>");
        });
        clearTimeout(params.timer); // 念のためタイマーをリセット
        params.timer = setTimeout(reload_json, params.refresh * 1000); // 次回の実行はparams.refresh秒後
      }
    );
  };

  params.timer = setTimeout(reload_json, 0); // 一回目実行

  $("#msg_form").submit(function() {
    if ($("#message").val().length > 0) {
      $.post(
        "<%= url_for 'json' %>",
        { 'msg': $("#message").val() },
        function(json) {},
        'json'
      );
      $("#message").val('');
    }
    return false;
  });
});
% end

<div>
  <%= form_for '/' => (method => 'post', id => 'msg_form') => begin %>
    <%= text_field 'msg' => (id => 'message') %>
    <%= submit_button '発言する' %>
  <% end %>
</div>
<div id="for_ajax"></div>

@@ layouts/main.html.ep
<!DOCTYPE html>
<html>
<head>
  <meta charset="<%= app->renderer->encoding %>">
  <title>Mojolicious</title>
  %= javascript 'https://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js'
</head>
<body>
  <%= content %>
</body>
</html>
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。