概要

railwayのコントローラーは、ユーザーの入力を受けて応答を行うモジュールです。コントローラーは一連のアクションにより構成され、それぞれのアクションは特定のルートの要求によって呼び出されます。使用するアクションはグローバル関数によって次のように定義されます。

action('index', function () {
    render();
});

機能概要

コントローラーでは応答を制御する為に次のような予約されたグローバル関数を使用することができます。
render – このアクションに関連するビューテンプレート描画します
send – ステータスコードやJSONオブジェクトなどのクライアントテキストを送信します
redirect – 特定の場所へクライアイントをリダイレクトします
header – クライアントにヘッダー情報を送信します
flash – フラッシュメッセージを表示します

以下は実行フローを制御する為の関数群です。
before – 任意のアクションの前に、このメソッドを呼び出します
after – 任意のアクションの後に、このメソッドを呼び出します
load – このメソッドを使用して別のコントローラを読み込みます
use or export – load関数によって読み込まれた別のコントローラに定義されているメソッドを取得します
publish or import – 別のコントローラでの使用を許可します
これらの関数についての詳細は下記を参照してください。

出力制御

注意:それぞれのアクションは、1つの出力メソッドを呼び出す必要があります。これは非同期の性質を持つNodeに課された唯一の用件です。もし、出力メソッドが呼び出されなかった場合は、クライアントは無限にサーバからのレスポンスを待ち続けることになるでしょう。

render

renderは0, 1つまたは2つの引数を持ちます。引数無しで呼び出した場合には、そのアクションに関連付けられたビューが描画されます。

例)posts_controller.js

action('index', function () {
    render();
});

viewとして app/views/posts/index.ejs が描画されるでしょう。

もしビューに何かデータを渡したい場合には、下記のようなの二つの方法があります。一つ目は、

action('index', function () {
    render({title: "Posts index"});
});

二つ目、

action('index', function () {
    this.title = "Posts index";
    render();
});

もし他のビューを描画したい場合には、最初の引数に名前を与えてやるだけです。

action('update', function () {
    this.title = "Update post";
    render('edit');
});

もしくは、

action('update', function () {
    render('edit', {title: "Update post"});
});

send

sendはシンプルなテキストやJSONデータを送信する1ページのアプリケーションやデバッグを行う時に便利です。重いテンプレートを描画する必要はありません。

番号(ステータスコード)で呼び出すことができます。

action('destroy', function () {
    send(403); // client will receive statusCode = 403 Forbidden
});

文字列で、呼び出す場合は、

action('sayHello', function () {
    send('Hello!'); // client will receive 'Hello!'
});

オブジェクトで、呼び出す場合は、

action('apiCall', function () {
    send({hello: 'world'}); // client will receive '{"hello":"world"}'
});

redirect

redirectはステータスコードやロケーションヘッダーを設定するだけで、クライアントを別の場所にリダイレクトすることができます。

redirect('/'); // root redirection
redirect('http://example.com'); // redirect to another host
flash

flashはセッション中は次の画面でも表示可能なメッセージを保持します。これは expressjs が標準で持っている機能です。詳しくは expressjs guide を参照して下さい。

例)posts_controller.js

action('create', function () {
    Post.create(req.body, function (err) {
        if (err) {
            flash('error', 'Error while post creation');
            render('new', {post: req.body});
        } else {
            flash('info', 'Post has been successfully created');
            redirect(path_to.posts);
        }
    });
});

このcreateアクションは成功のフラッシュ情報、失敗のフラッシュエラーを送信します。

実行フローのコントロール

DRY ( Don’t Repeat Yourself ) なコントローラーや、再利用性の高いコードを記述するために、railwayはメソッドチェインや外部コントローラの読み込みなど、いくつか追加の機能を提供します。

beforeやafterを使い、メソッドをチェインさせることができます。

checkout_controller.js

before(userRequired, {only: 'order'});
before(prepareBasket, {except: 'order'});
before(loadProducts, {only: ['products', 'featuredProducts']});
action('products', function () {...});
action('featuredProducts', function () {...});
action('order', function () {...});
action('basket', function () {...});
function userRequired () {next()}
function prepareBasket () {next()}
function loadProducts () {next()}

上記の例では 関数userRequiredはorderアクションでのみ呼ばれます。関数prepareBasketはorderアクション以外で呼び出され、関数loadProductsはproductsとfeaturedProductsアクションのみで呼び出されます。

また、beforeで実行される関数はチェイン中の次の関数へコントロールを移すためのグローバルなメソッドnextを呼ぶ必要があります。

一般的な実行コンテキスト

チェイン中には暗黙の機能がひとつあります。チェイン中のすべての関数はひとつのコンテキストから実行されている為、thisオブジェクトを使って、チェイン間でデータを渡すことができます。

function loadProducts () {
    Product.find(function (err, prds) {
        this.products = prds;
        next();
    }.bind(this));
}
action('products', function () {
    assert.ok(this.products, 'Products available here');
    render(); // also products will available in view
});

コントローラー間のコードの共有

下記の例userRequiredのようにメソッドは異なるコントローラで使用することができます。railwayはこのコントローラ間のコードの共有を行うためにload、useそして、publishメソッドを提供します。

application_controller.jsでpublishを呼び出し、requireUserを定義します。

application_controller.js

publish('requireUser', requireUser);
function requireUser () {
}

applicationコントローラーを読み込むと、ここでrequireUser関数が使用できます。

products_controller.js

load('application'); // note that _controller siffix omitted
before(use('userRequired'), {only: 'products'});

その他express.jsの機能

その他のexpress.jsの機能はまだグローバルなショートカットがありませんが、使用は可能です。なぜなら、request(または、短縮形req)やresponse(エイリアスであるres)オブジェクトがコントローラーのコンテキスト中ではグローバル変数と使用できる為です。
ビューのコンテキスト中ではよりよいコーディングスタイルを提供する為に、responseやrequestなどの長いエイリアスのみ使用できます。(ただし、ビューではこれらの変数の使用はあまり推奨されません)。