標高+1m

Make, Hack, Think

man ympbyc

manualhubというウェブサービスを作ったので記事を書きましょう。

ゴールデンウィークの前半3日、なにか作ろうと思ってやっちゃったのがこれです。暦通りの職場だし、後半3日は山中湖に旅行に行くので(いいでしょ!)、まとまった時間はこれしかない!
いつも通りNode.jsです。最近ほんとにjsしか書いてないんですよね。ブラウザ然りNode然りTitanium然り。Javascript便利!

manualhubの話しましょう。
manualhub、ほんとはmanhubって名前にしたかったんですが、ぐぐったらゲイサイトがヒットしちゃったのでやむなくmanualhub。
manページを使ったプロフィールサービスです。
ついったーで某やの字の名前をmanしてみたところから着想を得ました。

  man ympbyc

これできたらうれしいんじゃないかな!?

ご存知かとは思いますが、manページというのは、UNIXのコマンドに付いてくるドキュメントで、なかなか特徴的な見た目なんです。

これをプロフィールページとして使ってみましょう

そしてこれがmanualhub。ここで作ったmanページはダウンロードできます。

なかなか見やすくて、ギーク臭ただよっていい感じしますね。
こんなにかっこいいmanページを簡単に作れて、しかも公開プロフィールとしてホスティングしてくれるサービスがあったら間違いなく流行る!流行ろう!流行れ!


実装の事書きましょう。
Node.js, MongoDB とクライアントサイドのjsでできています。
ログインはgithubのOAuth2に寄生しました。認証フローがtwitterに比べてめちゃくちゃシンプルでびっくり!リダイレクトして回るだけ!HMACだNONCEだSHA1だなんて気にしないでおっけー!すごい!
注意点としては、ローカルで開発中はリダイレクト先をlocalhostにしたいんですが、どうやらこれができないみたいで、どっかにプロキシを建ててやる必要があります。今回はjsapp.usにシンプルなサーバを置いて中継させました。できるらしいです

RESTなAPIチックに書きたかったので、ejsなどのテンプレートエンジンを使ってレンダリングといった事は特に行わずに、urlに応じてhtmlを返してあげて、その中のjsからajaxAPIを叩く形にしました。RESTを意識したので、割とCOOLなURIになってると思うんです。
GET /auth/github
GET /auth/github/callback
GET /user/:name
PUT /user
GET /me
GET /:username
それから
POST /user
っていうのもあったんですが、後述する理由でなくしました。

server.js

app.put('/user', function (req, res) {
  UserModel.update({id : req.session....}, {$set : req.body...})
})
app.get('/:username', function (req, res) {
  res.render('user.html');
})

user.html::javascript

$('textarea').on('blur', function () {
  $.put('/user', {description : 'hogehogefugafuga'});
});

NodeでMongoDBを使うためのドライバにはMongooseを使いました。でもschemaはあんまり有効活用してないです。

プロフィールサービスの特性上、updateがしたいわけです。上書きsaveでも問題ないけど、アップデートしたいんだからupdateでしょ。

ところがところが、Mongooseのupdateって、MongoDBのupdateにクエリをそのまま丸投げするだけらしいんですよ。Schemaでせっかく面白いエラーメッセージいっぱい用意してバリデータを作ったのに、バリデータは通してくれないし、おまけにSomeModel.pre('update', function (){})みたいなホックもないんですよ。
というわけで自分でバリデータを書きましょう。

app.put('/user', function (req, res) {
  function validate (v) {
    return v.length > 0 && v.length < MAX_LENGTH && !v.match(/someregex/);
  }
  var change = {};
  _(req.body.changeSet).each(function (item, key) {
    if (key === 'name' || key === 'internai_id') return; //上書きされちゃ困るフィールドはスルーするー
    if (!validate(item)) return; //バリデーションに通らなかったらスルーするー
    change[key] = item;
  });
  SomeModel.update({id : req.session...}, change);
});

今回はすべてのフィールドが2000文字以下のStringっていう簡単な設計にしたので楽々でしたが、Embedded Documentとかを使ってると、再帰的にチェックしなきゃいけなかったり、型ごとのバリデータを作らなきゃいけなくなったりでめんどっちいです。耐えましょう。

ドキュメントは初回ログインのときにsaveしちゃっとくと、CRUDのCをRESTに出さずにUだけで済むので楽ちんになります。


こんなかんじです。


実行環境の話をしましょう。

もともとno.deとかnodesterとかnodejitsuとかでホストしてもらおうと思ってたんですが、どこもいっぱいでアカウントがもらえず。

dotcloudは無料スロット2つのうちtofuchaproxyで1スロット使っちゃってるのでサーバが動かせてもDBが動かせないっていう状況だし、
fluxflexはfcgiだかなんだかの上でNodeを動かすことになるから書き換えなきゃいけないし、
Herokuよくわかんないし、Dynoってなんだよ!

途方に暮れてたんですが、そういえばgehirnのRS2っていうのがあったなあと、今朝思い出しました。
SSHで接続できて、Nodeがコンパイルできるんですよ!すごいね!

#ローカル
ssh yourname@s*.rs2.gehirn.jp
#リモート
git clone http://github.com/ry/node
mkdir opt
export PREFIX=/home/yourname/opt
cd git
./configure --debug
make
make install

たしかこんなかんじ。

MongoDBはコンパイル面倒くさそうなので仮想rootっていう環境を作ってapt-getでインストールします。仮想rootってなんやねん?なんやねん?
isidaiさんのブログをみてください。
fakeroot環境ができたら

cd /root
apt-get install mongodb
mkdir db
mongod --dbpath /root/db --port 27017

こんなかんじでMongoDBが動きますょ。

そんなわけで http://s7.rs2.gehirn.jp:8080/ で公開してるんですが、アカウントの有効期限が5/11とのことなので(多分この日でβ版が終わるんじゃないかな?かな?)、とりあえず一時的な公開ということになりそうです。http://manualhub.herokuapp.com/で公開してます
昨日公開されたK社のMake::Boothに展示したかったので急いで作って急いで公開した形になります。
過度な期待はせずに個人情報ください。

ソースは https://github.com/ympbyc/manualhub にありますけどコメントとかまだ付いてません。

おしまい。