Twitterにfilter APIという単語で検索してstreamを受信できるAPIがあるので、授業のハッシュタグでfilterしてチャットを作った。
プログラムと起動方法は https://github.com/shokai/twitter-stream-api にある。
まず先週は、Terminalで動くバージョンを作って文字をデカくして全画面表示で教室の前に表示させて使ってた。
mitukiiiさんのuserstreamというgemのAPI参照先をfilter APIに変更したらすぐできた。@username の部分はrainbow で色を付けた。
twitterのログもテキストファイルで生成される。
次に今週はHTMLで表示させるようにした。Stream APIをem-websocketでwebsocketにして、文字をデカくした全画面表示SafariにWebSocketで表示させている。
表示用のHTMLとWebSocketサーバー起動方法は githubのtwitter-stream-api/viewer にある。
NFCタグリーダーをサーバーにした。
shokai/nfctag-server – GitHub
特殊なデバイスはみんなサーバーにしてHTMLとJSから使えるようにすればいい。
1つのハードウェアにいくつもアプリをぶら下げれるし、アプリはハードウェアと別のマシンで動かせるし、JavaScript書けるし、とにかく楽だ。
下は以前作ったWebSocket clientでnfc-tag-serverに接続してtagを読んでいるところ。
タグがある時はhex dumpされた文字列が、無い時はfalseが送信されてくる。同じ値がHTTPや普通のTCP Socketでも取れるようになっている。
■NFCタグリーダー
これ使ってる。USB接続で、けっこうどこででも売ってる。これじゃなくてもlibnfcで動くデバイスならいけるはず。
ただしlibnfcでFeliCaが読めなかった。MIFAREタグなら読めた。
売り上げランキング: 12161
■インストール
git clone git://github.com/shokai/nfctag-server.git
cd nfctag-server
Macの場合
brew install libnfcこれで必要なライブラリがインストールされる。
gem install nfc eventmachine eventmachine_httpserver em-websocket args_parser
Mac以外はlibnfcを自分でインストールすればたぶんok。Ubuntuは意外にもaptにlibnfc無かった。
■起動
./nfc-tag-server --http_port 8080 --websocket_port 8081 --socket_port 8082
これでHTTPとWebSocketと普通のTCP Socketのサーバーが同時に起動する。
それぞれ
HTTP-GET
curl 'http://localhost:8080'
WebSocket
## JavaScriptwebsocket clientを使えばすぐ試せる
var ws = new WebSocket("ws://localhost:8081");
ws.onmessage = function(e){
Console.log(e.data);
};
Socket
telnet localhost 8082
のような感じに接続して、tagのIDが読める。
http://canvas.shokai.org
HTML5のcanvasを使ってみたかったので作った。
websocketも使っているので、chromeかsafariで動く。ブラウザ2つ開いてみると、同期しているのがわかりやすい。
画像のURLを末尾につけるとその画像が読み込めて、マウスで線を引くとみんなでリアルタイムに描ける。Gyazoと合わせて使うと便利。
http://canvas.shokai.org/http://gyazo.com/365d02afdf4953d40ec904df5019aa13.png
ソースはgithubに置いた https://github.com/shokai/shared-canvas
こんな風に、オンラインゲームのマップを置いて、友達と「ここ攻めろ」みたいな指示を共有したいなと思って作ってみた。
http://canvas.shokai.org/http://gyazo.com/1b2f1f6b0df60aa2d0dfe3da79106a4e.png
canvasのプラグインは色々あるけど、canvasのAPIの操作感が変わりすぎる物ばかりだったので、とりあえず何も使わないで操作してみた → draw.js
Imageオブジェクトで画像を読み込んで、onloadイベント発生時にcanvasのサイズを変更するとぴったりのサイズになる。かならずdrawImage前にサイズ変更をすること。
(92行目あたり)
var draw_img = function(img_url, onload){
var img_tag = $('canvas#img');
ctx = img_tag[0].getContext('2d');
var img = new Image();
img.onload = function(){
img_tag.attr('width', img.width).attr('height', img.height);
ctx.drawImage(img, 0, 0, img.width, img.height);
if(onload && typeof onload == 'function') onload();
};
img.src = img_url; // 読み込み開始
};
rubyとem-websocketで作ったwebsocketサーバーは、daemontoolsで自動起動・復活するようにしてある。DBは無くて、最近10万本のlineの色と太さと座標データを保存している。
また線にはどの画像URLの線か、というデータも付いているので、これで画像URL毎に部屋分けを行っている。
AndroidでADK開発している時に便利なツールを作った → http://dev.shokai.org/js/websocket-client/
ADKをAndroidに挿すと、1つしかないUSBポートが埋まってしまって、logcatでデバッグ情報を見れなくなってしまう。しかし、俺の使っているNexusSはネットワーク越しのADB(Android Debug Bridge)はrootを取らないと使えない。
素晴らしいことにブラウザからlogcatを見る – 明日の鍵で、「LogcatSocketServer」というlogcatを全てwebsocketに流してくれるサーバーを作ってくれていた。これは凄く良い。
俺は普段はlogcatを複数開いて、 ^[DE]/ やアプリ名などでgrepしてエラーだけを表示したりしている。LogcatSocketServerの出力を分割してそれぞれgrepできるしくみがあれば、普段と同じ快適なAndroidアプリ開発環境が手に入る・・・ので作った。
webブラウザでwebsocketを受信して、縦分割で複数表示し、それぞれgrepできる。(クリックで拡大)
「+」ボタンを押す毎に分割パネルが増える。
この例では4分割して、左から順に「全て表示」「IOIOに関するものだけ表示」「DebugとErrorだけ表示」「バッテリー情報を表示」している。
Androidのデバッグだけではなく普通のwebsocketを使ったwebアプリのテストにも使えるはず。
■LogcatSocketServerのインストール
ブラウザからlogcatを見る – 明日の鍵にあるLogcatSocketServerをAndroidにインストールする。
バイナリがあるのでダウンロードして、AndroidにUSBケーブルつないでインストール。
wget http://tomorrowkey.googlecode.com/svn/trunk/LogcatOnBrowser/LogcatSocketServer/bin/LogcatSocketServer.apk
adb install -r LogcatSocketServer.apk
起動させ、start serverボタンを押すとAndroidがwebsocketサーバーになる。
■webブラウザでlogcatを見る
http://dev.shokai.org/js/websocket-client/を開く。Google ChromeかSafariでしか動かない。
LogcatSocketServerの画面に表示されているIPアドレスとポート番号を、左上に入力してに接続する。
「+」ボタンを押すとパネルが増える。あとはgrep欄に正規表現を書けば表示をフィルタできる。
^[VEW]/アプリ名 とかでgrepしたりすると便利ですぞ。
ローカルで実行するならgithubに置いたのでどうぞ https://github.com/shokai/websocket-client
git clone git@github.com:shokai/websocket-client.git
open websocket-client/index.html
こういうGUIアプリ、VisualStudioとかで作ってたけど無名関数をばしばし使えるJavaScriptの方が作りやすいですね。
iPhone JavaScript Console作った
作った → https://github.com/shokai/iphone-js-console
iPhone用のJavaScript shellのようなもの。FirebugやChromeの開発パネルみたいな感じで使う。
chrome拡張やiPhoneシミュレータでiPhone用のwebページの動作は確認できるが、加速度センサやGeo Location APIなんかは実機で動かさないとデバッグできない。
しかしデバッグをしようにも、iPhone上で大量のalertを出すと気が狂ってしまう。iPhoneで実行中のwebページ上の任意のオブジェクトの中身を、Macから覗いたり値を書き換えたり関数を実行したりするツールが必要だったので作った。
なお、Androidのブラウザにはwebsocketが無いのでこのツールは動かない。Androidではlogcatで
adb logcat | grep "^./browser" --color=autoすればconsole.log、console.errorの出力は見れるので、それで何とか我慢している。
■セットアップ
MacとiPhoneを用意する。MacとiPhoneは同じ無線LANの下に接続して、互いに通信できるようにしておく。
github.com/shokai/iphone-js-consoleからcloneしてくる。
iphone-js-console.jsをhtmlに読み込ませる。JsConsole.startで自分のMacに接続させるようにする。
<script src='iphone-js-console.js' type='text/javascript' />(あらかじめconsoleを起動するMacのIPアドレスは調べておこう)
<script type='text/javascript'>
JsConsole.start('ws://192.168.1.38:8088'); // Addr of Console Server
</script>
Macで、iphone-js-consoleを起動する
./iphone-js-console
iphone-js-consoleの起動時に、rubygemsが足りないという警告がでたら足りないgemをインストールする。
gem install eventmachine em-websocket多分これで足りる。
あとreadlineを使っているけど、Macの最初から入っているreadlineは腐ってた気がするので、brewかportsで入れるといい。(わからなかったら飛ばしても多分良い)
■使う
Macのiphone-js-consoleで
alert(document.title)と打つと、
このようにiPhoneのSafari上でJavaScriptが実行される。
また、iPhoneのSafariの変数を参照することもできる。
> window.navigator.appVersion
<1> "5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
中身が複雑なオブジェクトは整形されて表示される。
> location
<1>
{"href"=>"http://192.168.1.38:8080/debug-sample.html",
"hash"=>"",
"port"=>"8080",
"protocol"=>"http:",
"origin"=>"http://192.168.1.38:8080",
"pathname"=>"/debug-sample.html",
"hostname"=>"192.168.1.38",
"host"=>"192.168.1.38:8080",
"search"=>""}
もちろん、エラーも出力される。
> homu
<1> "error : Can't find variable: homu"
> 1+2
<1> 3
> 1+
<1> "error : Parse error"
iPhoneのJSで、あらかじめ
console.log(foo);とか書いておけばもちろんMacのiphone-js-consoleにfooの値が表示される。
一度入力したコマンドは、キーボード上下で履歴を呼び出したりできる。
大変便利ですね。
■技術的なこと
iPhoneとiphone-js-consoleの接続にはWebSocketを使っている。EventMachine::WebSocketが便利だった。
iphone-js-consoleからiphoneへ送信された文字列は、そのままiPhone上でevalで実行させている。
iphoneからiphone-js-consoleへのオブジェクトの送信は、iPhone上でJSON.stringifyでシリアライズして送信して、iphone-js-consoleでparseして適当に整形して表示している。
別に難しいことはしていない。