2011-06-25
Linux 用の pow みたいなもの、hoof
https://github.com/pyromaniac/hoof
pow みたいに http://#{app_name}.dev で Rack アプリケーションにアクセスできるようになります。
どうやってるのかなと見てみたら、名前解決にカスタム NSS モジュール、ポートの振り替えに iptables を使っているようです。アプリケーションサーバは普通の Unicorn です。
インストール
hoof はポート 80/443 へのアクセスを振り替えるために iptables の REDIRECT target を使うのですが、私の環境では有効になっていませんでした。カーネル設定を変更して make && make install modules_install して reboot します。
Linux/x86_64 2.6.39-gentoo-r1 Kernel Configuration
[*] Networking support --->
Networking options --->
[*] Network packet filtering framework (Netfilter) --->
[*] Advanced netfilter configuration
IP: Netfilter Configuration --->
<*> Full NAT
<*> REDIRECT target support
iptables を有効にします。
# /etc/init.d/iptables save * Saving iptables state ... # eselect rc add iptables Adding iptables to following runlevels default [done] # rc * Loading iptables state and starting firewall ...
あとは README の通りです。
$ gem install hoof Successfully installed hoof-0.0.7 $ hoof install gcc -Wall -fPIC -c -o nss_hoof.o nss_hoof.c -O2 gcc -shared -o libnss_hoof.so.2 nss_hoof.o -Wl,-soname,libnss_hoof.so.2 -O2 ln -sf /home/ursm/.rvm/gems/ruby-1.9.2-p180/gems/hoof-0.0.7/ext/libnss_hoof.so.2 /lib Successfully installed NSS library Successfully added iptables rules $ sudo vim /etc/nsswitch.conf (hosts: に hoof を足す) # grep hoof /etc/nsswitch.conf hosts: files dns hoof
使い方
$ rails new app && cd app
$ hoof init
create ome/ursm/.hoof/app
append Gemfile
$ bundle
~/.hoof 以下にシンボリックリンクが作成されて、Gemfile に unicorn が足されます。
$ hoof start hoof: process with pid 4279 started.
サーバの起動は手動です。pow と違ってアクセス時に自動起動したりはしません。
これでめでたく http://app.dev でアプリケーションにアクセスできます…が、Chromium と hoof の NSS モジュールの相性が悪いようでガンガン落ちます。Firefox なら大丈夫でした。
2010-11-16
RubyConf 2010
ルイジアナ州ニューオリンズで開催された RubyConf 2010 に行ってきました。私の英語力は残念すぎるので、テクニカルなセッションを選んで前の方に陣取りもっぱらスライドを読んでいました。こんなのでも結構楽しめてしまうあたり RubyConf は凄い。
やはり実際に参加してみると色々な気づきがありました。例えばテスティングについて、クリーンなコードについて、コミュニティなるものについて。ここ最近抱えているいくつかの悩みに対して次に進むべき方向が見出せたように思います。
何かについて考えを深めたいとき、手っ取り早い方法は別の視点から三角測量することです。日本の Rubyist にとって RubyConf は「別の視点」を得られる最適な場と言えるでしょう。だからみんな一度は行くといいと思うよ。
2010-09-25
ursm.jp に蛍機能が実装されました
Chrome か Safari 5 でアクセスすると他の人のマウスカーソルがぼんやり光って見えます。誰もいなかったら複数ウィンドウで試してみてください。
node.js を試してみた
凄いという噂は耳にするものの、何なのかよくわからない node.js を試してみました。
イベント駆動かつ非同期 I/O なアーキテクチャのサーバサイド JavaScript 処理系で、リソースの消費を抑えつつも高いパフォーマンスを実現するものと理解しましたがあってるんでしょうか。
インストール
私は Gentoo 使いなので、ここの overlay から emerge しました。
% layman -f -o http://github.com/downloads/monoid/gentoo-nodejs/nodejs-layman.xml -a nodejs % emerge -av nodejs
hello, world
公式サイトのサンプルをそのまま写経します。
// example.js var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/');
% node example.js Server running at http://127.0.0.1:8124/ % curl localhost:8124 Hello World
Rack を生で使ってる感じですね。
npm (Node Package Manager)
RubyGems みたいなやつです。
まず ~/.npmrc を作成します。
root = /home/ursm/.npm/libraries binroot = /home/ursm/.npm/bin manroot = /home/ursm/.npm/man
.bashrc などで環境変数を設定します。
export NODE_PATH=/home/ursm/.npm/libraries:$NODE_PATH export PATH=/home/ursm/.npm/bin:$PATH export MANPATH=/home/ursm/.npm/man:$MANPATH
インストールスクリプトを走らせます。
% curl http://npmjs.org/install.sh | sh % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 338 100 338 0 0 502 0 --:--:-- --:--:-- --:--:-- 1640 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 568k 100 568k 0 0 186k 0 0:00:03 0:00:03 --:--:-- 400k node cli.js cache clean npm info it worked if it ends with ok npm info version 0.2.2 npm ok node cli.js rm npm npm info it worked if it ends with ok npm info version 0.2.2 npm info not installed npm npm ok node cli.js install npm npm info it worked if it ends with ok npm info version 0.2.2 npm info fetch http://registry.npmjs.org/npm/-/npm@0.2.2.tgz npm info install npm@0.2.2 npm info activate npm@0.2.2 npm info build Success: npm@0.2.2 npm ok It worked
入りました。node.js 界隈はインストール方法が curl ... | sh のものが多くて怖いです。
この npm というやつ、man を見てもコマンド一覧すら載っていません。とりあえずパッケージのインストールは npm install のようです。
% npm install express npm info it worked if it ends with ok npm info version 0.2.2 npm info fetch http://registry.npmjs.org/express/-/express-1.0.0rc3.tgz npm info install express@1.0.0rc3 npm info activate express@1.0.0rc3 npm info build Success: express@1.0.0rc3 npm ok
npm ls で全パッケージの一覧を表示、検索はまだないみたいですnpm ls pkgname でパッケージ名から検索できます。
CoffeeScript
いきなり脇道に入りますが、JavaScript の文法ってどうも好きになれません。括弧を省略できないのとセミコロンと function() が長すぎるのが駄目みたいです。
Ruby/Python っぽい記法から JavaScript を生成する CoffeeScript というものを試してみます。
% npm install coffee-script
先ほどのサンプルを CoffeeScript に書き直してみます。
# example.coffee
http = require('http')
http.createServer (req, res) ->
res.writeHead 200, 'Content-Type': 'text/plain'
res.end 'Hello World\n'
.listen 8124, "127.0.0.1"
console.log 'Server running at http://127.0.0.1:8124/'
こんなふうに変換されます。
% coffee -p example.coffee
(function() {
var http;
http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
return res.end('Hello World\n');
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');
}).call(this);
coffee コマンドで直接実行できます。
% coffee example.coffee Server running at http://127.0.0.1:8124/
文字列中の式展開や可変長引数のサポートもあっていい感じです。
Express
Express というフレームワークを使ってみます。Sinatra によく似ています。
% npm install express
Express は Connect というフレームワークに依存しています。Connect は Rack middleware のような仕組みを実現するためのものみたいです。
express = require('express')
app = express.createServer()
app.get '/', (req, res) ->
res.send 'Hello World\n'
app.listen 8124
素晴らしいことに Haml が使えます。haml と hamljs があって紛らわしいのですが、Express がデフォルトで対応しているのは haml の方です。
% npm install haml
express = require('express')
app = express.createServer()
app.configure ->
app.set 'view engine', 'haml'
app.get '/', (req, res) ->
res.render 'index', layout: false, locals:
message: 'hello, world'
app.listen 8124
-# views/index.haml %h1 hello %p&= message
layout: false にしないと layout.haml がないときエラーになります。いけてませんね。locals はあるものの、インスタンス変数に相当するものがないので毎回すべての変数を渡さないといけません。content_for がないのもかなり痛いです。
この Haml あまり出来がよくなくて、コメントがコメントとして機能していません。後で hamljs を試してみます。
Spark
listen するポートがコードにベタ書きしてあるのは嫌ですね。rackup みたいなものはないかと探していたら、ありました。
% npm install spark
spark で起動できるようにするには「app.js で http.Server または net.Server のインスタンスをエクスポート」すればいいらしいです。
つまりこういうことみたいです。
# app.coffee
express = require('express')
app = module.exports = express.createServer()
app.configure ->
app.set 'view engine', 'haml'
app.get '/', (req, res) ->
res.render 'index', layout: false, locals:
message: 'hello, world'
あるファイルが require() されたとき、module.exports に設定されたオブジェクトが返ります。これは CommonJS という仕様の仕組みだそうです (参考: Server-side JavaScript と CommonJS - 電脳戦士ハラキリ -SE道とは死ぬ事と見つけたり-)。
このファイルは .js じゃなくて .coffee なので、spark で起動するにはちょっと小細工しないといけません。
% spark app.coffee --eval "require('coffee-script')"
Spark server(9596) listening on http://*:3000 in development mode
require('coffee-script') すると require() が .coffee を扱えるよう拡張されます。
spark を経由するとポート番号やワーカ数を設定できるようになります。
% spark -h Usage: spark [options] Options: --comment Ignored, this is to label the process in htop -H, --host ADDR Host address, defaults to INADDR_ANY -p, --port NUM Port number, defaults to 3000 --ssl-key PATH SSL key file --ssl-crt PATH SSL certificate file -n, --workers NUM Number of worker processes to spawn -I, --include PATH Unshift the given path to require.paths -E, --env NAME Set environment, defaults to "development" -M, --mode NAME Alias of -E, --env -e, --eval CODE Evaluate the given string -C, --chdir PATH Change to the given path -c, --config PATH Load configuration module -u, --user ID|NAME Change user with setuid() -g, --group ID|NAME Change group with setgid() -v, --verbose Enable verbose output -V, --version Output spark version -K, --no-color Suppress colored terminal output -h, --help Outputy help information --ENV VAL Sets the given spark environment variable
ただ残念なことに、先ほどの CoffeeScript 対応ハックを使うと複数ワーカの起動に失敗してしまいます。おそらく spawn したプロセスで --eval の内容が実行されていないのだと思いますが、直せそうだったら直してみたいものです。
長くなってきたので続きはまた今度。
2010-09-22
デプロイ時に releases を自動的に掃除する
% cap -e deploy:cleanup ------------------------------------------------------------ cap deploy:cleanup ------------------------------------------------------------ Clean up old releases. By default, the last 5 releases are kept on each server (though you can change this with the keep_releases variable). All other deployed revisions are removed from the servers. By default, this will use sudo to clean up the old releases, but if sudo is not available for your environment, set the :use_sudo variable to false instead.
というわけなのですが、こんなの手動で実行するほど勤勉じゃないので hook を仕掛けます。
# config/deploy.rb after 'deploy:update', 'deploy:cleanup'
こうするとデプロイのたびに古い release が削除されて嬉しいというお話でした。
Tailable Cursors を試してみた
http://www.mongodb.org/display/DOCS/Tailable+Cursors
「tail -f みたいなカーソル」だそうな。
Tailable Cursor が使えるのは Capped Collection だけらしい。Capped Collection はサイズに上限があるコレクションで、上限に達すると古いドキュメントから消えていく。挿入順で読み出せることが保証されている (普通のコレクションは保証されない)。
まず Capped Collection を作る。MongoDB だと明示的にコレクションを作る場面はほとんどないんだけど、Capped Collection を作るときは必要になる。
>> require 'mongo' >> col = Mongo::Connection.new.db('test').create_collection('capped', :capped => true, :size => 10000)
Tailable Cursor を作る。
>> cur = Mongo::Cursor.new(col, :tailable => true)
適当に書いたり読んだりしてみる。
>> col.insert(:i => 1) >> col.insert(:i => 2) >> cur.next_document => {"_id"=>BSON::ObjectId('4c98da42ad7d212878000001'), "i"=>1} >> cur.next_document => {"_id"=>BSON::ObjectId('4c98da45ad7d212878000002'), "i"=>2} >> cur.next_document => nil >> col.insert(:i => 3) >> cur.next_document => {"_id"=>BSON::ObjectId('4c98da53ad7d212878000003'), "i"=>3}
カーソルがコレクションの終端に達しても、新しいドキュメントが挿入されると続けて読めるということみたい。終端で next_document したときにブロックしてくれればキューとして使えそうなんだけどな。

