Sinatra ライクな JavaScript フレームワーク『Sammy.js』

はじめに

前に「Knockout.js は画面を切り替える手段を提供していない」っていうことをブログに書いたら

id:iakio
tutorialの中にはsammy.jsと連携してブラウザの履歴を使う例がありますね
http://learn.knockoutjs.com/#/?tutorial=webmail

っていうブクマコメントがありました。

Sammy って何?

location.hash を使ったルーティング機能を提供する、Sinatra ライクな JavaScript フレームワークとのこと。
README に書かれているサンプルコードがこちら。

$.sammy(function() {

    this.get('#/', function() {
        $('#main').text('Welcome!');
    });
    
});

たしかに Sinatra に似ていますね。
ソースコードをみた感じでは、location.hash が変わったら、登録したルートの中からマッチするものを探して、結び付けられている関数を実行しているみたいです。

Sammy で遊んでみます

サンプルコードは全部コピペで動くはずです。

ページを移動してみる
<!DOCTYPE html>
<html>
    <head>
        <title>Sammy Sample</title>
    </head>
    <body>
        <div id="main"></div>
        <ul>
            <li><a href="#/kagawa">Kagawa</a></li>
            <li><a href="#/miyaichi">Miyaichi</a></li>
            <li><a href="#/honda">Honda</a></li>
        </ul>

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://github.com/quirkey/sammy/raw/master/lib/min/sammy-latest.min.js"></script>
        <script type="text/javascript">
         var app = Sammy(function() {
            this.get("#/kagawa", function() {
                $("#main").text("Hello, Kagawa.");
            });

            this.get("#/miyaichi", function() {
                $("#main").text("Hello, Miyaichi.");
            });
            
            this.get("#/honda", function() {
                $("#main").text("Hello, Honda.");
            });
        });
        app.run();
        </script>
    </body>
</html>

Sinatra 同様、ルートは HTTP メソッドと URL マッチングパターンがペアになっています。ルートは無名関数に結び付けられています。上記は get の例。

名前付きパラメータを取得してみる
<!DOCTYPE html>
<html>
    <head>
        <title>Sammy Sample</title>
    </head>
    <body>
        <div id="main"></div>
        <ul>
            <li><a href="#/kagawa">Kagawa</a></li>
            <li><a href="#/miyaichi">Miyaichi</a></li>
            <li><a href="#/honda">Honda</a></li>
        </ul>

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://github.com/quirkey/sammy/raw/master/lib/min/sammy-latest.min.js"></script>
        <script type="text/javascript">
         var app = Sammy(function() {
            this.get("#/:name", function() {
                $("#main").text("Hello, " + this.params["name"] + ".");
            });
        });
        app.run();
        </script>
    </body>
</html>

ルートのパターンは名前付きパラメータを含むことができ、params で取得できます。

POST を処理してみる
<!DOCTYPE html>
<html>
    <head>
        <title>Sammy Sample</title>
    </head>
    <body>
        <div id="main"></div>
        <form method="POST" action="#/hello">
            <label for="name">name</label>
            <input type="text" name="name" />
            <input type="submit" value="greet" />
        </form>

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://github.com/quirkey/sammy/raw/master/lib/min/sammy-latest.min.js"></script>
        <script type="text/javascript">
         var app = Sammy(function() {
            // POST メソッドを処理する
            this.post("#/hello", function() {
                // this.params から POST パラメータを取り出せる
                // this は Sammy オブジェクト
                $("#main").text("Hello, " + this.params["name"]);
            });
        });
        app.run();
        </script>
    </body>
</html>

POST メソッドのリクエストを処理するルートも登録できます。form の submit ボタンをクリックすると、ルートに結び付けられた関数が実行されました。なんかヘンな感じ。

リダイレクトしてみる
<!DOCTYPE html>
<html>
    <head>
        <title>Sammy Sample</title>
    </head>
    <body>
        <div id="main"></div>
        <ul>
            <li><a href="#/greet/kagawa">Kagawa</a></li>
            <li><a href="#/greet/miyaichi">Miyaichi</a></li>
            <li><a href="#/greet/honda">Honda</a></li>
        </ul>

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://github.com/quirkey/sammy/raw/master/lib/min/sammy-latest.min.js"></script>
        <script type="text/javascript">
         var app = Sammy(function() {
            this.get("#/hello/:name", function() {
                $("#main").text("Hello, " + this.params["name"] + ".");
            });

            this.get("#/greet/:name", function() {
                // 他のルートにリダイレクトする
                this.redirect("#/hello/" + this.params["name"])
            });
        });
        app.run();
        </script>
    </body>
</html>

redirect メソッドで、他のルートを呼び出せます。POST メソッドのリクエストを処理した後にリダイレクト、というお馴染みの処理で使いそう。

ヘルパーを定義してみる
<!DOCTYPE html>
<html>
    <head>
        <title>Sammy Sample</title>
    </head>
    <body>
        <div id="main"></div>
        <ul>
            <li><a href="#/kagawa">Kagawa</a></li>
            <li><a href="#/miyaichi">Miyaichi</a></li>
            <li><a href="#/honda">Honda</a></li>
        </ul>

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script type="text/javascript" src="https://github.com/quirkey/sammy/raw/master/lib/min/sammy-latest.min.js"></script>
        <script type="text/javascript">
        var app = Sammy(function() {
            // ヘルパーを定義できる
            // ヘルパーはルートとテンプレート内で利用可能
            this.helpers({
                hello: function(name) {
                    return "Hello, " + name + ".";
                }
            });

            this.get("#/:name", function() {
                var name = this.params["name"];
                var message = this.hello(name);
                $("#main").text(message);
            });
        });
        app.run();
        </script>
    </body>
</html>

helpers にヘルパーを定義したオブジェクトを渡しています。定義したヘルパーはルートに結びつけた関数の中で使えます。

Knockout.js で使いそうなのはこんなところかな

他にも、テンプレートを描画したり、キャッシュが使えたり出来るみたいです。使いやすいから、JavaScript で Single Page Application を作るときの選択肢として有力じゃないでしょうか。
今度、Knockout.js と Sammy を組み合わせてみようと思います。