Nora チュートリアル 【第 1 回】

Nora チュートリアル 第 1 回

MoonWolf (moonwolf@moonwolf.com)

MoonWolf Development

2004 年 9 月 2 日

nora_neko.pngRuby には cgi.rb という CGI ライブラリが添付されており、tDiary、Hiki、RWiki といった Web アプリケーションは cgi.rb を使って書かれています。しかし cgi.rb を使いづらいと思った人たちが現れ cgi.rb の代わりとなるライブラリがいくつか発表されました (NARFRweb 等)。その中から Nora という Web ライブラリを連載形式で紹介していきたいと思います。

Nora とは

Nora とは HTTP、CGI といったプロトコル、API の下位階層から HTML、アプリケーションフレームワークといった上位階層まで Web プログラミング全般をサポートするライブラリです。

特徴

  • CGI/mod_ruby/FastCGI/Rinda/メールを扱うためのインターフェースを用意
  • セッション管理用の永続化フレームワーク
  • ファイルアップロード対応
  • HTTP クッキー対応
  • Perl の LWP 相当の HTTP クライアント
  • 更新アンテナファイル (LIRS/hina-di) のジェネレータ・パーサー
  • HTML テンプレートエンジン装備

各クラスは機能ごとに分離され、cgi.rb から使うこともできますし、静的ファイル生成などのバッチ処理にも使えます。

Nora の利点

Ruby 標準の cgi.rb でも RAA の各種ライブラリを使えば Nora と同様のことができます。それでも Nora を使う利点とはなんでしょうか?

  • cgi.rb は 1 クラスに API、リクエスト、レスポンス、コンテンツ生成を詰め込んでいて美しくない。Nora はクラスが綺麗に分離されていてソースコードの見通しが非常に良い。
  • 新規 API への対応が容易。例えば AJP インタフェースを書けば、Perl の様に mod_jk との連携も可能。
  • ソースを変更することなく同じコードが CGI/mod_ruby/FastCGI で動作する。
  • リクエスト、レスポンスオブジェクトが Marshal 可能なため、dRuby と相性がいい。Nora の Rinda インタフェースはバックエンドのアプリケーションサーバを冗長構成可能でリアルタイムにサーバの追加・削除ができる。
  • cgi.rb のフォーム値の取得は Ruby1.6 → 1.8 で仕様変更されアドホックな実装になっていて罠にはまる。しかし、Nora ではシンプルに思ったとおりにフォーム値が取得できる。

Hello World の表示

まず Nora を使って “Hello World!” を表示してみましょう。

リスト 1

 1:#!/usr/local/bin/ruby
 2:require 'web'
 3:
 4:api = Web::Interface::AUTO.new
 5:
 6:api.each {|req|
 7:  rsp = Web::Response.new
 8:  rsp.content_type = 'text/plain'
 9:  rsp << "Hello World!"
10:  api.response req,rsp
11:}

2 行目の require ‘web’ で主要なクラスを全て読み込みます。

4 行目の Web::Interface::AUTO.new でインタフェースオブジェクトを生成しています。Web::Interface::AUTO は拡張子や環境変数から CGI/mod_ruby/FastCGI を自動的に判断してくれます。

6 〜 11 行の api.each {〜} が Web アプリケーションのメインループです。CGI では 1 回しか実行されませんが、FastCGI 等の常駐タイプの API に対応するため繰り返しループの形になっています。

7 行目でレスポンスオブジェクト (Web::Response) を生成しています。

8 行目ではコンテントタイプを ‘text/plain’ にセットしています。HTTP のレスポンスヘッダには ‘Content-Type:text/plain’ と出力されます。

9 行目で “Hello World!” をレスポンスに書き出しています。IO のように <<,write でレスポンスに出力することができます。

10 行目でインタフェースオブジェクトにリクエストとレスポンスを返しています。これによりクライアントのブラウザに “Hello World!” が表示されます。

リクエストからの値の取り方

フォームに入力した値や URL の ‘?’ 以降のクエリ値を取り出すには Web::Request#[] を使います。

cgi.rb ではクエリ値とフォーム値の区別はありませんが Nora では Web::Request#query と Web::Request#form で区別しています。区別する必要がない場合は Web::Request#[] でクエリ値、フォーム値のどちらか入っているほうの値が取得できます。

クエリまたはフォームの text フィールドで最初の値を取得するには req[“text”] または req[“text”,0] と記述します。値があれば String が戻り無い場合は nil が戻ります。

text フィールドの 2 番目の値を取得するには req[“text”,1] と記述します。

text フィールドの値を配列として取得するには req[“text”,nil] と記述します。

リスト 2

 1:#!/usr/local/bin/ruby
 2:require 'web'
 3:
 4:api = Web::Interface::AUTO.new
 5:
 6:api.each {|req|
 7:  name = req["name"]||"World"
 8:
 9:  rsp = Web::Response.new
10:  rsp.content_type = 'text/html'
11:  rsp.write <<EOS
12:<html>
13: <head>
14:  <title>Hello #{Web::escapeHTML(name)}!</title>
15: </head>
16: <body>
17: <h1>Hello #{Web::escapeHTML(name)}!</h1>
18:  <form>
19:   <input type="text" name="name" value="#{Web::escapeHTML(name)}">
20:   <input type="submit">
21:  </form>
22: </body>
23:</html>
24:EOS
25:  api.response req,rsp
26:}

7 行目で “name” という名前のフィールドの値を取り出しています。”name” の値が無かった場合、req[“name”] で nil が戻り name 変数は “World” になります。

11 〜 24 行目で HTML をヒアドキュメントの形式で書き出しています。

Web::escapeHTML(name)は name の中の ‘<’,’>’,’&’,’”’ を文字実体参照に置換 (エスケープ) しています。ユーザの入力した値を表示するときは、このようにエスケープ処理をしないとクロスサイトスクリプティングというセキュリティ問題になるので注意が必要です。

次回予告

今回紹介した内容では本格的なアプリケーションを組むには不十分だと思います。次回はより実践的なアプリケーションを組むための Web アプリケーションフレームワークとテンプレート機能について紹介いたします。

参考文献

著者について

moonwolf.jpg MoonWolf は半導体メーカに勤めるプログラマです。2000 年に Ruby に触れ、それ以降 RAA (Ruby Application Archive) にてライブラリ・アプリケーションを発表し続けています。登録プロジェクト数 26 と世界 2 位であり 1 位を目指して日夜拡張ライブラリの書けそうな C ライブラリを探しています。著者の連絡先は moonwolf@moonwolf.comです。