2014年5月13日火曜日

PadrinoでSinatra::Flashを使う(Padrino::Flashでなく)

Webアプリを開発してますと、エラーメッセージを画面に出力するときに、次のリクエストの時に出力させたい、なんてことがよくあります。例えば何かの処理をした後に別のURLにリダイレクトさせて、その画面でエラーメッセージを出力させる場合がそうですね。

セッション変数に値を保持すれば、次回以降のリクエストに値を引き継げますが、もしこれがエラーメッセージだとすると、表示したらもう使わないので消さなければいけません。いちいち自分で消すのは面倒です。そこで、次のリクエストまでに限り値を保持するしくみがフレームワークやプラグインで用意されていたりします。このような仕組みは "Flash" と呼ばれています。(綴りは同じですがAdobeのFlashとは別物です。)

Sinatraの場合、公式ページにてRack:Flashを使うように書いてあります。しかしPadrinoでも使おうとしたらエラーになって使えませんでした(Gemfileに追加するだけでエラーになります。詳しい原因は追求してません)。

ということで、Padrinoの設定ファイルに記述があるSinatra:Flashを使うことにします。Sinatra:Flashは、セッション変数 session[:flash] にデータを保持しますが、直接セッション変数をアクセスせずに、flash[:key]を通じてアクセスします。ちなみに動作を見ていくとなかなか面白いです。まずは以下のコードと実行結果をみてください。

flash[:error] = "hoge"
p flash[:error]                   #=> nil
p flash.now[:error]               #=> nil
p flash.next[:error]              #=> "hoge"

flash.now[:error] = "fuga"
p flash[:error]                   #=> "fuga"
p flash.now[:error]               #=> "fuga"
p flash.next[:error]              #=> "hoge"

flash.next[:error] = "foo"
p flash[:error]                   #=> "fuga"
p flash.now[:error]               #=> "fuga"
p flash.next[:error]              #=> "foo"

入れたはずの値が入っていないように見えますが、実は以下のような仕組みです。flashはSinatra::Flashオブジェクトのインスタンスであり、Sinatra::Flashの[]= メソッドは@nextインスタンス変数に値を代入するようになっていて、[] メソッドは@nowインスタンス変数の値を出力します。そしてFlashオブジェクトのインスタンス変数@next, @nowには同名のメソッドでアクセスできるようになっています。

つまり代入は@nextへ、出力は@nowからってわけですね。よって一見上記は不思議に見えるようですがこれで正しい動作なのです。

でもって、アプリケーションのafterフックで現在の@nowの内容を@nextでまるごと置き換えてしまいます。これで次回リクエスト時には@nextの内容にアクセスされることになり、@nextへ記録した内容は次回リクエストまで限定で有効、ということになります。

ちなみに、Padrino:Flashというライブラリもあります。こちらはセッション変数 session[:_flash] にデータを保持します。使い方はSinatra:Flashと同じで、実際、Gemfileの記述をPadrino:Flashに変えただけでエラーなく動作しました。

ではデータの保存先以外に何が違うかというと、値を保存する際に、その値がシンボルである場合、自動的にI18n.traslateを通してくれること。そして付属するヘルパーメソッドのHTML出力形式が違うこと。

でも、今回は値の保存形式は独自に決めたかったし(エラーの種類ごとに更に内部にハッシュを持たせたかった)、その出力のためのヘルパーメソッドも自分で書くことにしたので、正直どちらでも良かったんですが、結局余計なことをしないシンプルなSinatra:Flashを採用したってわけです。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

0 件のコメント:

コメントを投稿