#!/usr/bin/env coffee
do ->
    co = require 'co'
    params = []
    for i, v of process.argv
        if i > 2
            params.push v

    task = require './tasks/' + process.argv[2]
    if not task
        console.log 'Nothing to do.'
        return

    co.apply this, [task].concat params
    .catch (e)->
        console.error e.stack
    .then ->
        console.info '[DONE]'

taskみたいな名前で保存して

$ chmod a+x task

しておく。

.
├── task
└── tasks
    ├── a.coffee
    └── user
        ├── add.coffee
        ├── remove.coffee
        └── show.coffee

みたいな構成で、 tasks/a.coffeeに

module.exports = (p1, p2)->
    console.log p1
    yield new Promise (r)->
        setTimeout ->
            do r
        , 2000
    console.log p2
    yield new Promise (r)->
        setTimeout ->
            do r
        , 1000

みたいに書いて

$ ./task a 1 2

を実行すると、まず1って出て、二秒後に2って出て、一秒後に[DONE]と出て終わる。これを利用すればユーザーをmongoose経由でmongoに追加する処理であれば./tasks/user/add.coffeeに、

module.exports = (p1, p2)->
    models = require '../../models'
    User = models.User

    if not p1 or not p2
        console.log 'plz specify username and password'
        return

    admin = new User
        username: p1
        password: p2
        approved: true

    u = yield admin.save()
    console.log 'added user: ', JSON.stringify u, 0, 4

みたいに書けば

./task user/add takuya mugendai

とやってやればそのまま有効なユーザーが作成できる。これの何が嬉しいって、

User.pre 'save', (next)->
    co (->
        salt = yield bcryptGenSalt 10
        @password = yield bcryptHash @password, salt
        do next
    ).call this
    .catch (err)->
        next err

こんな感じにパスワードを暗号化したものに差し替える処理とか保存時に引っかかっててもちゃんと保存できるようになるのである。mongoのシェルも便利だが、直接触るとどうしてもリレーションがぶっ壊れたりして鬱陶しいので、こうやって内部に割り込めると非常に助かる。ちょうどrails consoleとかpython manage.py shellみたいなノリだ(RailsやDjangoは保存とかもすべて同期的にやってくれるので、あまりこういう悩みはないけど)。

とにかくgenerator文はめっちゃ便利。