websocket.el + Amon2でリアルタイム Markdown Viewer
WebSocketがなんなのか全然わかっていませんが、サンプルを参考に
リアルタイム Markdown Viewerを作成してみました。日本語を書くとエラーになるとか、
効率を全く考えていないとか、繰り返しやってるとエラーが出るとか
問題はまだ多数ありますが、いろいろ楽しいことができそうな予感はしました。
デモ動画
初めの 10秒ぐらい戸惑っています。
サーバ側
Amon2付属の chat.psgiをちょっと直した程度です。
use strict; use warnings; use utf8; use Amon2::Lite; use Digest::MD5 (); use Text::MultiMarkdown qw/markdown/; get '/' => sub { my $c = shift; return $c->render('index.tt'); }; my $clients = {}; any '/emacs' => sub { my $c = shift; $c->websocket( sub { my $ws = shift; $ws->on_receive_message( sub { my ($c, $message) = @_; my $markdowned = markdown($message); for (keys %$clients) { $clients->{$_}->send_message($markdowned); } } ); $ws->on_eof( sub { my $c = shift; } ); $ws->on_error( sub { my $c = shift; } ); } ); }; any '/markdown' => sub { my ($c) = @_; my $id = Digest::SHA1::sha1_hex(rand() . $$ . {} . time); $c->websocket( sub { my $ws = shift; $clients->{$id} = $ws; $ws->on_receive_message( sub { my ( $c, $message ) = @_; } ); $ws->on_eof( sub { my ($c) = @_; delete $clients->{$id}; } ); $ws->on_error( sub { my ($c) = @_; delete $clients->{$id}; } ); } ); }; # load plugins __PACKAGE__->load_plugin('Web::WebSocket'); __PACKAGE__->enable_middleware('AccessLog'); __PACKAGE__->enable_middleware('Lint'); __PACKAGE__->to_app(handle_static => 1); __DATA__ @@ index.tt <!doctype html> <html> <head> <meta charset="utf-8"> <title>Realtime Markdown Viewer</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css"> </head> <body> <div class="container"> <header><h1>Demo</h1></header> <div id="preview"></div> <footer>Powered by <a href="http://amon.64p.org/">Amon2::Lite</a></footer> </div> <script type="text/javascript"> $(function () { var ws = new WebSocket('ws://localhost:5000/markdown'); ws.onopen = function () { console.log('connected'); }; ws.onclose = function (ev) { console.log('closed'); }; ws.onmessage = function (ev) { $('#preview').html(ev.data); }; ws.onerror = function (ev) { console.log(ev); }; }); </script> </body> </html>
クライアント
追記 2012/AUG/27 マルチバイト文字列対応を追加
;;; realtime-markdown-viewer.el --- ;; Copyright (C) 2012 by Syohei YOSHIDA ;; Author: Syohei YOSHIDA <syohex@gmail.com> ;; URL: ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;;; Code: (eval-when-compile (require 'cl)) (require 'websocket) (defgroup realtime-markdown-viewer nil "Realtime Markdown Viewer" :group 'text :prefix 'rtmv:) (defvar rtmv:websocket) (defun rtmv:init-websocket () (setq rtmv:websocket (websocket-open "ws://127.0.0.1:5000/emacs" :on-message (lambda (websocket frame) (message "%s" (websocket-frame-payload frame))) :on-close (lambda (websocket) (setq wstest-closed t))))) (defun rtmv:send-to-server () (if realtime-markdown-viewer-mode (let ((str (buffer-substring-no-properties (point-min) (point-max)))) (websocket-send-text rtmv:websocket (encode-coding-string str 'raw-text))))) (defun rtmv:init () (rtmv:init-websocket) (add-hook 'post-command-hook 'rtmv:send-to-server nil t)) (defun rtmv:finalize () (websocket-close rtmv:websocket) (remove-hook 'post-command-hook 'rtmv:send-to-server t)) (define-minor-mode realtime-markdown-viewer-mode "Realtime Markdown Viewer mode" :group 'realtime-markdown-viewer :init-value nil :global nil (if realtime-markdown-viewer-mode (rtmv:init) (rtmv:finalize))) (provide 'realtime-markdown-viewer) ;;; realtime-markdown-viewer.el ends here
終わりに
ブラウザの需要は高まる一方なので、WebSocketに限らず
連携できる手段というものは押さえておく必要がある知識
なのではないかと思いました。