├── LICENSE ├── README.md └── ja ├── 00_intro.md ├── 01_getting_plack.md ├── 02_hello_world.md ├── 03_using_plackup.md ├── 04_reloading_applications.md ├── 05_run_a_static_web_server_with_plack.md ├── 06_convert_cgi_apps_to_psgi.md ├── 07_use_web_application_framework_in_psgi.md ├── 08_adapting_web_frameworks_to_psgi.md ├── 09_running_cgi_scripts_on_plack.md ├── 10_using_plack_middleware.md ├── 11_using_plack_builder.md ├── 12_maps_multiple_apps_with_mount_and_urlmap.md ├── 13_use_plack_test_to_test_your_application.md ├── 14_use_plack_request.md ├── 15_authenticate_your_app_with_middleware.md ├── 16_adding_jsonp_support_to_your_app.md ├── 17_serving_static_files_from_your_application.md ├── 18_load_middleware_conditionally.md ├── 19_cascade_multiple_applications.md ├── 20_access_your_local_app_from_the_internet.md ├── 21_lint_your_application_and_middleware.md ├── 22_discover_more_middleware.md ├── 23_write_your_own_middleware.md ├── 24_wrap_up.md └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009-2012 Tatsuhiko Miyagawa 2 | 3 | The content of this book is licensed under CC BY-NC 3.0. 4 | http://creativecommons.org/licenses/by-nc/3.0/ 5 | 6 | If you want it re-licensed for commercial purposes, email me at miyagawa[at]bulknews.net. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rack-handbook 2 | ============= 3 | 4 | Discipline for understanding rack - from https://github.com/miyagawa/plack-handbook 5 | 6 | (TBD English informations...) 7 | 8 | 9 | ## Rack Handbook について 10 | 11 | * @miyagawa さんによる [the Plack Handbook](http://handbook.plackperl.org/) の内容を [Rack](http://rack.rubyforge.org/) に翻訳するためのプロジェクトです。 12 | * ライセンスは原著と同じく [Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0) ](creativecommons.org/licenses/by-nc/3.0/) とします(see LICENSE)。 13 | * Pull Request Welcome! 14 | 15 | ### TODOs 16 | 17 | * 日本語のみ、英語はだいぶあとで 18 | -------------------------------------------------------------------------------- /ja/00_intro.md: -------------------------------------------------------------------------------- 1 | ## イントロダクション 2 | 3 | この本は @miyagawa さんによる [Plack Handbook](http://advent.plackperl.org/) の内容をベースにしています。元々の経緯については、原著の [intro](https://github.com/miyagawa/plack-handbook/blob/master/ja/00_intro.md) の内容もご参照ください。 4 | 5 | [Rack](http://rack.github.com/) の勉強のため、すばらしい [Plack Handbook](http://advent.plackperl.org/) をRuby語に翻訳してみよう、と言う非常に個人的な目的が元々の動機ですが、Rackに習熟したい読者のためになれば何よりに思います。 6 | 7 | 本のソースコードはMarkdownフォーマットで[github レポジトリ](https://github.com/miyagawa/plack-handbook)ダウンロードでき(るようになる予定...)、Creative Commonsでライセンスされます。詳細はLICENSEファイルを参照してください。 8 | 9 | 2012年11月 近藤うちお 10 | 11 | 12 | -------------------------------------------------------------------------------- /ja/01_getting_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 1: Rackを入手 2 | 3 | まずはじめに、[Rack](https://rubygems.org/gems/rack)をインストールしましょう。Rackは通常のRubygemとして配布されているので、インストールは[Rubygems](https://rubygems.org/)をインストールしたシェルをたちあげて以下のようにタイプするだけです。 4 | 5 | ``` 6 | gem install rack 7 | ``` 8 | 9 | Rackと言う名前には二つの側面があります。ひとつはRackの[SPEC](http://rack.rubyforge.org/doc/files/SPEC.html)という意味、もう一つは上記の手順でインストールしたRackに関する様々なユーティリティツール群をまとめたgemという意味です。 10 | 11 | Rackは標準的なRubyの仕様にしか依存していないため、Ruby 1.8 以上のバージョンであれば問題無く動作しますし、CコンパイラのないWin32やDeveloper ToolsのないMac OS X環境でも利用が可能です。 12 | 13 | Rack gem をインストールすることで、以下のようなものが利用可能になります。 14 | 15 | * rackup コマンド 16 | * 最小限の Rack アプリケーション 17 | * 最小限の Rack ミドルウェア 18 | * その他、基本的なユーティリティ 19 | 20 | 標準のサーバ実装であるWEBrickは1.8以降のRuby本体に標準添付されています。また、テストに関しては、 rack-test というgemが存在します。 21 | 22 | ドキュメントに関しては、 ri と RDoc のどちらも利用可能です。以下のコマンドで明示的にインストールできます。 23 | 24 | ``` 25 | gem install rack --ri --rdoc 26 | ``` 27 | 28 | インストールの後、説明が読みたいクラスなどについて、以下のコマンドで説明が見られます。 29 | 30 | ``` 31 | ri Rack::Request 32 | ``` 33 | 34 | あるいは、以下のコマンドでRDocを閲覧するためのサーバが立ち上がります。 35 | 36 | ``` 37 | gem server 38 | ``` 39 | 40 | 立ち上がったあと、 [http://localhost:8808](http://localhost:8808) にアクセスしてみてください。 41 | 42 | -------------------------------------------------------------------------------- /ja/02_hello_world.md: -------------------------------------------------------------------------------- 1 | ## Day 2: Hello World 2 | 3 | プログラミング言語の学習で最初に書くプログラムは "Hello World" を表示することです。Rackでもそうしてみましょう。 4 | 5 | **注意:** 今日のコードは理解のためにRackの生インタフェースを利用して書かれていますが、あなたがWebアプリケーションフレームワークの作者でない限り、実際にはこうしたコードを書く必要はありません。Rubyを利用したWebアプリケーションフレームワークは現在ほとんど全てのものがRackをサポートしています。例えば、Ruby Toolboxと言うサイトで[RubyのWebアプリケーションフレームワーク](https://www.ruby-toolbox.com/categories/web_app_frameworks)を人気順に見ることができます。 6 | 7 | ### Hello, World 8 | 9 | "Hello World" を表示するための最小限のコードは以下のようになります。 10 | 11 | app = lambda {|env| 12 | [200, {'Content-Type' => 'text/plain'}, [ 'Hello World' ] ] 13 | } 14 | run app 15 | 16 | RackアプリケーションはRubyのProcオブジェクト(ここでは`lambda`メソッドを用いて生成しています)を利用して記述が可能です。ここでは`app`という変数名に格納しています(が、名前はなんでも構いません)。このProcオブジェクトは1つのブロック引数`|env|`を受け取り、ステータス、ヘッダとボディを格納したArrayオブジェクトを返します。 17 | 18 | このコードを`hello.ru`というファイルに保存し、rackupコマンドで起動しましょう: 19 | 20 | > rackup hello.ru 21 | [2012-11-26 16:56:32] INFO WEBrick 1.3.1 22 | [2012-11-26 16:56:32] INFO ruby 1.9.3 (2012-11-10) [x86_64-darwin11.4.2] 23 | [2012-11-26 16:56:32] INFO WEBrick::HTTPServer#start: pid=41221 port=9292 24 | 25 | rackup はアプリケーションをデフォルトのHTTPサーバ WEBrick を利用して localhost の9292番ポートで起動します。`http://127.0.0.1:9292/` をブラウザで開くと、Hello Worldが表示されましたか? 26 | 27 | ### 違うものを表示 28 | 29 | Hello Worldはとてもシンプルなものですが、もう少し違ったことをやってみましょう。クライアント情報をRack経由で環境変数から取得し、表示します。 30 | 31 | app = lambda {|env| 32 | [ 33 | 200, 34 | {'Content-Type' => 'text/plain'}, 35 | [ "Hello stranger from #{env['REMOTE_ADDR']}!" ] 36 | ] 37 | } 38 | run app 39 | 40 | このコードはクライアントのリモートアドレスをRack経由の環境変数を格納したハッシュ`env`から表示します。ローカルホストで起動している場合、値は127.0.0.1 になるはずです。`env`にはHTTPの接続やクライアントに関する情報、たとえばHTTPヘッダやリクエストパスなどが格納されていて、CGIの環境変数によく似ています。 41 | 42 | テキスト以外のデータをファイルから表示するには、以下のようにします。 43 | 44 | app = lambda {|env| 45 | case env['PATH_INFO'] 46 | when '/favicon.ico' 47 | file = File.open("/path/to/favicon.ico", 'r') 48 | [200, {'Content-Type' => 'image/x-icon'}, file] 49 | when '/' 50 | [200, {'Content-Type' => 'text/plain'}, [ "Hello again" ]] 51 | else 52 | [404, {'Content-Type' => 'text/html'}, [ '404 Not Found' ]] 53 | end 54 | } 55 | run app 56 | 57 | このアプリケーションはリクエストパスが`/favicon.ico`となっている場合に`favicon.ico`を表示し、ルート(/)については"Hello again"、その他のパスには404を返します。Rubyの標準的な`File`クラスのインスタンスはRackのボディにそのまま設定することができますし、ステータスコードについても妥当な数字をいれることができます。 58 | -------------------------------------------------------------------------------- /ja/03_using_plackup.md: -------------------------------------------------------------------------------- 1 | ## Day 3: rackupを使う 2 | 3 | Day 2の記事ではrackupコマンドを利用してHello World Rackアプリケーションを起動しました。 4 | 5 | rackup はRackアプリケーションを起動するためのコマンドラインランチャーで、PSGIのplackupをインスパイアしました。`.ru`ファイルに保存されたRackアプリケーションであれば、Rackハンドラーに対応したWebサーババックエンドの上で動かすことができます。使い方はシンプルで、`.ru`ファイルのパスをコマンドに渡すだけです。 6 | 7 | > rackup hello.ru 8 | [2012-11-26 16:56:32] INFO WEBrick 1.3.1 9 | [2012-11-26 16:56:32] INFO ruby 1.9.3 (2012-11-10) [x86_64-darwin11.4.2] 10 | [2012-11-26 16:56:32] INFO WEBrick::HTTPServer#start: pid=41221 port=9292 11 | 12 | カレントディレクトリの`config.ru`という名前のファイルを起動する場合、ファイル名も省略可能です。 13 | 14 | デフォルトで起動するバックエンドは以下の方法で選ばれます _[参考](https://github.com/rack/rack/blob/master/lib/rack/handler.rb)_ 。 15 | 16 | * 環境特有の環境変数、たとえば `REQUEST_METHOD` や `PHP_FCGI_CHILDREN` などが定義されている場合、CGIやFCGIバックエンドが自動で選ばれます 17 | * その他の場合、Thinが利用できる状態であればThinを、そうでない場合はWEBrickを利用します 18 | 19 | コマンドラインスイッチ`-s`か`--server`でバックエンドを指定することもできます。 20 | 21 | > rackup -s puma hello.ru 22 | 23 | rackupコマンドはデフォルトで5つのミドルウェアを有効にします。それらには `Rack::Lint`, `Rack::ShowException`, `Rack::CommonLogger`(* 他にも `Rack::Chunked`, `Rack::ContentLength` を有効にしますが、デバッグ目的としては関係がありません)が含まれ、開発の際にログやスタックトレースを表示してくれて便利ですが、これを無効にするには、`-E`または`--environment`スイッチで`development`以外の値をセットします: 24 | 25 | > rackup -E production -s puma hello.ru 26 | 27 | その他のコマンドラインオプションをサーババックエンドに渡すこともでき、サーバのリッスンするポートは以下のように設定できます: 28 | 29 | > rackup -s puma --host 127.0.0.1 --port 8080 hello.ru 30 | Puma 1.6.3 starting... 31 | * Min threads: 0, max threads: 16 32 | * Environment: development 33 | * Listening on tcp://127.0.0.1:8080 34 | 35 | FCGIバックエンドでUNIXドメインソケットを指定するには: 36 | 37 | > rackup -s fastcgi -OFile=/tmp/fcgi.sock hello.ru 38 | 39 | その他のオプションについては、コマンドラインから`rackup --help`を実行して参照してください。明日もrackupについて解説をつづけます。 40 | -------------------------------------------------------------------------------- /ja/04_reloading_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 4: アプリケーションのリロード 2 | 3 | 昨日はrackupの基本とコマンドラインオプションについて解説しました。今日もつづけましょう! 4 | 5 | ### 必要に応じてアプリケーションをリロードする 6 | 7 | 開発中のコードは`.rb`に書かれたRubyのコードを編集します。rackupによって起動されたサーバは永続プロセスのため、最初に一度だけコンパイルされたRubyコードが何度も実行されます。コードの変更を反映するにはサーバプロセスを再起動する必要があり、面倒です。 8 | 9 | ロード済みのファイルを監視し、変更があったらアプリケーションをリロードするためのRackミドルウェア`Rack::Reloader`が標準で付属しています。以下のように利用できます。 10 | 11 | require 'foo-lib' 12 | require 'foo-lib2' 13 | app = lambda {|env| 14 | # ... your app 15 | } 16 | use Rack::Reloader 17 | run app 18 | 19 | Rubyの組み込み変数`$LOADED_FEATURES`に格納される、あらゆる読み込み済みのファイルの変更を関知して、アクセスのたびにリロードします。plackupのリロードオプションと違い、rackupした本体である`*.ru`ファイルはリロードしない点にはご注意ください。 20 | 21 | また、`Rack::Reloader`は、デフォルトではファイルの最終更新時刻を検知してリロードしますが、その際10秒間だけ変更したファイルをキャッシュします。このキャッシュする秒数は変更可能です。 22 | 23 | use Rack::Reloader, 20 # 秒数を指定 24 | 25 | ### リロードがうまく動かない場合: shotgun 26 | 27 | 永続環境のRubyプロセスでモジュールやアプリケーションを読み直す場合、問題がおこることがあります。 28 | 29 | Rubygemsとして提供される`shotgun`コマンドを利用すると、リクエストごとに子プロセスをforkして、そこでアプリケーションを読み直します。 30 | 31 | shotgunコマンドの利用は簡単です。 32 | 33 | > shotgun myapp.ru 34 | 35 | アプリケーションのコンパイルがランタイムまで遅延され、新しいリクエストが来た際、新しい子プロセスをforkし、Rubyのレスポンスをパイプ上で返します。開発中には変更することのないgemなどはプリロードしておき、レスポンスのボトルネックにならないようにすることも可能です。 36 | 37 | たとえば、アプリケーションがActiveSupportの全機能を使っているなら、 38 | 39 | > shotgun -ractive_support/all myapp.ru 40 | 41 | とすればリクエストごとに読み直すコードが減少するため、スピードアップが期待できます。 42 | -------------------------------------------------------------------------------- /ja/05_run_a_static_web_server_with_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 5: Plackで静的サーバを起動する 2 | 3 | Plackディストリビューションにはすぐに使えるPSGIアプリケーションがPlack::Appネームスペースの下に用意されています。いくつかはとても便利で、一例がここで紹介するPlack::App::FileとPlack::App::Directoryです。 4 | 5 | Plack::App::Fileは`/foo/bar.html`といったリクエストパスを、`/path/to/htdocs/foo/bar.html`といったローカルのファイルにマップし、ファイルを開いてファイルハンドルをPSGIのレスポンスとして返します。lighttpd, nginx やApacheといった既成のWebサーバと同様です。 6 | 7 | Plack::App::DirectoryはPlack::App::Fileのラッパーで、Apacheのmod_autoindexのようなディレクトリインデックスを表示します。 8 | 9 | これらのアプリケーションの利用はとても簡単です。以下のような`.psgi`ファイルを記述します。 10 | 11 | use Plack::App::File; 12 | my $app = Plack::App::File->new(root => "$ENV{HOME}/public_html"); 13 | 14 | これをplackupで起動します。 15 | 16 | > plackup file.psgi 17 | 18 | これで`~/public_html`以下のファイルはURL http://localhost:5000/somefile.html でアクセスできるようになります。 19 | 20 | Plack::App::Directory についても同様ですが、plackupのコマンドラインから直接起動する例を紹介します。 21 | 22 | > plackup -MPlack::App::Directory \ 23 | -e 'Plack::App::Directory->new(root => "$ENV{HOME}/Sites"); 24 | HTTP::Server::PSGI: Accepting connections at http://0:5000/ 25 | 26 | plackupコマンドは、perlコマンド同様、`-I`(インクルードパス), `-M`(ロードするモジュール)や`-e`(実行するコード)を指定できるため、ワンライナーでPSGIアプリを書くことができます。 27 | 28 | 他にもPlack::Appにはいくつかアプリケーションが用意されていますが、それはまた別の日に。 29 | -------------------------------------------------------------------------------- /ja/06_convert_cgi_apps_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 6: CGIアプリケーションをPSGIに変換 2 | 3 | Perlでウェブアプリケーションを書く方法として長い間もっとも人気があったのがCGI, FastCGIとmod_perlでした。CGI.pmはPerlに付属するコアモジュールで、この3つの環境で同時に動くコードを記述することが(少しの変更で)できます。これによって、多くのWebアプリケーションやフレームワークはCGI.pmを使って環境の差異を吸収してきました。 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI) を使うと、既存のCGI.pmベースのアプリケーションをPSGIに簡単に変換できます。以下のようなCGIアプリケーションがあるとします: 6 | 7 | use CGI; 8 | 9 | my $q = CGI->new; 10 | print $q->header('text/plain'), 11 | "Hello ", $q->param('name'); 12 | 13 | とてもシンプルなCGIスクリプトですが、これをPSGIに変換するには以下のようにします: 14 | 15 | use CGI::PSGI; 16 | 17 | my $app = sub { 18 | my $env = shift; 19 | my $q = CGI::PSGI->new($env); 20 | return [ 21 | $q->psgi_header('text/plain'), 22 | [ "Hello ", $q->param('name') ], 23 | ]; 24 | }; 25 | 26 | `CGI::PSGI->new($env)` はPSGIの環境変数ハッシュを受け取り、CGI.pmのサブクラスであるCGI::PSGIのインスタンスをつくります。`param`, `query_string`といったメソッドは今までどおり動作しますが、CGIの環境変数ではなく、PSGI環境変数から値を取得します。 27 | 28 | `psgi_header` はCGIの`header`メソッドのように動作するユーティリティで、ステータスコードとHTTPヘッダの配列リファレンスをリストで返します。 29 | 30 | 明日は既存のCGI.pmを利用したフレームワークをPSGIに変換します。 31 | -------------------------------------------------------------------------------- /ja/07_use_web_application_framework_in_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 7: WebアプリケーションフレームワークをPSGIで利用する 2 | 3 | PlackとPSGIプロジェクトを2009年9月にはじめて以来、Catalyst, Jifty やCGI::Applicationといった人気のあるフレームワークのデベロッパーから多くのフィードバックをもらいました。 4 | 5 | [CGI::Application](http://cgi-app.org/) はもっとも「伝統的」なCGIベースのWebアプリケーションフレームワークで、昨日も紹介したようにCGI.pmを利用して様々な環境の差異を吸収しています。 6 | 7 | CGI::Applicationの現在のメンテナであるMark Stosbergと、CGI::ApplicationにおけるPSGIサポートについて検討してきました。いくつかのアプローチを検討し、その中にはPSGIサポートを直接CGI::Applicationに記述するものも含まれましたが、昨日も紹介したCGI::PSGIをラッパーとして開発し、[CGI::Application::PSGI](http://search.cpan.org/perldoc?CGI::Application::PSGI)を既存のCGI::Applicationを変更することなくPSGI互換モードで起動させるように実装しました。 8 | 9 | CGI::Application::PSGIをCPANからインストールして、`.psgi`ファイルを以下のように記述します。 10 | 11 | use CGI::Application::PSGI; 12 | use WebApp; 13 | 14 | my $app = sub { 15 | my $env = shift; 16 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 17 | CGI::Application::PSGI->run($app); 18 | }; 19 | 20 | そして[plackup](http://advent.plackperl.org/2009/12/day-3-using-plackup.html)を使ってスタンドアロンや各種バックエンドのサーバ上で起動することができます。 21 | 22 | 同様に、多くのWebフレームワークではPSGIサポートをするためのプラグイン、エンジンやアダプターを提供し、PSGIモードで起動するための機能が用意されています。たとえば[Catalyst](http://www.catalystframework.org/)ではCatalyst::EngineというレイヤーでWebサーバエンジンの抽象化をおこなっていて、[Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) でCatalystをPSGI上で起動することができます。(**注**: 2011年にCatalyst 5.8 がリリースされ、PSGIサポートはCatalyst本体に組み込まれており、エンジンを別途インストールする必要はありません) 23 | 24 | 重要なのは、"PSGIサポート"しているフレームワークを利用する場合、利用者のアプリケーションをPSGI用に書き換える必要はないということです。多くの場合、1行のコードの変更も必要ありません。かつ、PSGIを利用することでplackup, Plack::Testやミドルウェアなど多くのエコシステムを利用することができます。これらについては、のちほど紹介していきます。 25 | -------------------------------------------------------------------------------- /ja/08_adapting_web_frameworks_to_psgi.md: -------------------------------------------------------------------------------- 1 | ## Day 8: WebフレームワークをPSGIに対応させる 2 | 3 | Webアプリケーションフレームワーク作者にとって、PSGIの最大のメリットは、一度PSGIに対応すれば、FastCGI、CGIといったWebサーバ固有の環境の差異といった問題に対応する必要がなくなるということです。 4 | 5 | オープンソースあるいはプロプライエタリの大規模なWebアプリケーションを開発している場合、自家製のフレームワークを作っているという場合も多いでしょう。 6 | 7 | 今日のエントリではこうしたフレームワークをどのようにPSGIインタフェースに対応させるかを紹介します。 8 | 9 | ### CGI.pm ベースのフレームワーク 10 | 11 | Day 7ではCGI::ApplicationベースのアプリケーションをCGI::Application::PSGI経由で、PSGIで起動させる方法を紹介しました。CGI::Applicationはその名前からわかるようにCGI.pmを利用していますので、ここをCGI::PSGIにすり替えてしまうのがもっとも手っ取り早い方法です。 12 | 13 | package CGI::Application::PSGI; 14 | use strict; 15 | use CGI::PSGI; 16 | 17 | sub run { 18 | my($class, $app) = @_; 19 | 20 | # HACK: deprecate HTTP header generation 21 | # -- CGI::Application should support some flag to turn this off cleanly 22 | my $body = do { 23 | no warnings 'redefine'; 24 | local *CGI::Application::_send_headers = sub { '' }; 25 | local $ENV{CGI_APP_RETURN_ONLY} = 1; 26 | $app->run; 27 | }; 28 | 29 | my $q = $app->query; 30 | my $type = $app->header_type; 31 | 32 | my @headers = $q->psgi_header($app->header_props); 33 | return [ @headers, [ $body ] ]; 34 | } 35 | 36 | メインとなる実装はたったこれだけです。CGI::Applicationの`run`メソッドは通常、HTTPヘッダとボディを含む全体の出力を文字列で返します。ご覧のとおり、このモジュールはちょっと行儀の悪いハックでHTTPヘッダ生成をオーバーライドして、CGI::PSGIモジュールの`psgi_header`メソッドを使ってPSGIのレスポンスを返しています。 37 | 38 | [Mason](http://search.cpan.org/perldoc?HTML::Mason) や [Maypole](http://search.cpan.org/perldoc?Maypole) 用のPSGIアダプターも実装してみましたが、おおむねコードは同様です。 39 | 40 | * `$env`からCGI::PSGIインスタンスを作り、それをCGI.pmインスタンスの代わりにセットする 41 | * 必要ならHTTPヘッダ出力を抑制 42 | * アプリのメインディスパッチャーを実行 43 | * 送信するHTTPヘッダを抽出、`psgi_header` を使ってステータスとヘッダを生成 44 | * レスポンスボディを抽出 45 | 46 | ### アダプターベースのフレームワーク 47 | 48 | フレームワークがすでにアダプターベースのアプローチでWebサーバ環境の差異を吸収している場合、PSGIサポートを追加するのはさらに簡単になります。CGI用のコードを少し変更するだけですみます。以下のコードは[Squatting](http://search.cpan.org/perldoc?Squatting) をPSGI対応させるためのコードです。SquattingはSquatting::On::* ネームスペースでmod_perl, FastCGIやその他のフレームワーク(Catalyst, HTTP::Engine)などへのアダプターを記述します。[Squatting::On::PSGI](http://search.cpan.org/perldoc?Squatting::On::PSGI) でPSGI対応のコードを書くのはとても簡単でした。 49 | 50 | package Squatting::On::PSGI; 51 | use strict; 52 | use CGI::Cookie; 53 | use Plack::Request; 54 | use Squatting::H; 55 | 56 | my %p; 57 | $p{init_cc} = sub { 58 | my ($c, $env) = @_; 59 | my $cc = $c->clone; 60 | $cc->env = $env; 61 | $cc->cookies = $p{c}->($env->{HTTP_COOKIE} || ''); 62 | $cc->input = $p{i}->($env); 63 | $cc->headers = { 'Content-Type' => 'text/html' }; 64 | $cc->v = { }; 65 | $cc->status = 200; 66 | $cc; 67 | }; 68 | 69 | # \%input = i($env) # Extract CGI parameters from an env object 70 | $p{i} = sub { 71 | my $r = Plack::Request->new($_[0]); 72 | my $p = $r->params; 73 | +{%$p}; 74 | }; 75 | 76 | # \%cookies = $p{c}->($cookie_header) # Parse Cookie header(s). 77 | $p{c} = sub { 78 | +{ map { ref($_) ? $_->value : $_ } CGI::Cookie->parse($_[0]) }; 79 | }; 80 | 81 | sub psgi { 82 | my ($app, $env) = @_; 83 | 84 | $env->{PATH_INFO} ||= "/"; 85 | $env->{REQUEST_PATH} ||= do { 86 | my $script_name = $env->{SCRIPT_NAME}; 87 | $script_name =~ s{/$}{}; 88 | $script_name . $env->{PATH_INFO}; 89 | }; 90 | $env->{REQUEST_URI} ||= do { 91 | ($env->{QUERY_STRING}) 92 | ? "$env->{REQUEST_PATH}?$env->{QUERY_STRING}" 93 | : $env->{REQUEST_PATH}; 94 | }; 95 | 96 | my $res; 97 | eval { 98 | no strict 'refs'; 99 | my ($c, $args) = &{ $app . "::D" }($env->{REQUEST_PATH}); 100 | my $cc = $p{init_cc}->($c, $env); 101 | my $content = $app->service($cc, @$args); 102 | 103 | $res = [ 104 | $cc->status, 105 | [ %{ $cc->{headers} } ], 106 | [ $content ], 107 | ]; 108 | }; 109 | 110 | if ($@) { 111 | $res = [ 500, [ 'Content-Type' => 'text/plain' ], [ "
$@" ] ]; 112 | } 113 | 114 | return $res; 115 | } 116 | 117 | 多少のコード量がありますが、ほとんどは [Squatting::On::CGI](http://cpansearch.perl.org/src/BEPPU/Squatting-0.70/lib/Squatting/On/CGI.pm) と共通で、CGI.pmを利用している箇所をPlack::Requestに置き換えただけの単純なコードです。 118 | 119 | 昨日紹介した[Catalyst::Engine::PSGI](http://search.cpan.org/perldoc?Catalyst::Engine::PSGI) もほとんどがCGI用と共通です。 120 | 121 | ### mod_perl 中心のフレームワーク 122 | 123 | いくつかのフレームワークは mod_perl のAPIを多用して実装されていることがあり、こうした場合はCGI.pmを置き換えるといったアプローチは利用できません。Apache::RequestのAPIをfake/mock objectなどでモックする必要があるでしょう。WebGUIデベロッパーであるPatric Donelanが mod_perl ライクなAPIからPSGIへポートした際の事例を[ブログ記事](http://blog.patspam.com/2009/plack-roundup-at-sf-pm)で紹介しています。実際にリンクされている[モッククラス](http://github.com/pdonelan/webgui/blob/plebgui/lib/WebGUI/Session/Plack.pm) を見てみるのもよいでしょう。 124 | -------------------------------------------------------------------------------- /ja/09_running_cgi_scripts_on_plack.md: -------------------------------------------------------------------------------- 1 | ## Day 9: CGIスクリプトをPlackで走らせる 2 | 3 | 既存のCGIベースのアプリケーションをPSGIに変換する方法を幾つか紹介してきました。今日のエントリでは、すべてのCGIスクリプトを、多くの場合なにも変更せずに、PSGIアプリケーションとして実行する究極の方法を紹介します。 4 | 5 | [CGI::PSGI](http://search.cpan.org/perldoc?CGI::PSGI)はCGI.pmのサブクラスとして実装されていて、CGI.pmからのマイグレーションは、多くの場合数行の変更だけで可能ですが、これは元のコードがある程度エントリポイントが整理されているなどの前提が必要です。レガシーなCGIスクリプトで、いろんな箇所で環境変数を直接参照したり、STDOUTに出力がされていて変更が難しい場合はどうでしょうか。 6 | 7 | [CGI::Emulate::PSGI](http://search.cpan.org/perldoc?CGI::Emulate::PSGI) はCGIベースのPerlプログラムをPSGI環境で実行するモジュールです。CGI::Emulate::PSGIでは環境変数やSTDIN/STODOUTをCGI向けにエミュレートしてから実行するため、上で書いたようなレガシーなCGIスクリプトでSTDOUTにいろいろな箇所で出力をしていても問題ありません。 8 | 9 | use CGI::Emulate::PSGI; 10 | CGI::Emulate::PSGI->handler(sub { 11 | do "/path/to/foo.cgi"; 12 | CGI::initialize_globals() if &CGI::initialize_globals; 13 | }); 14 | 15 | このコードで既存のCGIスクリプトをPSGIとして実行できます。CGI.pm を使っている場合、CGI.pmは多くのグローバル変数にキャッシュをつくるため、`initialize_globals`をリクエストごとに手動で実行する必要があります。 16 | 17 | San FranciscoからLondon Perl Workshopに向かうフライトの途中で、これよりもさらにスマートな方法を思いついてハックしていました。`do`でスクリプトを都度実行するのではなく、CGIスクリプトをサブルーチンにコンパイルしてしまうものです。このモジュールは[CGI::Compile](http://search.cpan.org/perldoc?CGI::Comple) として公開されていて、CGI::Emulate::PSGIと組み合わせて使うと最適です。 18 | 19 | my $sub = CGI::Compile->compile("/path/to/script.cgi"); 20 | my $app = CGI::Emulate::PSGI->handler($sub); 21 | 22 | [Plack::App::CGIBin](http://search.cpan.org/perldoc?Plack::App::CGIBin) がPlackに付属していて、このアプリケーションは`/pat/to/cgi-bin`といったディレクトリにあるCGIスクリプトをそのままPSGIアプリケーションとして起動することができます。 23 | 24 | > plackup -MPlack::App::CGIBin -e 'Plack::App::CGIBin->new(root => "/path/to/cgi-bin"))' 25 | 26 | こうして`/path/to/cgi-bin`にあるCGIスクリプトをマウントします。cgi-binディレクトリにある`foo.pl`は http://localhost:5000/foo.pl でアクセスできます。最初の実行時にコンパイルされるため、mod_perlのApache::Registryと似たような感じで動作します。 27 | -------------------------------------------------------------------------------- /ja/10_using_plack_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 10: Plackミドルウェアの利用 2 | 3 | ### ミドルウェア 4 | 5 | ミドルウェアはPSGIにおけるコンセプト(いつものように、PythonのWSGIやRubyのRackからのパクリです)で、サーバとアプリケーション両側の動作をするコンポーネントです。 6 | 7 |  8 | 9 | (画像は Pylons project) 10 | 11 | この画像はミドルウェアのコンセプトをうまく解説しています。玉ねぎの真ん中にPSGIアプリケーションがあり、それをミドルウェアがラップし、リクエストが来るごとに(外側から内側に)前処理をおこない、レスポンスが出力されたら(内側から外側に)後処理を行います。 12 | 13 | HTTP認証、エラーの補足、JSONPなど数多くの機能をミドルウェアとして実装することによって、PSGIアプリケーションやフレームワークに動的に機能追加していくことが可能になります。 14 | 15 | ### Plack::Middleware 16 | 17 | [Plack::Middleware](http://search.cpan.org/perldoc?Plack::Middleware) はミドルウェアを記述するためのベースクラスで、シンプルにかつ再利用可能な形でミドルウェアを書くことができます。 18 | 19 | Plack::Middlewareで書かれたミドルウェアの利用は簡単で、元のアプリケーションを`wrap`メソッドでラップするだけです。 20 | 21 | my $app = sub { [ 200, ... ] }; 22 | 23 | use Plack::Middleware::StackTrace; 24 | $app = Plack::Middleware::StackTrace->wrap($app); 25 | 26 | この例は元のアプリケーションをStackTraceミドルウェア(実際にはplackupのデフォルトで有効)の`wrap`メソッドでラップします。ラップされたアプリケーションが例外を投げた場合、ミドルウェアがエラーを捕捉して、Devel::StackTrace::AsHTMLを利用して美しいHTMLページを表示します。 27 | 28 | ミドルウェアの中にはパラメータをとるものもあります。その場合、`$app`の後ろにハッシュでパラメータを渡すことができます。: 29 | 30 | my $app = sub { ... }; 31 | 32 | use Plack::Middleware::MethodOverride; 33 | $app = Plack::Middleware::MethodOverride->wrap($app, header => 'X-Method'); 34 | 35 | 多くのミドルウェアをラップするのは、とくにそのミドルウェアを実装したモジュールを先にuseする必要もあるため、退屈になりがちです。この対策として、DSL風の記述法が用意されています。 36 | 37 | use Plack::Builder; 38 | my $app = sub { ... }; 39 | 40 | builder { 41 | enable "StackTrace"; 42 | enable "MethodOverride", header => 'X-Method'; 43 | enable "Deflater"; 44 | $app; 45 | }; 46 | 47 | Plack::Builderの利用方法については明日解説します。 48 | 49 | ### Middleware and Frameworks 50 | 51 | ミドルウェアの美しい点は、どんなPSGIアプリケーションにも適用できるところです。このコード例からは当たり前かもしれませんが、実際にはラップされたアプリケーションはどんなものでも構いません。既存のWebアプリケーションをPSGIモードで起動し、それにPlackミドルウェアを追加することだってできます。例えば、CGI::Applicationで、 52 | 53 | use CGI::Application::PSGI; 54 | use WebApp; 55 | 56 | my $app = sub { 57 | my $env = shift; 58 | my $app = WebApp->new({ QUERY => CGI::PSGI->new($env) }); 59 | CGI::Application::PSGI->run($app); 60 | }; 61 | 62 | use Plack::Builder; 63 | builder { 64 | enable "Auth::Basic", authenticator => sub { $_[1] eq 'foobar' }; 65 | $app; 66 | }; 67 | 68 | このようにすると、CGI::ApplicationベースのアプリケーションにBasic認証をつけることができます。[PSGIをサポートしているフレームワーク](http://plackperl.org/#frameworks)であればなんでもこのようなことが可能です。 69 | -------------------------------------------------------------------------------- /ja/11_using_plack_builder.md: -------------------------------------------------------------------------------- 1 | ## Day 11: Plack::Builderを使う 2 | 3 | 昨日のエントリではPlackミドルウェアを.psgiで利用する方法を紹介しました。ミドルウェアを`use`して、そのあと`$app`を`wrap`メソッドでラップしていくのは退屈ですし、直感的ではありません。そこで、それをより簡単にするDSL (Domain Specific Language) 風シンタックスを用意しています。それが、Plack::Builderです。 4 | 5 | Plack::Builderのりようはとても簡単です。`builder`と`enable`キーワードを使います。 6 | 7 | my $app = sub { 8 | return [ 200, [], [ "Hello World" ] ]; 9 | }; 10 | 11 | use Plack::Builder; 12 | builder { 13 | enable "JSONP"; 14 | enable "Auth::Basic", authenticator => sub { ... }; 15 | enable "Deflater"; 16 | $app; 17 | }; 18 | 19 | このコードは元のアプリケーション`$app`に対して、Deflater, Auth::Basic と JSONP ミドルウェアを内側から外側に向けてラップしていきます。つまり、以下のコードと同様です。 20 | 21 | $app = Plack::Middleware::Deflater->wrap($app); 22 | $app = Plack::Middleware::Auth::Basic->wrap($app, authenticator => sub { }); 23 | $app = Plack::Middleware::JSONP->wrap($app); 24 | 25 | ただし、各モジュールを先に`use`する必要がないので、よりDRYになっています。 26 | 27 | ### 外側から内側へ、上から下へ 28 | 29 | ラップされるミドルウェアの順番が逆であることにきづいたでしょうか?builder/enableのDSLでは、ラップされる`$app`に近い行が、*内側*で、最初の行が*外側*にくるようにラップされます。昨日紹介した玉ねぎの図と比較するとよりわかりやすくなります。アプリケーションに近い行ほど、レイヤーの内側になるということです。 30 | 31 | `enable`を使うばあいPlack::Middleware::をミドルウェアの名前から省略できます。Plack::Middleware以外の名前空間を使う場合、たとえば MyFramework::PSGI::MW::Foo であれば、 32 | 33 | enable "+MyFramework::PSGI::MW::Foo"; 34 | 35 | とすることができます。重要なのはプラス(+)でモジュール名がFully Qualified であることを指定できます。 36 | 37 | ### 裏でおこっていること 38 | 39 | もしPlack::Builderの実装に興味があれば、コードを見て何をしているか追ってみてください。`builder`はコードブロックを受け取り、それを実行した結果のコードリファレンスを元のアプリケーション(`$app`)として受け取り、そしてenableされたミドルウェアを逆順にラップしていきます。つまり、`builder`ブロックの最後に`$app`またはPSGIアプリケーションを配置することが重要で、また`builder`ブロックは.psgiファイルの最後になければなりません。 40 | 41 | ### Thanks, Rack 42 | 43 | Plack::BuilderはRubyのRack::Builderにインスパイアされています。Rack::Builderでは`use`キーワードを使っていますが、Perlではこれは使えないため、`enable`で代用しています:) Rackには`map`キーワードでアプリケーションをパスにマップする機能がありますが、Plackでこれをどうするかは明日解説します。 44 | -------------------------------------------------------------------------------- /ja/12_maps_multiple_apps_with_mount_and_urlmap.md: -------------------------------------------------------------------------------- 1 | ## Day 12: 複数のアプリケーションをmountとURLMapでマウントする 2 | 3 | ### Hello World! but anyone else? 4 | 5 | 本書では、シンプルなアプリケーションの例として "Hello World" を使っていました。 6 | 7 | my $app = sub { 8 | return [ 200, [], [ "Hello World" ] ]; 9 | }; 10 | 11 | より複雑な例として、Webアプリケーションフレームワークを使って書かれた複数のアプリケーションを、1つのサーバ内でmod_aliasなどでマップしているような例を考えてみましょう。 12 | 13 | ### Plack::App::URLMap 14 | 15 | Plack::App::URLMap は複数のPSGIアプリケーションを*合成*して1つのPSGIアプリケーションのように振る舞います。リクエストパスやヴァーチャルホストのようにホスト名ベースでアプリケーションをディスパッチすることができます。 16 | 17 | my $app1 = sub { 18 | return [ 200, [], [ "Hello John" ] ]; 19 | }; 20 | 21 | my $app2 = sub { 22 | return [ 200, [], [ "Hello Bob" ] ]; 23 | }; 24 | 25 | このように2つのアプリがあり、1つはJohnもう1つはBobにHelloを返します。この2つのPSGIアプリを1つのサーバで動作させたい場合、どうすればよいでしょう。Plack::App::URLMapを利用すると以下のように出来ます。 26 | 27 | use Plack::App::URLMap; 28 | my $app = Plack::App::URLMap->new; 29 | $app->mount("/john" => $app1); 30 | $app->mount("/bob" => $app2); 31 | 32 | たったこれだけです。リクエストパスに応じて、`/john`には`$app1`つまり"Hello John", `/bob`には`$app2`つまり"Hello Bob"へディスパッチします。またマップされいていないパス、たとえばルートの "/" などは404が返ります。 33 | 34 | `PATH_INFO`や`SCRIPT_NAME`といったPSGI環境変数は自動的に調整され、Apacheのmod_aliasやCGIスクリプトを起動したときのように、そのまま動きます。アプリケーションやフレームワークは、`PATH_INFO`を使ってリクエストを処理し、`SCRIPT_NAME`をベースパスとしてURLを生成する必要があります。 35 | 36 | ### mount DSL 37 | 38 | Plack::App::URLMapの`mount`はとても便利なので、Plack::BuilderのDSLにも追加してあります。Rack::Builderでは`map`を使っていますが、これもPerlでは使えないので、`mount`として使います。 39 | 40 | use Plack::Builder; 41 | builder { 42 | mount "/john" => $app1; 43 | mount "/bob" => builder { 44 | enable "Auth::Basic", authenticator => ...; 45 | $app2; 46 | }; 47 | }; 48 | 49 | '/john' へのリクエストはURLMapのときと同様、`$app1`にディスパッチされます。この例では"/bob"に対して`builder`をネストさせ、"Hello Bob"を表示するアプリケーションにBasic認証を追加しています。この例は以下のコードと同様です。 50 | 51 | $app = Plack::App::URLMap->new; 52 | $app->mount("/john", $app1); 53 | 54 | $app2 = Plack::Middleware::Auth::Basic->wrap($app2, authenticator => ...); 55 | $app->mount("/bob", $app2); 56 | 57 | が、DSLを利用した方がより短いコードで、簡潔になっています。 58 | 59 | ### マルチテナントフレームワーク 60 | 61 | もちろん、このURLMapやmount APIをつかって複数のフレームワークのアプリケーションを1つのサーバにマウントすることができます。3つのアプリケーションがあって、"Foo"がCatalyst, "Bar"がCGI::Application, "Baz"がSquattingで書かれているとしましょう。 62 | 63 | # Catalyst 64 | use Foo; 65 | my $app1 = Foo->psgi_app; 66 | 67 | # CGI::Application 68 | use Bar; 69 | use CGI::Application::PSGI; 70 | my $app2 = sub { 71 | my $app = Bar->new({ QUERY => CGI::PSGI->new(shift) }); 72 | CGI::Application::PSGI->run($app); 73 | }; 74 | 75 | # Squatting 76 | use Baz 'On::PSGI'; 77 | Baz->init; 78 | my $app3 = sub { Baz->psgi(shift) }; 79 | 80 | builder { 81 | mount "/foo" => $app1; 82 | mount "/bar" => $app2; 83 | mount "/baz" => $app3; 84 | }; 85 | 86 | こうすると、別々のフレームワークで書かれた3つのアプリケーションが、plackupなどを利用して同一サーバ上で異なるパスにマップされて起動します。 87 | -------------------------------------------------------------------------------- /ja/13_use_plack_test_to_test_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 13: Plack::Test でアプリケーションをテストする 2 | 3 | ### Testing 4 | 5 | Webアプリケーションのテストにはいろいろな方法があり、ライブのサーバを使ったり、モックリクエストを使う方法などがあります。こうしたテスト手法を提供しているフレームワークもありますが、どのようにテストを記述するかはフレームワークごとに異なっていることが多いです。 6 | 7 | Plack::TestはどんなPSGI対応のWebアプリケーションフレームワークでも、共通のインターフェースを使って、それぞれモックとライブのサーバを使ったテスト手法を導入できます。 8 | 9 | ### Plack::Testを利用 10 | 11 | Plack::Testのりようはとても簡単で、Perlのテストプロトコル標準である[TAP](http://testanything.org/wiki/) や[Test::More](http://search.cpan.org/perloc?Test::More)と互換性があります。 12 | 13 | use Plack::Test; 14 | use Test::More; 15 | use HTTP::Request; 16 | 17 | my $app = sub { 18 | return [ 200, [ 'Content-Type', 'text/plain' ], [ "Hello" ] ]; 19 | }; 20 | 21 | test_psgi $app, sub { 22 | my $cb = shift; 23 | 24 | my $req = HTTP::Request->new(GET => 'http://localhost/'); 25 | my $res = $cb->($req); 26 | 27 | is $res->code, 200; 28 | is $res->content, "Hello"; 29 | }; 30 | 31 | done_testing; 32 | 33 | PSGIアプリを作成またはロード([Plack::Util](http://search.cpan.org/perldoc?Plack::Util)の `load_psgi` 関数をつかって`.psgi`ファイルからアプリケーションをロードできます)し、`test_psgi`関数でアプリケーションをテストします。2個目の引数はコールバックで、テスト用クライアントコードを記述します。 34 | 35 | 名前付き引数をつかって、以下のようにも書けます: 36 | 37 | test_psgi app => $app, client => sub { ... } 38 | 39 | クライアントコードはコールバック `$cb` を受け取り、これに対して HTTP::Request オブジェクトを渡すと HTTP::Response オブジェクトを返します。1つのクライアントコード内で複数のリクエストを投げて、リクエストやレスポンスのテストを記述できます。 40 | 41 | このファイルを`.t`で保存し、`prove`などでテストを実行します。 42 | 43 | ### HTTP::Request::Common 44 | 45 | これは必須ではありませんが、[HTTP::Request::Common](http://search.cpan.org/perldoc?HTTP::Request::Common) を利用してHTTPリクエストを作成することをおすすめします。コードがより簡潔になります。 46 | 47 | use HTTP::Request::Common; 48 | 49 | test_psgi $app, sub { 50 | my $cb = shift; 51 | my $res = $cb->(GET "/"); 52 | # ... 53 | }; 54 | 55 | スキームやホスト名は省略可能で、http://localhost/ (ライブ・テストの場合ポート番号は自動補完されます) になります。 56 | 57 | ### ライブ/モックモード 58 | 59 | デフォルトでは`test_psgi`のコールバックはモックHTTPリクエストモードで実行され、受け取ったHTTP::RequestオブジェクトをPSGI環境変数ハッシュに変換し、PSGIアプリケーションを実行し、レスポンスをHTTP::Responseに変換します。 60 | 61 | これをライブHTTPモードに変更して実行するには、a) パッケージ変数`$Plack::Test::Impl` または b) 環境変数 `PLACK_TEST_IMPL` を `Server` に設定します。 62 | 63 | use Plack::Test; 64 | $Plack::Test::Impl = "Server"; 65 | 66 | test_psgi ... # the same code 67 | 68 | 環境変数を使えば、`.t`コードを変更する必要がありません。 69 | 70 | env PLACK_TEST_IMPL=Server prove -l t/test.t 71 | 72 | Serverモードでは、PSGIアプリケーションをスタンドアロンサーバで記述し、LWP::UserAgent を利用してHTTPリクエストを送信します。テスト内のクライアントコードを変更する必要はありませんし、ホスト名やポート番号はテスト環境によって自動で設定されます。 73 | 74 | ### フレームワークをPlack::Testでテスト 75 | 76 | 繰り返しになりますが、PSGIとPlackの素晴らしいところは、PSGIをターゲットにして書かれたアプリケーションフレームワークであればなんでも利用が可能であるということです。Plack::Testも同様に、PSGIに対応したWebアプリケーションフレームワークをテストすることができます。 77 | 78 | use Plack::Test; 79 | use MyCatalystApp; 80 | 81 | my $app = MyCatalystApp->psgi_app; 82 | 83 | test_psgi $app, sub { 84 | my $cb = shift; 85 | # ... 86 | }; 87 | done_testing; 88 | 89 | -------------------------------------------------------------------------------- /ja/14_use_plack_request.md: -------------------------------------------------------------------------------- 1 | ## Day 14: Plack::Requestを利用する 2 | 3 | Plack自身はWebフレームワークではありません。むしろ、PSGIサーバとミドルウェアの実装にplackup, Plack::Testなどのユーティリティが入ったツールキットのようなものです。 4 | 5 | Plackプロジェクトは[HTTP::Engine](http://search.cpan.org/perldoc?HTTP::Engine)プロジェクトから派生した側面もあり、リクエスト・レスポンススタイルのAPIを使ってウェブアプリケーションを開発する需要はあるようです。Plack::Requestは、PSGI環境変数やレスポンス配列に対して、簡単なオブジェクト指向APIを提供します。新しいミドルウェアを記述する際のライブラリとしても利用できますし、PlackをベースにしたWebフレームワークを記述する差異のリクエスト/レスポンスのベースクラスとしても使えます。 6 | 7 | ### Plack::Request と Response を使う 8 | 9 | Plack::Request はPSGI環境変数へのラッパーであり、コードは以下のようになります。 10 | 11 | use Plack::Request; 12 | 13 | my $app = sub { 14 | my $req = Plack::Request->new(shift); 15 | 16 | my $name = $req->param('name'); 17 | my $res = $req->new_response(200); 18 | $res->content_type('text/html'); 19 | $res->content("Hello World"); 20 | 21 | return $res->finalize; 22 | }; 23 | 24 | HTTP::Engineからマイグレートする場合、変更する箇所は`shift`で取得したPSGI環境変数をPlack::Requestに渡し、最後に`finalize`を読んでPSGIレスポンスを取得することだけです。 25 | 26 | その他、`path_info`, `uri`, `param` などはHTTP::Engine::RequestやResponseとほぼ同等に動作します。 27 | 28 | ### Plack::Request と Plack 29 | 30 | Plack::Request はPlackディストリビューションに同梱されCPANから入手可能です。Plack::Requestをフレームワークで利用した場合、Plack標準のサーバ以外でも、PSGIに対応したサーバ実装であれば、どのサーバでも動作させることができます。 31 | 32 | ### Plack::Request を使うか否か 33 | 34 | Plack::Requestを利用したコードを`.psgi`に直接記述するのは、簡単なプロトタイピングやテストには便利ですが、ある程度の規模アプリケーション開発にはおすすめしません。1000行のコードをCGIスクリプトに直接書くようなもので、多くの場合はモジュールに分割していくのが正しい方法です。PSGIでも同様で、アプリケーションをクラスにまとめ、リクエストクラスとしてPlack::Requestをサブクラスするなどして、`.psgi`ファイルにはPlack::BuilderのDSLでミドルウェアを設定するようなコードとエントリポイントだけが含まれるようになるはずです。 35 | 36 | Plack::Request を利用して既存のフレームワークをPSGIインタフェースに対応させるためのライブラリとして使うのもよいでしょう。 37 | -------------------------------------------------------------------------------- /ja/15_authenticate_your_app_with_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 15: ミドルウェアでアプリケーションの認証 2 | 3 | Plackのミドルウェアは数多くリリースされていて、Plackに含まれているものや、CPANに単独でリリースされているものもあります。このAdvent Calendarをアップデートしている最中にも、多くのデベロッパーがミドルウェアを開発してCPANにアップロードしていました。 4 | 5 | 今日から、PSGI対応のアプリケーションにすぐ適用可能な、いくつかのおすすめミドルウェアを紹介します。 6 | 7 | ### Basic認証 8 | 9 | Plackミドルウェアはアプリケーションをラップするため、真価を発揮するのはHTTPレイヤでの前処理、後処理です。今日紹介するのはBasic認証を行うミドルウェアです。 10 | 11 | アプリケーションへのBasic認証の追加はいくつかの方法があります。フレームワークが対応していれば、提供されている機能で可能でしょう。例えばCatalystではCatalyst::Authentication::Credential::HTTPで対応がされています。多くのCatalyst拡張と同様、認証方法やユーザのストレージなど、様々な設定が可能になっています。 12 | 13 | また、認証をウェブサーバレイヤーで行うこともできます。たとえば、Apacheとmod_perlでアプリケーションを動かしている場合、Apacheデフォルトのmod_authモジュールで認証を追加するのはとても簡単ですが、「ユーザをどのように認証するか」の設定は、カスタムのApacheモジュールを書くなどしない限り、限界があります。 14 | 15 | Plackミドルウェアでは、Webアプリケーションフレームワークがこうした機能を共有し、多くの場合シンプルなPerlコールバックで拡張することが可能です。Plack::Middleware::Auth::BasicはBasic認証に対してこうしたインターフェースを提供します。 16 | 17 | ### Plack::Middleware::Auth::Basic 18 | 19 | その他のミドルウェアと同様、Auth::Basicミドルウェアの利用はとても簡単です。 20 | 21 | use Plack::Builder; 22 | 23 | my $app = sub { ... }; 24 | 25 | builder { 26 | enable "Auth::Basic", authenticator => sub { 27 | my($username, $password) = @_; 28 | return $username eq 'admin' && $password eq 'foobar'; 29 | }; 30 | $app; 31 | }; 32 | 33 | 34 | このコードでアプリケーション`$app`にBaisic認証機能が提供されます。ユーザ名*admin*がパスワード*foobar*でサインインすることができます。認証に成功したユーザはPSGI環境変数`REMOTE_USER`にセットされ、アプリケーションから利用したりAccessLogミドルウェアからログに追加されます。 35 | 36 | コールバックベースの設定になるため、Kerberosのような認証システムと連携するのはAuthen::Simpleモジュールをつかうと簡単にできます。 37 | 38 | use Plack::Builder; 39 | use Authen::Simple; 40 | use Authen::Simple::Kerberos; 41 | 42 | my $auth = Authen::Simple->new( 43 | Authen::Simple::Kerberos->new(realm => ...), 44 | ); 45 | 46 | builder { 47 | enable "Auth::Basic", authenticator => sub { 48 | $auth->authenticate(@_): 49 | }; 50 | $app; 51 | }; 52 | 53 | 同様に [Authen::Simpleバックエンド](http://search.cpan.org/search?query=authen+simple&mode=all) を使ってLDAPなどと連携することも可能です。 54 | 55 | ### URLMap 56 | 57 | URLMap は複数のアプリケーションを1つのアプリケーションに合成することができます。Authミドルウェアと組み合わせると、同一のアプリを認証モードと非認証モードで走らせることもできます。 58 | 59 | use Plack::Builder; 60 | my $app = sub { 61 | my $env = shift; 62 | if ($env->{REMOTE_USER}) { 63 | # Authenticated 64 | } else { 65 | # Unauthenticated 66 | } 67 | }; 68 | 69 | builder { 70 | mount "/private" => builder { 71 | enable "Auth::Basic", authenticator => ...; 72 | $app; 73 | }; 74 | mount "/public" => $app; 75 | }; 76 | 77 | このようにして同一の`$app`を/publicと/privateにマップし、/privateではBasic認証を必須とします。アプリケーションでは`$env->{REMOTE_USER}`をチェックすることで認証ずみアクセスかどうか判別します。 78 | -------------------------------------------------------------------------------- /ja/16_adding_jsonp_support_to_your_app.md: -------------------------------------------------------------------------------- 1 | ## Day 16: アプリケーションにJSONPサポートを追加する 2 | 3 | 今日はとてもシンプルですが便利な例として、HTTPのベーシック機能だけではないミドルウェアを紹介します。 4 | 5 | ### JSONP 6 | 7 | [JSONP](http://ajaxian.com/archives/jsonp-json-with-padding) (JSON-Padding) はJSONをJavaScriptのコールバック関数にラップするテクノロジーの名前です。JSONベースのコンテンツをサードパーティサイトから`script`タグでクロスドメイン読み込みさせるために利用されています。 8 | 9 | ### Middleware::JSONP 10 | 11 | JSONでエンコードされたデータを`application/json`コンテンツ・タイプで返すアプリケーションがあったとします。簡単なインラインPSGIアプリケーションでは以下のようになります。 12 | 13 | use JSON; 14 | my $app = sub { 15 | my $env = shift; 16 | if ($env->{PATH_INFO} eq '/whatever.json') { 17 | my $body = JSON::encode_json({ 18 | hello => 'world', 19 | }); 20 | return [ 200, ['Content-Type', 'application/json'], [ $body ] ]; 21 | } 22 | return [ 404, ['Content-Type', 'text/html'], ['Not Found']]; 23 | }; 24 | 25 | これにJSONPサポートを追加するには、Middleware::JSONPを利用します: 26 | 27 | use Plack::Builder; 28 | builder { 29 | enable "JSONP"; 30 | $app; 31 | }; 32 | 33 | たった1行だけです!このミドルウェアは、レスポンスのcontent-typeが`application/json`であるかチェックし、かつ`callback`パラメータがURLにあるかどうかチェックします。"/whatever.json"といったリクエストはそのままJSONとして返されますが、"/whatever.json?callback=myCallback"のようなリクエストには、 34 | 35 | myCallback({"hello":"world"}); 36 | 37 | というデータが Content-Type `text/javascript` で返され、 Content-Length は自動で調整されます(すでに設定されていた場合)。 38 | 39 | ### フレームワーク 40 | 41 | JSONに加えてJSONPをサポートするのは、多くのフレームワークでは簡単なことですが、Middleware::JSONP はPSGI/Plackのレイヤで共通動作させるものを簡単につくれるよい例でしょう。 42 | 43 | もちろん、JSONPミドルウェアはJSONを出力するフレームワークであればどんなものでも適用することができます。Catalystであれば、 44 | 45 | package MyApp::View::JSON; 46 | use base qw( Catalyst::View::JSON ); 47 | 48 | package MyApp::Controller::Foo; 49 | sub hello : Local { 50 | my($self, $c) = @_; 51 | $c->stash->{message} = 'Hello World!'; 52 | $c->forward('MyApp::View::JSON'); 53 | } 54 | 55 | これに、Plack::BuilderでJSONPサポートを追加します。 56 | 57 | use MyApp; 58 | my $app = MyApp->psgi_app; 59 | 60 | use Plack::Builder; 61 | builder { 62 | enable "JSONP"; 63 | $app; 64 | }; 65 | 66 | JSONを出力する[Catalyst::View::JSON](http://search.cpan.org/perldoc?Catalyst::View::JSON) は私が書いたもので、JSONPコールバックはネイティブでサポートされていますが、やり方は1つではありません! 67 | -------------------------------------------------------------------------------- /ja/17_serving_static_files_from_your_application.md: -------------------------------------------------------------------------------- 1 | ## Day 17: 静的ファイルを配信する 2 | 3 | Day 5 ではplackupでカレントディレクトリからファイルを配信する方法を紹介しました。ミドルウェアの利用方法や、URLMapをつかったアプリケーションの合成方法を学んだので、Webアプリケーション開発に必須の機能を追加するのもとても簡単です。静的ファイルの配信です。 4 | 5 | ### あるパスから静的ファイルを配信する 6 | 7 | ほとんどのフレームワークが静的ファイルを配信する機能を備えています。PSGIに対応したフレームワークであれば、この機能をつける必要はもうありません。Staticミドルウェアを利用します。 8 | 9 | use Plack::Builder; 10 | 11 | my $app = sub { ... }; 12 | 13 | builder { 14 | enable "Static", path => qr!^/static!, root => './htdocs'; 15 | $app; 16 | } 17 | 18 | /staticではじまるリクエストにマッチすると、そのパスを"htdocs"にマップします。つまり、"/static/images/foo.jpg" は "./htdocs/static/images/foo.jpg" のファイルをレスポンスとして返します。 19 | 20 | 多くの場合、ディレクトリ名を変更したり、ローカルのパス名とオーバーラップしている場合があります。たとえば、/static/index.css へのリクエストを "./static-files/index.css" にマッピングするといった具合です。以下のようにします。 21 | 22 | builder { 23 | enable "Static", path => sub { s!^/static/!! }, root => './static-files'; 24 | $app; 25 | } 26 | 27 | 重要なのは、pathに正規表現 (`qr`) ではなく、コールバックを利用し、`sub { s/// }`で文字列を置換しています。コールバックはリクエストパスに対して実行され、その値は `$_` に保存されています。この例では、リクエストパスが "/static/" で始まるかテスト、その場合パスから削除し、残りのパスを "./static-files/" 以下に追加しています。 28 | 29 | 結果として、"/static/foo.jpg" は "./static-files/foo.jpg" となります。このパターンマッチに失敗したリクエストはそのまま元の `$app`にパススルーされます。 30 | 31 | ### URLMap と App::File でDYI 32 | 33 | Perlですから、やり方は一つではありません。Day 12で紹介したmountやURLMapの使い方を覚えていれば、App::Fileと`mount`を使う方法はより直感的にかけます。前の例は、以下のように書けます。 34 | 35 | use Plack::Builder; 36 | 37 | builder { 38 | mount "/static" => Plack::App::File->new(root => "./static-files"); 39 | mount "/" => $app; 40 | }; 41 | 42 | これをどう見るかは個人の主観でしょうが、個人的にはこちらのほうが簡潔だと思います。Staticミドルウェアのコールバックでは、リクエストパスのマッチングや置換がより柔軟に行えるため、こちらを使う方がよい場合もあるでしょう。どちらでも好きな方法を使ってください。 43 | -------------------------------------------------------------------------------- /ja/18_load_middleware_conditionally.md: -------------------------------------------------------------------------------- 1 | ## Day 18: ミドルウェアを条件ロードする 2 | 3 | いくつかのミドルウェアを紹介しました。便利なので全体に有効にできるものもあれば、ある条件下でのみ有効にしたいものもあるでしょう。どうやってそれを実現するかを解説します。 4 | 5 | ### ミドルウェアを条件分岐でロードする 6 | 7 | Conditional ミドルウェアはメタミドルウェアで、1つのミドルウェアを受け取り、それをランタイムの条件によって適用させるか決定するミドルウェアを返します。例をとってみましょう。 8 | 9 | * JSONP ミドルウェアをパスが /public で始まるときだけ有効にしたい 10 | * Basic認証をローカルIPからのリクエストについては無効にしたい 11 | 12 | WSGIやRackでこうした問題をどのように対処するかを調査していましたが、きれいな方法は見つかりませんでした。各ミドルウェアで有効にする条件を設定するのは、あまりいい方法だとは思えませんでした。 13 | 14 | ### Middleware::Conditional 15 | 16 | Conditional ミドルウェアはこうした問題を柔軟に解決します。 17 | 18 | use Plack::Builder; 19 | 20 | builder { 21 | enable_if { $_[0]->{REMOTE_ADDR} !~ /^192\.168\.0\./ } 22 | "Auth::Basic", authenticator => ...; 23 | $app; 24 | }; 25 | 26 | Plack::Builder に新しいキーワード `enable_if` が追加されています。ブロックを受け取り、リクエスト時に評価され(`$_[0]`がPSGI環境変数ハッシュ) ブロックがtrueを返した場合、ミドルウェアでラップされたアプリケーションを、そうでない場合はなにもせずにパススルーします。 27 | 28 | この例ではリクエストがローカルネットワークから来ているかチェックし、そうでない場合にBasic認証ミドルウェアを適用しています。 29 | 30 | Conditional は普通のミドルウェアとして実装sれているので、内部的には以下のコードと同等です: 31 | 32 | use Plack::Middleware::Conditional; 33 | use Plack::Middleware::Auth::Basic; 34 | 35 | my $app = sub { ... }; 36 | 37 | $app = Plack::Middleware::Conditional->wrap($app, 38 | builder => sub { 39 | Plack::Middleware::Auth::Basic->wrap( 40 | $_[0], authenticator => ..., 41 | ); 42 | }, 43 | condition => sub { 44 | my $env = shift; 45 | $env->{REMOTE_ADDR} !~ /^192\.168\.0\./; 46 | }, 47 | ); 48 | 49 | ですがこれを毎回書くのは退屈ですので、DSL版をおすすめします :) 50 | -------------------------------------------------------------------------------- /ja/19_cascade_multiple_applications.md: -------------------------------------------------------------------------------- 1 | ## Day 19: 複数のアプリケーションをカスケードする 2 | 3 | Conditionalミドルウェア(Day 18)とURLMapアプリケーション(Day 12)には共通点があります。これら自体がPSGIアプリケーションですが、既存のPSGIアプリケーションやミドルウェアを受け取ってそれらにディスパッチするものです。これがPSGIのアプリケーションやミドルウェアの美しい点で、今日紹介するのもその一例です。 4 | 5 | ### 複数のアプリケーションをカスケードする 6 | 7 | 複数のアプリケーションを順番に実行し、成功が返ってくるまでカスケードするのは便利なことがあります。デザインパターンではChain of responsibilityと呼ばれ、mod_perlハンドラなどのWebアプリケーションでも応用されています。 8 | 9 | ### Cascade Application 10 | 11 | Plack::App::Cascade は複数のPSGIアプリケーションを合成し、順番に実行して404以外のレスポンスが返ってくるまでトライします。 12 | 13 | use Plack::App::Cascade; 14 | use Plack::App::File; 15 | use Plack::App::URLMap; 16 | 17 | my @paths = qw( 18 | /home/www/static 19 | /virtualhost/example.com/htdocs/static 20 | /users/miyagawa/public_html/images 21 | ); 22 | 23 | my $app = Plack::App::Cascade->new; 24 | for my $path (@paths) { 25 | my $file = Plack::App::File->new(root => $path); 26 | $app->add($file); 27 | } 28 | 29 | my $map = Plack::App::URLMap->new; 30 | $map->mount("/static" => $app); 31 | $map->to_app; 32 | 33 | このアプリケーションを URLMap を使って /static にマップしています。すべてのリクエストは`@paths`に設定された3つのディレクトリをApp::Fileで順番にトライし、ファイルが見つかったらそれをレスポンスとして返します。スタティックファイルを複数のディレクトリからカスケードして返すのに便利です。 34 | 35 | ### アプリケーションをカスケード 36 | 37 | use CatalystApp; 38 | my $app1 = CatalystApp->psgi_app; 39 | 40 | use CGI::Application::PSGI; 41 | use CGIApp; 42 | my $app2 = sub { 43 | my $app = CGIApp->new({ 44 | QUERY => CGI::PSGI->new($_[0]), 45 | }); 46 | CGI::Application::PSGI->run($app); 47 | }; 48 | 49 | use Plack::App::Cascade; 50 | Plack::App::Cascade->new(apps => [ $app1, $app2 ])->to_app; 51 | 52 | この例では2つのアプリケーション、1つはCatalyst もう1つはCGI::Applicationを用意し、順番に実行します。/what/ever.cat をCatalystアプリケーション、/what/ever.cgiapp をCGI::Application側で処理する、といった際にこのようにカスケードさせることができます。 53 | 54 | とはいえ、クレイジーなアイデアのように聞こえるかもしれません。実際にはURLMapで違うパスにmountするほうが現実的でしょうが、こういうこともできるということで :) 55 | -------------------------------------------------------------------------------- /ja/20_access_your_local_app_from_the_internet.md: -------------------------------------------------------------------------------- 1 | ## Day 20: ローカルアプリケーションにインターネットからアクセスする 2 | 3 | (**注意**: reverseHTTP サービスは2012年現在停止しています) 4 | 5 | 最近では、ノートPC上でアプリケーションを開発し、ローカルのIPアドレスを使ってテストをするのが簡単です。こうして開発してローカルで動いているアプリケーションに対して、リモートで仕事をしている同僚や、webhookなどのテストをするために、インターネットからアクセスしたいことがあります。 6 | 7 | ### Reverse HTTP 8 | 9 | この問題を解決するにはいくつかのソリューションがありますが、一つ面白いやりかたが[ReverseHTTP](http://www.reversehttp.net/)を使った方法です。ReverseHTTPはクライアント・サーバ・ゲートウェイ間のシンプルなプロトコルで、HTTP/1.1の拡張を用いています。便利なことに、reversehttp.netでデモゲートウェイが動作しているため、自分でサーバを立てたりすることなく、デモに利用することができます。 10 | 11 | これが実際どのように動作するのか興味が有る人は、[仕様書](http://www.reversehttp.net/specs.html)に目を通してみるとよいでしょう。*Reverse* HTTP という名前の通り、動作させるアプリケーション・サーバがロングポールのHTTPクライアントになり、ゲートウェイ・サーバがインターネット経由でアクセスされたリクエストをレスポンスとして返します。 12 | 13 | ### Plack::Server::ReverseHTTP 14 | 15 | [Plack::Server::ReverseHTTP](http://search.cpan.org/~miyagawa/Plack-Server-ReverseHTTP-0.01/) はPlackのバックエンドサーバ実装で、ReverseHTTPプロトコル上でPSGIアプリケーションを実行し、外部インターネットから、ローカルで動作しているPSGIアプリケーションへのアクセスを可能にします。 16 | 17 | ReverseHTTPを利用するには、必要なモジュールをCPANからインストールし、以下のコマンドを実行します。 18 | 19 | > plackup -s ReverseHTTP -o yourhostname --token password \ 20 | -e 'sub { [200, ["Content-Type","text/plain"], ["Hello"]] }' 21 | Public Application URL: http://yourhostname.www.reversehttp.net/ 22 | 23 | `-o` は`--host`のエイリアスで、利用するサブドメイン(ラベル)を指定します。`--token`は登録したラベルを使うためのパスワードとして利用します。指定しないこともできますが、この場合、登録したラベルを後から誰でも利用可能になります。 24 | 25 | コンソールに出力されたアドレス(URL)をブラウザで開くと、Helloが表示されます。 26 | 27 | ### フレームワークからの利用 28 | 29 | もちろんPSGIサーバのバックエンドですから、どんなフレームワークでも利用することができます。Catalystアプリに対して実行するには、 30 | 31 | > catalyst.pl MyApp 32 | > cd MyApp 33 | > plackup -o yourhost --token password myapp.psgi 34 | 35 | たったこれだけです。 デフォルトのCatalystアプリケーションが http://yourhost.reversehttp.net/ というURLで、インターネット経由でどこからでもアクセス可能です。 36 | 37 | ### 注意 38 | 39 | ReverseHTTP.netのゲートウェイサービスは実験的なものであり、SLAのような保証はありません。プロダクション環境などでの利用は避けたほうが良いでしょう。ちょっとしたアプリを友達に見せるときなどに、SSHやVPNトンネリングなどを必要とせず利用できるのは便利ですね。 40 | -------------------------------------------------------------------------------- /ja/21_lint_your_application_and_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 21: アプリケーションとミドルウェアのLintチェック 2 | 3 | 既存のWebフレームワークをPSGIに対応させたり(Day 8)、新しくアプリケーションをPSGIインタフェースで開発する話をしてきましたが、エラー処理についてはまだ解説していませんでした。 4 | 5 | ### エラー処理 6 | 7 | plackupではデフォルトで素敵なスタックトレース(Day 3)が有効になっています。ユーザのアプリケーションが例外を投げた場合、これを捕捉してエラーページを表示します。もし、ミドルウェアやアプリケーションフレームワークのアダプターそのものにバグやエラーがあった場合はどうでしょう? 8 | 9 | 以下のコードを走らせてみます: 10 | 11 | > plackup -e 'sub { return [ 0, {"Content-Type","text/html"}, "Hello" ] }' 12 | 13 | 繰り返しですが、生のPSGIインタフェースでアプリケーションを書くことは本来ありませんが、ミドルウェアやフレームワークのアダプターにバグがあった場合のシミュレーションには十分です。 14 | 15 | このアプリケーションにブラウザからアクセスすると、サーバは以下のようなエラーとおもにクラッシュします。 16 | 17 | Not an ARRAY reference at lib/Plack/Util.pm line 145. 18 | 19 | これは、レスポンスのフォーマットがPSGIインターフェース的に不正であるためです。ステータスコードが不正ですし、ヘッダが配列でなくハッシュリファレンスになっており、またボディも配列でなくプレーンな文字列になっています。 20 | 21 | ### Lint ミドルウェア 22 | 23 | こうしたエアーを各サーバでチェックすることは可能ですが、あまり綺麗なほうほうではありません。コードの重複がおきますし、実際にプロダクション環境でこうしたチェックをすることはパフォーマンスの観点からもよくありません。こうしたアプリケーション、サーバやミドルウェアの妥当性はテストや開発時に検証し、プロダクションでは無効にするのがよいでしょう。 24 | 25 | Middleware::Lint はリクエストやレスポンスが妥当なPSGIインタフェースかをチェックします。PSGIアプリケーションをLintミドルウェアを有効にして実行します。 26 | 27 | > plackup -e 'enable "Lint"; sub { return [ 0, { "Content-Type"=>"text/html" }, ["Hello"] ] }' 28 | 29 | (**注意**: 執筆時はLintがplackupデフォルトではありませんでしたが、現在はdevelopmentモードでは有効になるため、別途指定する必要はありません) 30 | 31 | こうすると、アプリケーションにアクセスすると以下のようなエラーとスタックトレースが表示されます。 32 | 33 | status code needs to be an integer greater than or equal to 100 at ... 34 | 35 | Lintミドルウェアがレスポンスが正しくPSGI仕様に沿っているかチェックするためです。 36 | 37 | フレームワークのアダプタやミドルウェアを開発する際は、必ずLintミドルウェアを有効にすることを忘れないでください。 38 | 39 | ### 新しくPSGIサーバを書く 40 | 41 | Lintミドルウェアはリクエスト、レスポンス両方のバリデートを実装していますので、新しくPSGIサーバを開発する際にもチェックが利用できます。ただし、サーバの開発者でさらにPSGI仕様を正しく実装しているかテストしたいには、Plack::Test::Suiteを利用するとよいでしょう。 42 | 43 | 実際にPlack::Test::Suiteを利用しているコードがPlackの`t/Plack-Handler`ディレクトリ内に含まれています。ここにはPSGIサーババックエンドをテストするためのテストスイートが用意されていて、既存のPlack::Handlerはすべてこのテストをパスするように書かれています。 44 | -------------------------------------------------------------------------------- /ja/22_discover_more_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 22: さらにミドルウェアの紹介 2 | 3 | この連載も終了に近づいて来ましたが、すべてのミドルウェアを紹介するには紙面が足りないようです。今日はまだ紹介していないいくつかの素晴らしいミドルウェアを簡単に紹介しましょう。 4 | 5 | ### ErrorDocument 6 | 7 | アプリケーションが500エラーを出した際や、認証が失敗した際のForbiddenページを出力する際、レスポンスコードに応じて、カスタムのエラーページを表示したいことがあります。ErrorDocumentは、Apacheの同名ディレクティブと同様、この処理を実装しています。 8 | 9 | builder { 10 | enable "ErrorDocument", 500 => "/path/to/error.html"; 11 | $app; 12 | }; 13 | 14 | 任意のエラーコードをスタティックファイルのパスにマップします。開発時にはStackTraceミドルウェア、プロダクションではErrorDocumentできれいなエラーページを表示するとよいでしょう。 15 | 16 | このミドルウェアはPlackディストリビューションに付属しています。 17 | 18 | ### Session 19 | 20 | Rackでは`rack.session`をRackの環境変数で標準定義しており、Hashオブジェクトのインタフェースを提供しています。PSGIではこれを標準には取り入れていませんが、アイデアと実装についてはかなりインスパイアされています。 21 | 22 | builder { 23 | enable "Session", store => "File"; 24 | $qpp; 25 | }; 26 | 27 | デフォルトではセッションデータはオンメモリのハッシュに保存されますので、preforkやマルチプロセス型のサーバではうまく動作しません。CHIなどのエンジンが付属していて、またその他のストレージエンジンに対するインタフェースを記述することで、Authミドルウェアなどと同様、拡張を簡単に行うことができます。 28 | 29 | セッションデータは`psgix.session`というPSGI環境変数にハッシュリファレンスとして格納されています。アプリケーションやフレームワークはこのハッシュに直接アクセスしてもよいですし、Plack::Sessionモジュールを使ってラッパーを定義することも可能です。例えば、Tatsumakiフレームワークでは以下のように利用できます。 30 | 31 | # Tatsumaki app 32 | sub get { 33 | my $self = shift; 34 | my $uid = $self->request->session->get('uid'); 35 | $self->request->session->set(last_access => time); 36 | ... 37 | } 38 | 39 | Sessionミドルウェアの利点として、PSGIアプリケーション間でのセッション共有が可能になるということです。フレームワークのアダプターによっては、ユーザアプリケーションからPSGI環境変数へのアクセスが許可されていない場合がありますが、こうした問題は徐々に解消されていくはずです。 40 | 41 | SessionミドルウェアはStevan Littleによって開発され、[github](http://github.com/stevan/plack-middleware-session) とCPANから入手できます。 42 | 43 | ### Debug 44 | 45 | こちらも[Rack-bug](http://github.com/brynary/rack-bug)と[django debug toolbar](http://github.com/robhudson/django-debug-toolbar)からのインスパイアで、このミドルウェアを有効にすると、デバッグ用の「パネル」が表示され、リクエストに関するデータやアナリティクスが表示されます。 46 | 47 | 標準のパネルにはTimer(リクエスト時間)、Memory(メモリー使用量)、Request(リクエストヘッダ情報)、Response(レスポンスヘッダ)などが含まれています。 48 | 49 | builder { 50 | enable "Debug"; 51 | $app; 52 | }; 53 | 54 | 利用はたったこれだけで、利用するパネルを制限したり、標準以外のものを追加するには、`panels`パラメータを指定します。 55 | 56 | DBIのクエリプロファイラや、Catalystのログなどの拡張パネルはCPANとgithubから入手可能です。 57 | 58 | ### Proxy 59 | 60 | HTTPリクエストをインターネットまたはローカルネットワークで動いている別のアプリケーションにプロキシしたいことがあります。たとえば、JSONPをサポートしていないJSON APIをプロキシしたい場合など、Cross Origin Policy上アクセスできないため、プロキシを立てる必要があります。また、ローカルで動く別のアプリケーションに対してフロントエンド的に動作させるリバースプロキシ的な利用法もあるでしょう。 61 | 62 | Plack::App::Proxyはこれらを可能にします。 63 | 64 | use Plack::App::Proxy; 65 | use Plack::Builder; 66 | 67 | my $app = Plack::App::Proxy->new(host => '192.168.0.2:8080')->to_app; 68 | 69 | builder { 70 | mount "/app" => $app; 71 | }; 72 | 73 | Plack::App::ProxyはLee Aylwardによって[github](http://github.com/leedo/Plack-App-Proxy)で開発されています。 74 | 75 | ### さらに 76 | 77 | さらに多くのミドルウェアがPlackディストリビューションに付属し、またCPANから検索することができます。すべてのミドルウェアがすぐに使えるというわけではないかもしれませんが、PSGIをサポートするフレームワークで共有できるというすばらしいメリットがあります。 78 | -------------------------------------------------------------------------------- /ja/23_write_your_own_middleware.md: -------------------------------------------------------------------------------- 1 | ## Day 23: ミドルウェアを書く 2 | 3 | ミドルウェアの紹介の最後として、自分で書いてみることにしましょう。 4 | 5 | ### ミドルウェアを書く 6 | 7 | PSGIミドルウェアは、通常のPSGIアプリケーションと同様に動作しますが、実際のPSGIアプリケーションをラップするため、サーバからみるとアプリケーションのように見え、ラップされたアプリケーションからはサーバのように振る舞います。 8 | 9 | User-Agentを偽装するためのシンプルなミドルウェアは以下のようになります。 10 | 11 | # Wrapped application 12 | my $app = sub { 13 | my $env = shift; 14 | my $who = $env->{HTTP_USER_AGENT} =~ /Mobile Safari/ ? 'iPhone' : 'non-iPhone'; 15 | return [ 200, ['Content-Type','text/html'], ["Hello $who"] ]; 16 | }; 17 | 18 | # Middleware to wrap $app 19 | my $mw = sub { 20 | my $env = shift; 21 | $env->{HTTP_USER_AGENT} .= " (Mobile Safari)"; 22 | $app->($env); 23 | }; 24 | 25 | アプリケーションはリクエストがiPhoneブラウザ (*Mobile Safari*) からきた場合のみ、"Hello iPhone" を表示します。このミドルウェアは Mobile Safari 文字列をすべてのリクエストに追加します。このアプリケーションを実行して、任意のブラウザで表示すると、"Hello iPhone" が表示され、アクセスログには以下のように表示されるでしょう。 26 | 27 | 127.0.0.1 - - [23/Dec/2009 12:34:31] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 28 | (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like 29 | Gecko) Version/4.0.4 Safari/531.21.10 (Mobile Safari)" 30 | 31 | " (Mobile Safari)" がUser-Agent文字列に追加されています。 32 | 33 | ### 再利用可能なMiddlewareにする 34 | 35 | `.psgi`にインラインで記述する方法は、一度きりの利用をするには大変便利ですが、多くの場合、一般化して他のアプリケーションでも再利用できるようにしたいでしょう。Plack::Middlewareを利用すると、これが可能になります。 36 | 37 | package Plack::Middleware::FakeUserAgent; 38 | use strict; 39 | use parent qw(Plack::Middleware); 40 | use Plack::Util::Accessors qw(agent); 41 | 42 | sub call { 43 | my($self, $env) = @_; 44 | $env->{HTTP_USER_AGENT} = $self->agent; 45 | $self->app->($env); 46 | }; 47 | 48 | 1; 49 | 50 | たったこれだけです。Plack::Middlewareを継承し、必要とすつオプションをAccessorで定義し、`call`メソッドを実行して`$self->app`に処理をデリゲートします。このミドルウェアはPlack::BuilderのDSL(Day 11)と互換性があるので、 51 | 52 | use Plack::Builder; 53 | 54 | builder { 55 | enable "FakeUserAgent", agent => "Mozilla/3.0 (MSIE 4.0)"; 56 | $app; 57 | }; 58 | 59 | としてすべてのリクエストをInternet Explorer 4から来たように偽装することができます。また、条件によって有効にするには`enable_if` (Day 18)を使うことができます。 60 | 61 | ### リクエストの後処理 62 | 63 | 上で紹介した例はリクエストの`$env`に対して前処理をおこないました。レスポンスに対しての場合はどうでしょう。ほとんど同じで、 64 | 65 | my $app = sub { ... }; 66 | 67 | # Middleware to fake status code to 500 68 | my $mw = sub { 69 | my $env = shift; 70 | my $res = $app->($env); 71 | $res->[0] = 500 unless $res->[2] == 200; 72 | $res; 73 | }; 74 | 75 | これはとても実用的でないミドルウェアで、すべての200以外のステータスコードを500に変更します。使う場面は想像できませんが、例としては十分でしょう。 76 | 77 | 多くのサーバがPSGIの[streaming interface](http://bulknews.typepad.com/blog/2009/10/psgiplack-streaming-is-now-complete.html)を実装して、レスポンスのストリームを可能にしていますが、このミドルウェアはそのインタフェースには対応していません。このインタフェースに対応するコードを各ミドルウェアで書くのは効率的でないので、Plack::Middlewareではそのユーティリティを用意しています。 78 | 79 | package Plack::Middleware::BadStatusCode; 80 | use strict; 81 | use parent qw(Plack::Middleware); 82 | 83 | sub call { 84 | my($self, $env) = @_; 85 | my $res = $self->app->($env); 86 | $self->response_cb($res, sub { 87 | my $res = shift; 88 | $res->[0] = 500 unless $res->[0] == 200; 89 | }); 90 | } 91 | 92 | 1; 93 | 94 | レスポンス `$res` を`response_cb` に渡し、レスポンスをラップするコールバックを渡します。このユーティリティが、ストリーミングインタフェースへの対応を面倒みてくれます。 95 | 96 | ### 名前空間 97 | 98 | この例では Plack::Middleware 名前空間にモジュールを定義しましたが、必ずしもこの名前空間を使う必要はありません。多くのPSGIアプリケーションで利用できる一般的なものであれば、この名前空間で問題ありませんが、特殊な用途に利用するものや、特定のフレームワークのみで動作するものなどは、その他の名前空間を利用し、 99 | 100 | package MyFramework::Middleware::Foo; 101 | use parent qw(Plack::Middleware); 102 | 103 | +(プラス)記号をつかって名前空間を指定します。 104 | 105 | enable '+MyFramework::Middleware::Foo', ...; 106 | 107 | DSLでないAPIを使う場合には、 108 | 109 | $app = MyFramework::Middleware::Foo->wrap($app, ...); 110 | 111 | とすれば問題なく動作します。 112 | -------------------------------------------------------------------------------- /ja/24_wrap_up.md: -------------------------------------------------------------------------------- 1 | ## Day 24: まとめ 2 | 3 | これが最後のカレンダーエントリになります。 4 | 5 | ### Best Practices 6 | 7 | PlackとPSGIはまだ始まって間もないプロジェクトですが、新しくPSGI対応のアプリケーションやフレームワークを書く際のベストプラクティスがまとまってきています。 8 | 9 | 新しくフレームワークを書く際には、ユーザアプリケーションからPSGI環境変数へアクセス出来る方法を提供してください。直接アクセスできるのでも、アクセサ経由でもかまいません。DebugやSessionなど、PSGI環境変数を利用するミドルウェアを利用できるようになります。 10 | 11 | Plack::Requestを`.psgi`ファイルから使ってアプリケーションを記述することは避けてください。スタイルの問題もありますが、アプリケーションは適切なクラスやオブジェクトに分割し、再利用やテスト(Day 13)可能にするのがよい開発手法です。`.psgi`ファイルは数行のブートストラップコードと、Builderによるミドルウェア設定のみになるでしょう。 12 | 13 | Plack::App名前空間を使用することは避けてください。Plack::App名前空間はラッパーとして動作しないミドルウェア用に予約されたもので、Proxy, File, CascadeやURLMapなどが良い例です。Plackを使ってブログアプリを書いたからといって、Plack::App::Blogのようなものは**決して**使わないでください。ソフトウェアの名前は何をするかによって決められるべきで、何を使ってかかれたかは関係ないはずです。 14 | 15 | ### 探索 16 | 17 | Plackデベロッパーは[github](http://github.com/)でコードを開発しています。[github レポジトリを"Plack"で検索](http://github.com/search?langOverride=&q=plack&repo=&start_value=1&type=Repositories)すると面白いものが見つかるかもしれません。CPANをPlackやPSGIで検索しても、ミドルウェアやPSGIに対応しているツールを見つけることができるでしょう。 18 | 19 | ### 開発チームにコンタクトする 20 | 21 | Plackはまだまだ若いプロジェクトで、まだまだ改善の余地があります。改善したい点、要望、バグをみつけたら、開発チームにコンタクトするなり、githubでforkしてpull requestを送ってください! 22 | 23 | IRCチャンネル irc.perl.org の #plack でチャットをしていますし、[メーリングリスト](http://groups.google.com/group/psgi-plack) や[githubのissue tracker](http://github.com/plack/Plack/issues)でコンタクトすることが可能です。 24 | -------------------------------------------------------------------------------- /ja/README.md: -------------------------------------------------------------------------------- 1 | rack-handbook 2 | ============= 3 | 4 | Decipleine for understanding rack - from https://github.com/miyagawa/plack-handbook 5 | 6 | (TBD English informations...) 7 | 8 | 9 | ## Rack Handbook について 10 | 11 | * @miyagawa さんによる [the Plack Handbook](http://handbook.plackperl.org/) の内容を [Rack](http://rack.rubyforge.org/) に翻訳するためのプロジェクトです。 12 | * ライセンスは原著と同じく [Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0) ](creativecommons.org/licenses/by-nc/3.0/) とします(see LICENSE)。 13 | * Pull Request Welcome! 14 | --------------------------------------------------------------------------------