mvc (3)
昨晩の続きで bootControllers 手続きから。定義は以下です。
function bootControllers(app) { fs.readdir(__dirname + '/controllers', function(err, files){ if (err) throw err; files.forEach(function(file){ bootController(app, file); }); }); }
controllers ディレクトリの中身を列挙しつつ、一つづつ bootController 手続きに渡しております。
で、bootController 手続きなんですが順に少しづつ確認します。
function bootController(app, file) { var name = file.replace('.js', '') , actions = require('./controllers/' + name) , plural = name + 's' // realistically we would use an inflection lib , prefix = '/' + plural;
name という変数に .js な拡張子以外のファイル名を取り出して格納。actions に require した結果を格納。plural には name に単純に 's' を付けた文字列を格納。最後の prefix に plural のアタマに '/' を付加したナニを格納してます。
// Special case for "app" if (name == 'app') prefix = '/';
で、name が 'app' の場合は prefix を '/' にしております。
で、次が面白いですね。
Object.keys(actions).map(function(action){ var fn = controllerAction(name, plural, action, actions[action]);
actions には require した結果が、と書いたんですけど、例えば controllers/app.js がどんなになってるかというと以下。
module.exports = { // / index: function(req, res){ res.render(); } };
javascript なオブジェクトになってますね。Scheme が何でもリストなように、javascript は何でも基本コレなのでしょうね。あと手続きオブジェクトを actions[action] で参照できる、とゆーのもアレ。
で、ここで先に controllerAction 手続きを確認しときます。なんか曲線的 (というか抽象的?) な手続き定義だなぁ。ざっくり確認したところでは
- 手続きオブジェクトを戻す
- 戻される手続きオブジェクトは以下が実装されている
- res.render に手続きオブジェクトを設定する
- json なフォーマットを show せい、というリクエストだった場合の処理
- 引数で渡された手続きオブジェクトを apply する
というカンジ。controllers/user.js なんかを見てみるにここがキモな模様。ただ、ちょっとまだ view 周辺の制御について、なんつーか理解が微妙ですね。
あ、あと res.render な名前云々で面白いことしてますね。
function controllerAction(name, plural, action, fn) { return function(req, res, next){ var render = res.render , format = req.params.format , path = __dirname + '/views/' + name + '/' + action + '.html'; res.render = function(obj, options, fn){ res.render = render;
一旦 res.render に格納されてる手続きオブジェクトをテンポラリな領域に保管しておいて、res.render に手続きオブジェクトを格納してるのですが、その手続きの中で再度 render から res.render に退避した手続きオブジェクトを格納してます。
で、最後で呼び出しておられます。
return res.render(path, options, fn); };
この手続きもクロージャを上手に使ってますね。とは言え、res.render にはどのような手続きオブジェクトが格納されておるのか、は確認したいな。
bootController 手続きに戻る
あとは action 毎のナニを設定するのみ、な模様。
switch(action) { case 'index': app.get(prefix, fn); break; case 'show': app.get(prefix + '/:id.:format?', fn); break; case 'add': app.get(prefix + '/:id/add', fn); break; case 'create': app.post(prefix + '/:id', fn); break; case 'edit': app.get(prefix + '/:id/edit', fn); break; case 'update': app.put(prefix + '/:id', fn); break; case 'destroy': app.del(prefix + '/:id', fn); break; }
RESTful ですね。でも show で json くれって場合の URL はどうなるんかな。