2012-02-09
marmaladeからmagitを入れてみた
emacsからgitを使いたいと思って、訊いてみたところmagitとかeggとかがあるそうで。
みたところmagitは何ファイルかに分かれていてmake installするカンジで、eggはほぼ一つにまとまっている。eggは本家じゃなくてforkの方が開発進んでいるかんじなのかな。
あとちょっと気になってるMarmaladeとかからパッケージをインストールするのはどうなんだろう?と思ってチャレンジしてみることにした。
ここにmagit-1.1.1が入っているので、ここからインストールすることができそう。eggは無いようだ。
まずはpackage.elを入れてパスを通す。既にauto-installを使っているのでそれを使って入れてしまった
(auto-install-from-url "http://repo.or.cz/w/emacs.git/blob_plain/1a0a666f941c99882093d7bd08ced15033bc3f0c:/lisp/emacs-lisp/package.el")
で、準備
(require 'package) (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/"))
これでmarmaladeに登録されているものをインストールしたりできるようになるらしい。magitは簡単に入った。デフォルトでは~/.emacs.d/elpa以下に入るようだ。あとは初期化ファイルで
(require 'package) (package-initialize) (package-activate 'magit '(1 1 1))
とか書いておけば使えるようになるらしい。
magit使ってみたカンジではとりあえずシェルでやっている操作はだいたいちゃんと出来そうではある。まだ慣れなくて戸惑うのもあって時間かかるけど。eggの方が良いのかなぁとかも思いつつも、まずはしばらくmagitを練習してみるつもり
2012-01-20
起動時間を計測する 改訂版
起動時間を計測する - すぎゃーんメモ の続き。
id:tomoyaさんからコメントで教えていただきまして、emacs-init-timeという関数が存在していることを知りました。定義は
(defun emacs-init-time () "Return a string giving the duration of the Emacs initialization." (interactive) (let ((str (format "%.1f seconds" (float-time (time-subtract after-init-time before-init-time))))) (if (called-interactively-p 'interactive) (message "%s" str) str)))
となっていて、まさに中でafter-init-timeとbefore-init-timeの差分を計算している。わざわざ自分で書かなくてもこのコマンド一発で起動にかかった時間を知ることができるんですね、、。ただ、表示されるのは秒単位で小数点第一位までのようで、そこらへんカスタマイズは出来ないぽい。そうか、時間の加減を扱う場合はfloat-timeとかtime-subtractとかを使うと良いんだ。
(add-hook 'after-init-hook (lambda () (message "init time: %.3f sec" (float-time (time-subtract after-init-time before-init-time)))))
と書いて小数点第三位までを表示するようにした。
2012-01-15
ddskk 14.4
SKK(ddskk)を入れてみた - すぎゃーんメモ のときに導入していたけど、改めて入れてみようと思ってみてみたら、DDSKK 14.2以降からはAPELなどを入れる必要がなくなったそうで。
$ curl -O http://openlab.ring.gr.jp/skk/maintrunk/ddskk-14.4.tar.gz $ tar zxvf ddskk-14.4.tar.gz $ cd ddskk-14.4 $ sudo make install EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs
とするだけで使えるようになるっぽい。
起動時の設定には
(require 'skk-autoloads) (custom-set-variables '(skk-sticky-key ";")) (define-key global-map (kbd "C-x C-m") 'skk-mode)
とだけ書いて使ってみてる。
2012-01-11
anything-c-yasnippetを使わずにyasnippetをanythingインタフェースで選択する
yasnippetを使ったスニペット補完を初めて使い始めている。で、補完の呼び出し方には通常のyas/expandコマンドを呼ぶ以外に、auto-complete-configに含まれるac-source-yasnippetを使う方法や、anythingインタフェースで選択するためのanything-c-yasnippetがある。
yasnippet, anything-c-yasnippetのまとめエントリー - IMAKADO::BLOG
個人的にはauto-completeの補完とyasnippetのスニペット補完は別々にしたいな、と思ってanything-c-yasnippetを使おうと思ったのだけど、どうやらコレは最新のyasnippetでは動かないらしい。
yasnippet,anything-c-yasnippetをインストールした - Dive into the Tech World!
yasnippet-0.6.1b(c?)を使うとanything-c-yasnippet.elが動かない - 放牧日記
これらの記事のように、パッチを当てる必要がある。
で、このanything-c-yasnippetやyasnippetのソースを読んでいて気付いたのだけど、今のyasnippetにはyas/prompt-functionsという変数が定義されている。ログ見る限りでは2009-07-13、v0.6.0あたりから出てきたのかな?
(defcustom yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt yas/completing-prompt yas/ido-prompt yas/no-prompt)
複数の補完候補があった場合に、ここに登録されている関数が呼ばれ、ユーザに選択させることになるらしい。それらの関数はプロンプト文字列と、選択候補のリストを引数として受け取り、確定させる場合はそのリスト内の要素を返す。nilが返された場合は次の関数に選択を任せることになるようだ。デフォルトでは上記のように定義されていて、例えばyas/x-promptはx-popup-menu関数を使ってユーザに選択させることになる。同じkeyで異なる定義のスニペットを複数登録してある場合や、2つ以上のスニペットが登録してあるモードでyas/insert-snippetを呼んだときはコレが呼ばれるはず。
なので、このprompt-functionsを自分で定義してやれば、anythingインタフェースを簡単に作ることができる。
(defun my-yas/prompt (prompt choices &optional display-fn) (let* ((names (loop for choice in choices collect (or (and display-fn (funcall display-fn choice)) coice))) (selected (anything-other-buffer `(((name . ,(format "%s" prompt)) (candidates . names) (action . (("Insert snippet" . (lambda (arg) arg)))))) "*anything yas/prompt*"))) (if selected (let ((n (position selected names :test 'equal))) (nth n choices)) (signal 'quit "user quit!")))) (custom-set-variables '(yas/prompt-functions '(my-yas/prompt)))
丁寧なことにオプショナルな第3引数としてchoicesリストそれぞれの要素(templateのオブジェクト)から登録してあるテンプレート名を引くための関数が渡されてきてくれるので、それを使って候補名のリストを作って、anything-other-bufferに渡してやるだけで良い。actionでは選択した名前をそのまま渡すようにすることで、変数selectedに選択されたテンプレート名を得ることができる。中断した場合はnilが返ってくるので、quitシグナルを送ってやることで補完も中断される。
これだけで、anything-c-yasnippetを導入してパッチを当てたりしなくても、yasnippetの補完をanythingインタフェースで実現することができた。さくさく選べるようになるのでちょっとしたモノもどんどんsnippetに登録していくと効率が上がりそう。
試しにauto-completeとyasnippetの補完を使ってコードを書く例をyoutubeに上げてみた。
スラスラっと書けるカンジがして良いですね。
2012-01-10
expressでapp全体の設定値をroutesで使う(module間で変数を受け渡す)方法いろいろ
Expressでテンプレートからプロジェクトを作ると、現在の最新版2.5.4では
.
├── app.js
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ └── index.js
└── views
├── index.jade
└── layout.jade
6 directories, 6 files
のようにファイルとディレクトリが作られる。app.jsが本体で、
var express = require('express') , routes = require('./routes') var app = module.exports = express.createServer(); ... app.get('/', routes.index);
のような形でパスに対するハンドラを登録する形になっている。routes/index.jsでは
exports.index = function(req, res){ res.render('index', { title: 'Express' }); };
となっていて、ここでそれぞれリクエストに対する処理を記述する形で作っていける。
問題
例えばoauth認証を行うハンドラを作る際に、oauthのconsumer_keyやconsumer_secretなどの値はroutes/index.jsにはベタ書きしたくない。環境変数で管理しても良いけど、設定値の数が多くなってくるとつらいので、例えば起動時にapp.jsでconfigファイルを読み込んで使うようにする(その上で環境変数でちょっと弄ったりもできる)。*1
var config = JSON.parse(require('fs').readFileSync('config.json')); config.foo = process.env.NODE_ENV === 'production' ? 'bar' : 'baz';
ここで作ったconfig変数で一元管理するようにしたい。が、そうすると、その変数はroutes/index.jsからは使えない。
こういうときにどうすれば良いか。近い話が先日、nodejs_jpのMLで議論されていた。
モジュール分割とグローバル変数のお作法 - nodejs_jp | Google グループ
自分で読んで理解した範囲で、幾つかの方法を検証してメリットとデメリットを考えてみた。自分だったらこうするかな、という形で書いたのでMLの内容とは違うアプローチだったりするけど。
1. global変数にする
config = JSON.parse(require('fs').readFileSync('config.json')); config.foo = process.env.NODE_ENV === 'production' ? 'bar' : 'baz';
グローバル変数ならroutes/index.jsからも参照できる。
exports.index = function (req, res) { console.log(config); // available res.render('index', { title: 'Express' }); };
長所
簡単に使える…ってことくらい?
短所
グローバル汚染は致命的。どこで上書きされるか分からないし管理出来ない。
2. app.setを使ってmodule.parentから参照する
MLを読むまでmodule.parentって使ったことなくて知らなかった。requireした側のexportsオブジェクトを参照出来る…?
var app = module.exports = express.createServer(); var config = JSON.parse(require('fs').readFileSync('config.json')); config.foo = process.env.NODE_ENV === 'production' ? 'bar' : 'baz'; app.set('config', config);
exports.index = function (req, res) { console.log(module.parent.exports.set('config')); // available res.render('index', { title: 'Express' }); };
といったかたちで、module.parent.exportsでapp.jsのexports(すなわちapp.js内のvar app)を得られるので、そこからsetを使って値を参照できる。
長所
短所
app.setはexpressに依存しているし、express内でもapp.setを使っているので"env"や"view"などのキー名で設定値を弄ると危険。そしてmodule.parentを使って参照するのは相互の依存が強すぎて、例えばapp.jsが
var app = express.createServer();
としか書いていなくてexportsにappを入れていないと参照できないなどの問題が考えられる。
3. クロージャを使う
routesの使い方から変更する形になるけれど、例えばroutes/index.jsを
module.exports = function (config) { return { index: function (req, res) { console.log(config); res.render('index', { title: 'Express' }); } }; };
のように「引数として設定値を受け取る関数」を返すように変形してやるこの関数がハンドラやオブジェクトを返す。で、app.jsは
var config = JSON.parse(require('fs').readFileSync('config.json')); config.foo = process.env.NODE_ENV === 'production' ? 'bar' : 'baz'; routes = routes(config);
と、require('routes')で返ってきた「関数」にconfigを引数として渡してやることで、そのconfigを内部で使用するハンドラやオブジェクトを得ることができる。この例では実行結果を同名の変数に上書きしているけど、変えてやれば混乱することはないと思う。
長所
スコープが絞れる。引数にapp丸ごと渡して2.と同様にapp.setで参照することもできるが、そこまでやる必要がないので最低限のデータ受け渡しで済む。また、app->routesの一方的な渡し方になるので、例えばroutes側でconfigが勝手に書き換えられるといった心配がなくなる。
短所
関数呼び出しを忘れないように気をつけなければならない、とか routes側のコードの見通しが少し悪くなる…とか? まぁちょっとカッコ悪い気はする
4. 共有オブジェクトとして使うmoduleを作る
module.exports = {};
とだけ書いておく。これを使って、データを共有する。app.jsでは
var config = JSON.parse(require('fs').readFileSync('config.json')); config.foo = process.env.NODE_ENV === 'production' ? 'bar' : 'baz'; require('./lib/share').config = config;
といったかたちでshareオブジェクトに代入。routes/index.jsでは
exports.index = function (req, res) { console.log(require('./../lib/share').config); // available res.render('index', { title: 'Express' }); };
という具合に参照できる。
パスに"."とか".."とか書きたくない場合は環境変数NODE_PATHに通して起動してやれば良いらしい。
$ NODE_PATH=lib node app.js
もしくはpackage.jsonに
{
"name": "application-name"
, "version": "0.0.1"
, "private": true
, "dependencies": {
"express": "2.5.4"
, "jade": ">= 0.0.1"
}
, "scripts": {
"start": "NODE_PATH=lib node app.js"
}
}
$ npm start
で起動。こうしてパスを通しておくと、どこからでもrequire('share')で参照できるようになる。ただし例えば"share"とうモジュールはnpmパッケージに存在しているので、このネーミングには注意する必要がある。
長所
共有するオブジェクトを定義しておくだけでrequire使えばどこからでも使えるようになるので便利
短所
上述のパス指定とモジュール名の衝突の懸念くらいか。個人的にはrequireしてきた値を変更する、というのが何か違和感があるのだけど… どこからでも変更できてしまうので最終的にどこでどういう変更が行われてどういう値になっているのか、がわかりにくくなりそうな気はする。
まとめ
MLの議論では他の方法も紹介されていたし他にも方法はあると思うので上記4つから選ばないといけないわけではない。結局それぞれ長所と短所があるので、それらを把握した上で使いどころを考えるしかないかな、と。ただグローバル汚染は問題外だと思うのでよっぽどのことがない限り使うべきではないでしょう。

