0

glitchtext.js作った

作った。https://github.com/shokai/js-glitchtext

ここで試せる → glitchtext.js sample
jsのサイズが圧縮しても385Kbもあるんだけど、9割以上がインターネットをクロールして取得した装飾用顔文字になってしまった。必要ない人は後述するビルド方法に基づいてsrc/plugins/face.jsを除外してビルドするともっと小さくなるはず。


元々は、去年の夏にRubyで作ったglitchtweetというtwitterクライアントがあって、それの機能をJavaScriptに移植したもの。

glitchtweetについてはずっと使ってるんだけどblogに書くの忘れてた。ふつうの発言を入れると適当に装飾・変形させてtweetするもので、20個ぐらいのプラグインがランダムに作用して壊れた文字列を作るというもの。
後輩のぽわわがTogetter – 「奇跡のglitchtweet開幕だ」にまとめてた。


■glitchtext.jsの使い方
まずTinySegmenterとglitchtext.jsを読み込んで

<script src="tiny_segmenter-0.1.js" type="text/javascript" />
<script src="glitchtext.js" type="text/javascript" />


javascriptでGlitchTextオブジェクトを作って、randomすると適当にランダムに変形される。
2〜3回randomを繰り返すとより強力に変形する。
var g = new GlitchText();
var source = '今日はかずすけ楽しかったねー また遊ぼうねー 今度はお弁当持って行こう';
var result = g.random(source);
もしくはglitch_を先頭に持った関数で任意の変形ができる。関数の一覧はmethods()関数で取得できる。

TinySegmenterが無くても動くが、いくつかのプラグインが有効化されない。



工夫したことについて書いておく

