Ukigumo + hubot-ircでビルド結果を通知
hubotをIRC通知するやり方はあちこちで書かれてるが、僕としてはhubot-irc-runnableを使った方がカジュアルなのでオススメ。
→jgable/hubot-irc-runnable · GitHub
その上で、scripts/httpd.coffeeに以下のコードを追加する
#http://chobie.hatenablog.com/entry/2012/02/26/125532 robot.router.post "/hubot/say", (req, res) -> room = req.body.channel message = req.body.message #user = robot.userForId room # これだと動かない user = robot.adapter.userForId room user.room = room user.type = 'groupchat' robot.send user, "#{message}" res.writeHead 200, {'Content-Type': 'text/plain'} res.end 'OK'
こうすると、POST /hubot/sayにリクエストを送ればmessageパラメータの内容をchannelパラメータで指定したチャンネルに対して投稿してくれる。これでhubotにもikachanの機能が追加される。
なので、
$app->push_notifier( Ukigumo::Client::Notify::Ikachan->new( url => 'http://example.com/hubot', channel => '#your-channel', method => 'say' ) );
Ukigumo::Clientのpush_notifierでNotify::Ikachanがそのまま使えるようになる。methodの指定が必要だから、同梱されてるukigumo-client.plとは別でスクリプトを用意しないといけないけど、/hubot/noticeのパスで受け取れるようにしておけばそのまま使える。
socket.ioで下層パスで通信する
メモ書き。例えば/path/toなんてパスの下でコンテンツの表示もsocket.ioの通信もしたい時。
// in node var app_path = "/path/to/app"; io.set('resource', app_path); io.of(app_path).on('connect', function() { // do something });
とやって、表示するHTMLでsocket.io.jsを取得する際に
<script src="/path/to/app/socket.io/socket.io.js"></script> <script type="javascript"> var socket = io.connect('http://' + location.hostname, { 'resource': 'path/to/app' }); </script>
という感じで指定する。
キモはnode側でresourceとofメソッドの引数に同じものを入れることと、クライアント側でio.connectのオプションにnode側と同じ(でも頭の/はない)resourceを入れておくことか。
別のパスで別のsocket.ioのアプリを立てたい時なんかに使える。
Perlでのfactory_girlもどきのもの
ちょっと作ってみた。
→ taiyoh/p5-Test-Factory · GitHub
PerlだとO/R Mapperが乱立しまくってて何かに依存したモジュールにするのは汎用性が低すぎてないわー、って感じだったので、DBIとSQL::MakerでDB毎の処理は抽象化させて、SQL的にはinsertだけに絞って機能を提供するようにした。その点ではRubyはActiveRecordの影響力が絶大で楽。
factory_girlほど高機能じゃないけど、作ったパターンをベースに統一されたインターフェイスからデータオブジェクトを生成できる(&DBにそのデータを突っ込む)、ってことについてはとりあえずできる。
package Foo; use Test::Factory; my $foo_counter = 0; factory foo => { col1 => 'hoge', col2 => 'fuga', col3 => 'piyo', col4 => sub { ++$foo_counter } }; package main; # O/R Mapperではないので、内部でDBI使って自前でinsertする Foo->dsn("dbi:mysql:dbname;host=localhost", 'db username', 'db password', {}); my $f1 = Foo->build('foo'); print $f1->created; # => 0 (false) print $f1->col4; # => 1 my $f2 = Foo->build('foo'); print $f2->col4; # => 2 $f1->save; # insert into foo (col1, col2, col3, col4) values (?, ?, ?, ?) /* hoge, fuga, piyo, 1 */ my $f3 = Foo->create('foo'); print $f3->created; # => 1 (true) print $f3->col4; # => 3
Amon2のコントローラをRailsっぽくしてみる
ただしルーティングはDispatcher::RouterSimple限定だけど。
これをコントローラクラスでuseしておくと、before_filterとafter_filterが使えるようになる。(ちゃんとテスト書いてないので思いつきレベル)
ただ、Module::Functions::get_public_functionsを使ってる都合で、フィルタとして使う関数はprivate methodとしてprefixに「_」を付けておかないと、public methodと見なされて登録されてしまうのが注意点。
自作のオートマトンのモジュールをCPANに上げた
承前→ 有限オートマトンの習作として - taiyoh's memorandum
半年くらい前にこんなエントリ書いていたんですが、気づいたら2月中旬に知らない人がFSM::Simpleって名前でCPANに上げてて、しかも僕の実装より全然シンプルじゃなくて腹が立ったので、"FSM::Tiny"ってモジュール名に変更してアップしました。
→ http://search.cpan.org/~taiyoh/FSM-Tiny/
ホントはこんなyak shavingじゃなくて、FSM::Tinyを使ったネタがあるんだけど、ブログに書くにはちょっと長いし説明するのがメンドイ。
node-fiberでライフチェンジングとか煽ったことを若干後悔してる
承前→ node.jsのvmとsynchronizeはライフチェンジング - taiyoh's memorandum
@hokaccha先生の記事読んで、やっぱインフルエンサーが書くと内容の充実度も反応も全然違うなー、とか思ってたのですが、特にあの記事をぶくましてるけどfiber使ったことない人は、使いどころをちゃんと見極めた方がいい、という注意喚起。もしくは過去の自分の記事に対する懺悔。
実は以前qiitaにこっそり投稿してたんだけど、以下の処理の時にエラーが発生する。
// sync_test.js var sync = require('synchronize'); sync.fiber(function() { try { sync.await((function(defer) { console.log("await 1 start"); setTimeout(function() { console.log("await 1 end"); try { sync.await((function(defer2) { console.log("await 2 start"); setTimeout(function() { console.log("await 2 end"); defer2(null, 'ok'); }, 1000); })(sync.defer())); console.log("await 2 over"); defer(null, "ok"); } catch(e) { console.log("await 2 error", e); defer(e, null); } }, 1000); })(sync.defer())); } catch(e) { console.log("await 1 error", e); } console.log("await 1 over"); });
ずらずら長いが、端的に言えば、sync.await中にもう一回sync.awaitしたらどうなるか、ということを検証したもの。結果は以下のとおりになる。
% node sync_test.js await 1 start await 1 end await 2 error [Error: no current Fiber, defer can'b be used without Fiber!] await 1 error [Error: no current Fiber, defer can'b be used without Fiber!] await 1 over
これだけだとピンとこないかもしれない。じゃあ以下のパターンがあった時は?
- ちょっと大きめのDBのselect文をsync.awaitで発行してる最中に、別のリクエストでsync.awaitを使ってファイルへの書き込みをする
- OAuth認証の処理をsync.awaitで発行してる最中に別のリクエストでsync.awaitを使ったselect文を発行する
他にも色々あると思うけど、ふつーにこういうウェブアプリケーション書いてしまうと、確実にエラーが発生し、「テストでは通ってるけど、本番に上げたらなんかちょくちょくエラーが起きる」という事態が発生する。シングルスレッドでこなすnodeならではの現象だと思う。PerlとかRubyみたいにpreforkしてリクエストを処理したらどうなるかはわからない(そもそもできるのか?このへんは自分はよく知らない)。でも、そういうアプリケーションを書く人は、よっぽどの変態か「自分はJS以外の言語を知らないから」という消極的な理由でアプリケーション実装を選択してるか以外にないだろうと思う。
要は、プロセス中のシーケンスが単一であればいい。なので、バッチスクリプトを書くときなんかは、すごく効果的なんじゃないかと思う。バッチ実装で非同期とか考えないでいい筈なのにコールバック地獄とか、ただの面倒事でしかないので。
ただのメモ書き、またはArk::ModelsライクなものをAmon2でも用意する
調子に乗ってAmon2::Modelsとかあるといいかも、って思ってたけど、
package MyApp::Models; use strict; use warnings; use Object::Container '-base'; sub import { $_[1] = 'model'; # or something goto \&Object::Container::import; } register config => sub { shift->ensure_class_loaded('MyApp::Config'); MyApp::Config->current; }; package MyApp::Config; use Config::ENV 'PLACK_ENV'; 1;
これで大体やりたいことはできる感じがしてきた。