■プラグイン
今のところ、22のプラグインが入っている。
こんな感じ
glitch_addspace : こ こ に ソ ー ス 置 い た   h t t p s : / / g i t h u b . c o m / s h o k a i / j s - g l i t c h t e x t
glitch_atai : \ここにソース置いた https://github.com/shokai/js-glitchtext/
glitch_cmabridge : ここにーソスい置た htpts://github.com/shokai/js-giltchtext
glitch_double : ここにソース置いた https://github.com/shokai/js-glitchtext ここにソース置いた https://github.com/shokai/js-glitchtext
glitch_doubleChar : ここここににソソーースス置置いいたた hhttttppss::////ggiitthhuubb..ccoomm//sshhookkaaii//jjss--gglliittcchhtteexxtt
glitch_dullness : ごごにゾーズ置いだ https://github.com/shokai/js-glitchtext
glitch_face : ここにソース置いた https://github.com/shokai/js-glitchtext ォヵェリ☆彡..._〆(`・∀・´o)
glitch_fill140 : ここにソース置いた https://github.com/shokai/js-glitchtextここにソース置いた https://github.com/shokai/js-glitchtextここにソース置いた https://github.com/shokai/js-glitchtext
glitch_hiragana : ここにそーす置いた https://github.com/shokai/js-glitchtext
glitch_hirakata : ココニそーす置イタ https://github.com/shokai/js-glitchtext
glitch_ignoreSearch : こ/こ/に/ソ/ー/ス/置/い/た/ /h/t/t/p/s/://///g/i/t/h/u/b/./c/o/m///s/h/o/k/a/i///j/s/-/g/l/i/t/c/h/t/e/x/t
glitch_insertWave : こ〜こに〜ソー〜ス置〜いた h〜ttp〜s://g〜ithu〜b〜.com/shok〜a〜i/j〜s-g〜litch〜text
glitch_kaibu : ここにソース置いた https://github.com/shokai/js-glitchtextxethctilg-sj/iakohs/moc.buhtig//:sptth たい置スーソにここ
glitch_katakana : ココニソース置イタ https://github.com/shokai/js-glitchtext
glitch_kirakira : .'。:。'★。.:゜。*:*゜*'ここにソース置いた https://github.com/shokai/js-glitchtext'*゜*:*。゜:.。★'。:。'.
glitch_linePrefix : ────ここにソース置いた https://github.com/shokai/js-glitchtext
glitch_reverse : txethctilg-sj/iakohs/moc.buhtig//:sptth たい置スーソにここ
glitch_sekine : ここにショーシュ置いた https://github.com/shokai/js-glitchtext
glitch_speak : (ここにソース置いた https://github.com/shokai/js-glitchtext)
glitch_updown : ここにソー↓ス↑置いた ↓h↑t↓tps://g↑i↓t↑hub.com/shoka↓i↑/↓j↑s-gli↓tch↑t↓e↑xt
glitch_vertical_reverse : ここにソース置いた ɥʇʇds://bıʇɥnb.ɔoɯ/sɥoʞɐı/ظs-b1ıʇɔɥʇǝxʇ



■Rakeでビルドする
RakeはRubyで書けるMakefile。

今回は文字列を操作する関数をたくさん作りたかったが、1つのjsファイルに追記していくとメンテナンスがしづらい。
動作テストまでは関数群はpluginsディレクトリに置いてあって、リリース時にRakeで一つのglitchtext.jsに合体させる様にした。

今までJSのオブジェクト指向的な機能はあまり使ったことがなかった。
忘れないうちにメモしておく
  • 先にクラスを定義しておけば後からprototypeで関数を追加できる
  • for(i in object)でオブジェクトのメソッドの名前のリストを取れる
  • メソッド名がわかれば、object[メソッド名]()で関数を実行できる
  • メソッドとプロパティの区別があまり無いので高階関数が簡単に作れる
  • if(typeof hoge == ‘function’)でプロパティかメソッドか判別できる
このへん今回重要だった。



■TinySegmenterでcmabridge
cmabridgeというプラグインにTinySegmenterを使った。TinySegmenterはJavaScriptで実装されたわかち書きエンジンで、つまり日本語を単語レベルに分割してくれる。

cmabridgeはcambridge大学で発見されたという人間の認知の穴を突いた手法。単語中の隣接する1文字が入れ替わっていてもなぜか文章として読めてしまうというもの。
こんなの↓
日今はかずけすし楽かったーね たま遊ぼうーね 度今はお弁持当ってこ行う
tinysegmenterでは品詞は分類できないが、この用途には十分だった。
あとArray.prototype.mapにRubyのArray.mapを追加したので、メソッドチェーンでどんどん文字列操作できて書いてて楽しい。



■デバッグ
chromeのjsエンジンのv8を使った。Macでは
brew install v8
でインストールできる。v8はchromeの開発パネルみたいにどのファイルの何行目でエラーが起こったかを確かめられるので良い。

glitchtext.jsは、文字列を入れたら文字列を返す関数をたくさん作る必要があったので、ブラウザでデバッグするよりもterminalからたくさん文字を入れてまとめてテストできるようにした方が効率的だったので使ってみた。
rake test
すると、プラグインディレクトリとテストコードのディレクトリを総ざらいしてこんな風に実行する
v8 /Users/sho/src/js/glitchtext/libs/tiny_segmenter-0.1.js /Users/sho/src/js/glitchtext/src/glitchtext.js /Users/sho/src/js/glitchtext/src/plugins/addspace.js /Users/sho/src/js/glitchtext/src/plugins/atai.js /Users/sho/src/js/glitchtext/src/plugins/cmabridge.js /Users/sho/src/js/glitchtext/src/plugins/double.js /Users/sho/src/js/glitchtext/src/plugins/double_char.js /Users/sho/src/js/glitchtext/src/plugins/dullness.js /Users/sho/src/js/glitchtext/src/plugins/face.js /Users/sho/src/js/glitchtext/src/plugins/fill140.js /Users/sho/src/js/glitchtext/src/plugins/hiragana.js /Users/sho/src/js/glitchtext/src/plugins/hirakata.js /Users/sho/src/js/glitchtext/src/plugins/ignore_search.js /Users/sho/src/js/glitchtext/src/plugins/insert_wave.js /Users/sho/src/js/glitchtext/src/plugins/kaibun.js /Users/sho/src/js/glitchtext/src/plugins/katakana.js /Users/sho/src/js/glitchtext/src/plugins/kirakira.js /Users/sho/src/js/glitchtext/src/plugins/line_prefix.js /Users/sho/src/js/glitchtext/src/plugins/reverse.js /Users/sho/src/js/glitchtext/src/plugins/sekine.js /Users/sho/src/js/glitchtext/src/plugins/speak.js /Users/sho/src/js/glitchtext/src/plugins/updown.js /Users/sho/src/js/glitchtext/src/plugins/util.js /Users/sho/src/js/glitchtext/src/plugins/vertical_reverse.js /Users/sho/src/js/glitchtext/test/test.js

v8は、引数に複数のjsファイルを指定できて、先に指定した物から読み込まれる。先にGlitchTextクラスの定義を読み込んで、続けてプラグインを読み込んでGlitchTextクラスをprototype拡張し、最後にテストコードを実行している。

こうすると、どのプラグインの何行目でエラーが起きたかがちゃんとv8が教えてくれる。複数のプラグインを試行錯誤しながら作っている場合でも、できた物からgitにcommitできるし、別のパソコンで他のプラグインが欠けている状態からでもテストを実行できる。


■ビルド
v8に渡したのと同じく、glitchtext本体→プラグインの順にjsファイルを連結させれば単体の.jsファイルにできる。
jsminを使うとjsファイルを圧縮できた。
Macではhomebrewでインストールできる。
brew install jsmin
jsmin < input.js > output.js

1

PhoneGapでAndroidアプリを作る

増井さんがPhoneGapを使って5分で作るAndroidアプリケーションというビデオを2時間ぐらいかけて撮影していたので、俺も触発されてPhoneGapやってみた。
PhoneGapはHTML/CSS/JavaScriptでAndroidやiPhoneアプリを作れる環境。アプリ起動時にブラウザコンポーネントを読み込んで、そこにHTMLで画面を作るしくみになってる。


特にコンテンツとして面白いところもないサンプルアプリケーションっぽいのができた。バスの中で。
せっかくAndroidのネイティブアプリ相当が作れるのだから、加速度センサーを秒間30回取得して画面を描画しつつ、本体のバイブレーションとビープを鳴らしたりした。



ソースはgithubに全部置いておいた
https://github.com/shokai/droidgap-test-app


さて、PhoneGapでAndroidアプリを作る方法をメモしておく。


■Androidの開発環境を作る
まず、普通にEclipseでJavaとXMLで書かれたAndroidプロジェクトをコンパイルできる環境を整える。
android sdkインストール実機で実行するに書いた。
Javaは書かなくていいので、最初に新規プロジェクト作成したままをコンパイルして、自分のAndroid端末(もしくはエミュレータ)に送って実行できる事を確かめておく。



■PhoneGap/DroidGapでの開発環境を作る
droidgapでandroidアプリ開発にくわしく書いた。
PhoneGap本家サイトからPhoneGap一式をダウンロードすると、iPhoneとかBlackberryとかpalm用の環境も手に入るけど、DroidGapが入ってない。
DroidGapはAndroid Development Toolと連携してプロジェクトを自動生成してPhoneGapでの開発に必要なファイルも全て配置してくれる神ツールなので、DroidGapを使うべき。DroidGap自体もrubyで書かれているので不具合があってもなんとかしやすい。

https://github.com/phonegap/phonegapではなくhttps://github.com/phonegap/phonegap-androidの方を使う。



■アプリを作る

droidgap gen アプリ名
してテンプレートを作って、
ant install
でビルドしてインストールできる。アプリの実行は端末から手動でする。


最近はhamlが気に入っているので、index.hamlを書く
!!! XML
%html
%head
%script{:type => 'text/javascript', :src => "./phonegap.js"}
%script{:type => 'text/javascript', :src => "./jquery.js"}
%script{:type => 'text/javascript', :src => "./jquery.color.ver2.js"}
%script{:type => 'text/javascript', :src => "./main.js"}
%body
%h1 droidgap test app
%h2 accelerometer:
%div#acc_vars
%div#x
%div#y
%div#z
%div#color
%h2 logs
%div#log

で、hamlからhtmlに変換する
gem install haml
haml index.haml index.html


中のコンテンツはjQueryで動的に作るので、idの振られたdivがあるだけでhtmlはシンプル。
index.html
<?xml version='1.0' encoding='utf-8' ?>
<html>
<head>
<script src='./phonegap.js' type='text/javascript'></script>
<script src='./jquery.js' type='text/javascript'></script>
<script src='./jquery.color.ver2.js' type='text/javascript'></script>
<script src='./main.js' type='text/javascript'></script>
</head>
<body>
<h1>droidgap test app</h1>
<h2>accelerometer:</h2>
<div id='acc_vars'>
<div id='x'></div>
<div id='y'></div>
<div id='z'></div>
</div>
<div id='color'></div>
<h2>logs</h2>
<div id='log'></div>
</body>
</html>


jQueryは自分でダウンロードしてきて同じディレクトリに置いておく。
phonegap.jsはdroidgap gemした時に自動配置してくれてる。
で、main.jsを書く。
var acc_stat = false;
var acc_watch;
var acc_max = 0;

document.addEventListener("deviceready", function(){
navigator.notification.beep(1);
navigator.notification.vibrate(0);
accel(true);
}, true);

$(function(){
log('start');
$('div#color').css('width','100%').css('height',400).css('background-color', '#000000');
$('div#color').click(function(){
navigator.notification.beep(1);
navigator.notification.vibrate(0);
});
});

function log(message){
$('div#log').prepend($('<p>').html(message));
};

function accel(start_stop){
log("accel "+start_stop);
if(start_stop == acc_stat) return;
acc_stat = start_stop;
if(start_stop){
var opts = new Object();
opts.frequency = 30; // 30ミリ秒
acc_watch = navigator.accelerometer.watchAcceleration(
display_acc, function(e){
acc_stat = false;
navigator.accelerometer.clearWatch(acc_watch);
log("accelerometer error "+e.name+":"+e.message);
}, opts);
}
else{
navigation.accelerometer.clearWatch(acc_watch);
display_acc(new Object());
}
};

function display_acc(acc){
$('div#acc_vars div#x').html('x : '+acc.x);
$('div#acc_vars div#y').html('y : '+acc.y);
$('div#acc_vars div#z').html('z : '+acc.z);
var r = Math.floor(acc.x*60);
if(r < 0) r *= -1;
if(r > 255) r = 255;
var g = Math.floor(acc.y*60);
if(g < 0) g *= -1;
if(g > 255) g = 255;
var b = Math.floor(acc.z*60);
if(b < 0) b *= -1;
if(b > 255) b = 255;
$('div#color').css('background-color',$.parseColorCode([r,g,b]));
};
30ミリ秒毎に加速度センサーの値を取得して、毎回HTMLを書きなおしているけど特に問題ない。
あと、ハードウェアの機能が使えるようになるタイミングはdocument.onloadではなく、devicereadyイベントの後になる。
ユーザが画面に触れて動かす系のイベントは、ふつうに$(‘dom’).click()とかで登録して良いだろうけど、起動時に加速度センサーをonにする等の処理はdevicereadyイベントに登録する。

色を書き換えるのに、jQuery Color プラグインを拡張:humming birdを使わせてもらった。0〜255の値から16進数のカラーコードを作れるのでとても便利。



■JavaScriptのデバッグ
Androidのwebkitはchromeやfirebugと同じくconsole.log(message)関数が使えて、logcatで見れる。JavaScriptの実行時エラーも見れる。
Debugging Web Apps | Android Developers

PhoneGapの場合は行頭にI/Web Consoleが付いてconsole.logが、E/Web Consoleでエラーが来るのでgrepしておくと良い
adb logcat | grep "^./Web Console" --color=auto

Androidのデフォルトのwebブラウザも、console.logとエラーをadbに投げてくれる。こちらは^./browserでgrep。
adb logcat | grep "^./browser" --color=auto

このようにどのファイルの何行目でどんなエラーが起こったかまでちゃんと追えるので便利


■PhoneGapができなそうな事をいくつか
アプリ組み込み版のGoogleMapが表示できなそう。PhoneGapではJavaの方でActivityの代わりにDroidGapを継承しているのだが、AndroidのGoogleMapはGoogleが提供しているMapActivityを継承して使うことで色々と隠蔽されるようになっているので。

クロスプラットフォームになるのかというと、そうでもなさそうな気がする。特にハードウェア依存の機能があるとどのOSも同じコードで動かせる、とはいかなくなるし、同じ種類のセンサーを持っていてもセンサーが返す値はOS/機種毎に違うんじゃないか?とかいう気がしている。
画面の解像度も機種毎に違うので、特にOSが違う場合にフォントサイズがどうなってしまうのかも気になる。

Androidのサービスを作る、暗黙的インテントを受信する、ホームスクリーンウィジェットを作るなど、PhoneGapのサポートしていない機能もある。が、これは無い部分をJavaで書けばなんとかなりそう。


■JavaからJavaScriptを呼び出す
最初に起動するactivityで、
super.loadUrl("file:///android_asset/www/index.html");
とやってhtmlを読み込んでいるんだけど、同じように
super.loadUrl("javascript://alert('kzsk')");
と、ブックマークレットを渡すと、JavaScriptの関数を呼び出して値を渡せる。phonegap.jsの中で使ってた。URLの文字数の制限がどれぐらいあるか調べてないけど、これでPhoneGapで機能的に足りない部分は自分でJavaのコード書いて補えるはず。

逆に、JavaScriptからJavaを呼び出すのはどうやるかはまだわからない。今調べてる。

0

serial-http-gateway作った

シリアルポートをhttpで使えるツールを作った。
ブラウザでhttp://localhost:8783を開くとデータが読める。POSTでデータを送ると書き込める。

webブラウザでロボットを操作する部分がある、OB降臨システムというのを作っているのでその部品として作った。

githubに全部置いた。



■インストールと起動
git clone git://github.com/shokai/serial-http-gateway.git
cd serial-http-gateway
gem install serialport eventmachine eventmachine_httpserver json ArgsParser


Arduino等をMacに接続すると/dev/tty.usb〜〜という名前になる。引数に渡して起動する。
./serial-http-gateway --help
./serial-http-gateway /dev/tty.usbserial-A7006Rqn
port 8783で起動する。引数-portで変更できる。



gemが全て入っていれば実行ファイル単体で動くので、適当なパスが通っている場所にコピーして置くと便利。
sudo cp serial-http-gateway /usr/local/sbin/



■使う
HTTP POSTでシリアルポートに書き込める。
% curl -d 'testtest' 'http://localhost:8783'


HTTP-GETでシリアルポートからのデータが読める。
最近100件の受信データが保存してあって配列で返ってくる。timeに時間が入っている。
curl 'http://localhost:8783'


時間はミリ秒でunixtimeなので、1000で割れば普通のunixtimeになる
[{"data":287,"time":1296767483756},{"data":288,"time":1296767483253},{"data":291,"time":1296767482751},{"data":293,"time":1296767482246},{"data":292,"time":1296767481743},{"data":293,"time":1296767481238},{"data":294,"time":1296767480736},{"data":299,"time":1296767480233},{"data":303,"time":1296767479729},{"data":305,"time":1296767479226},{"data":307,"time":1296767478721},{"data":312,"time":1296767478219},{"data":321,"time":1296767477714},{"data":332,"time":1296767477211},{"data":344,"time":1296767476709},{"data":359,"time":1296767476204},{"data":"\u0000390","time":1296767475701}]


eventmachine_httpserver便利だなー

0

EventMachine::WebSocketでチャットを作る

Ruby & WebSockets: TCP for the Browser – igvita.comを参考にした。

websocketなのでchromeかsafariかiPhoneで動くけど、FirefoxとIEでは動かない。



■サーバー

サーバーはrubyのEventMachine::WebSocketを使った。
まずgemでインストールしておいて
gem install em-websocket


EM::runの中でEM::WebSocket.startするだけ。
1プロセスに複数クライアントつなぎっぱなしにして、全クライアントにまとめてメッセージを送るためにEM::Channelを使った。EM::WebSocketのexamplesの中に入ってたtwitterのstreamを流すサーバーでも使ってた。
EM::Channelにpushで何か入れると、あらかじめsubscribeで登録しておいたブロック全てにそれが渡されて実行される。onopenした時にsubscribeしておくとクライアントを管理できる。
server.rb
require 'rubygems'
require 'em-websocket'

MAX_LOG = 100

EM::run do

puts 'server start'
@channel = EM::Channel.new
@logs = Array.new
@channel.subscribe{|mes|
@logs.push mes
@logs.shift if @logs.size > MAX_LOG
}

EM::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
ws.onopen{
sid = @channel.subscribe{|mes|
ws.send(mes)
}
puts "<#{sid}> connected!!"
@logs.each{|mes|
ws.send(mes)
}
@channel.push("hello <#{sid}>")

# channel登録時のidを使うためにonopen内で他のイベント登録を済ませる
ws.onmessage{|mes|
puts "<#{sid}> #{mes}"
@channel.push("<#{sid}> #{mes}")
}

ws.onclose{
puts "<#{sid}> disconnected"
@channel.unsubscribe(sid)
@channel.push("<#{sid}> disconnected")
}
}
end

EM::defer do
loop do
puts Time.now.to_s
@channel.push Time.now.to_s
sleep 60*60*3 # 3時間ごと
end
end
end
websocketのつなぎっぱなし感を試したかったので、EM::deferも回しておいて3時間ごとにEM::Channelに時刻をpushしてみた。全チャットクライアントに時刻が表示される。


後から接続してきたクライアントのために、メモリ上に配列で100件ログを取っておくことにした。
新しいクライアントが来たらログの中身をまとめてsendする。プロセス自体はforkしないで全クライアントまとめて接続させてるからDBが必要ない。


サーバーは
ruby server/server.rb
でport8080で起動する。



■クライアント

次にクライアント。
ごくふつうのjQueryを読み込んだhtmlを書いておいて、
new WebSocketでサーバーに接続してonmessageとonopenとoncloseイベントを登録して、WebSocket.send関数で送信するだけ。
サーバーが再起動した時にそなえてoncloseしたらsetIntervalで定期的に接続しなおすようにすると良さげ。
var ws = new WebSocket("ws://localhost:8080");
ws.onmessage = function(e){
trace(e.data);
};
ws.onclose = function(){
log("ws closed");
};
ws.onopen = function(){
log('connected!!');
};

$(function(){
$('input#post').click(function(){
var name = $('input#name').val();
var mes = $('input#message').val();
ws.send(name+" : "+mes);
$('input#message').val("");
});
});

function log(message){
trace("[log] "+message);
};

function trace(message){
var mes_div = $('<div />').html(message);
$('div#chat').prepend(mes_div);
};



■動作環境

rubyとem-websocketがインストールされてればどこでも動かせる。

websocketは、httpの80番portと別のportで起動しないとならんのでさくらのVPSにubuntu10.04入れたサーバーで動かしてる。
server.rbのプロセスが死んでも復活するようにdaemontools使った。

0

Macのsaykanaをhttpから使えるようにした

saykanaをリモートから使いたかったので作った。Macをterminalから喋らせれるアレ。
sinatraで作っていて、http postされた文字列をsaykanaで読み上げる。これが研究室のMacProのpassengerで動いているので、他のマシンからでも研究室のMacしゃべらせれて大変楽しい。


なんか説明書こうかと思ったけどgithubのreadmeに書いたからいいや


rb-mecabが必要。saykanaがひらがな・カタカナしか読めないので、漢字をかなに変換するのに使っている。

http-postでsaykanaさせるAPIが使える。/sayにpostパラメータ message で送ればいい
/ にHTMLで書いたformがあってそこからもsayさせれる。



sayやsaykanaやafplayをsshからログインしたmacで使ったり、apache+passengerで使うと

ConnectPort: can’t find mach server port name = jackdmp_entry.502_default err = unknown error code

というエラーが出て音がならないんだけど、解決方法があった

とりあえず一旦どのユーザでもいいのでGUIでログインすれば使えるようになる。

すぐログアウトしていいので、適当なユーザーを作ってパスワードなしで起動時にログインするように設定しておいて.bashrcにlogoutとか書いておけばいいんじゃないかと思う(まだ試してない)