レベル: 中級 Michael Galpin (mike.sr@gmail.com), Software architect, eBay
2008年 05月 27日 2008年 06月 04日 更新 この記事は、XForms と IBM® DB2® pureXML™、そして Ruby を一緒に使うことで容易に Web アプリケーションを作成する 4 回シリーズの第 2 回です。このシリーズでは、病院で患者の情報を管理するための仮定のアプリケーションを作成し、XForms、DB2 pureXML、そして Ruby on Rails それぞれの技術が個々に持つ強みの一端を知ると共に、これらの技術を併せて使用する方法も学びます。シリーズ第 2 回目の今回はアプリケーションの実装を開始します。
はじめに
この 4 回シリーズの第 1 回では、病院で患者が情報を入力するための Web アプリケーションの設計を開始しました。そこでは、そうしたアプリケーションを作成するために XForms と DB2 pureXML、そして Ruby on Rails をどのように使えばよいかを説明し、これらの技術を一緒に使っていくつかの実験を行いました。今回の第 2 回では、アプリケーションの実装を始めます。最初の XForms を設計し、DB2 にフォームのデータを挿入するための Ruby on Rails のバックエンドを作成します。この記事を読むことで、これらの 3 つの技術をどのように使用すればアプリケーション全体にわたって XML を活用できるのかを理解できるはずです。
前提条件
 |
頻繁に使用する頭字語
- API: application programming interface
- CSS: Cascading Stylesheets
- UI: User Interface
- URL: Uniform Resource Locator
- XML: Extensible Markup Language
- XSD: XML Schema Definition
|
|
この記事では XML と Web アプリケーションに関して全般的に慣れていることを前提としています。これまでに 3 つのコア技術 (XForms とDB2 pureXML、そして Ruby on Rails) を経験したことがあればもちろん役立ちますが、決してそれが必須なわけではありません。この記事を執筆するにあたり、Mozilla XForms プラグインのバージョン 0.8.0.3 を使用しました。このプラグインによって、Firefox を始めとするすべての Mozilla ブラウザーで XForms ランタイムをサポートすることができます。もう 1 つ、Mozilla 用の非常に便利なプラグインが XForms Buddy です。XForms Buddy は XForms 用のデバッガーです。この記事ではバージョン 0.5.6 を使用しました。また IBM の DB2 データベース・サーバーも必要です。この記事では Windows® システムと Linux® システム、そして UNIX® システムで使用できる DB2 Express-C バージョン 9.5 を使用しています。最後に、Ruby on Rails が必要です。この記事では Ruby 1.8.6 と Rails 1.2.5 を使用しています。また Rails と合わせて Mongrel Web サーバーも使っています。Mongrel は Ruby Gems から入手することができます (コマンドラインから gem install mongrel と入力すればよいだけです)。ダウンロード用のリンクは「参考文献」を参照してください。
患者情報
第 1 回では、このシリーズで使用している技術、つまり XForms と DB2 pureXML、そして Ruby on Rails によって、アプリケーションのフロントエンドとバックエンドでどのように XML を使えるようになるのかを説明しました。この設計の 1 つの長所は、XML データが中心になっていることです。この XML データ・モデルの設計では、XForms ベースのフロントエンドの実装方法と、Ruby on Rails のバックエンドを使って DB2 からデータを取得する方法を定義します。そこで、このアプリケーションを作成するためには XML データ・モデルの設計から始めるのが合理的です。
XML データ・モデル
このアプリケーションでは、医者や病院の職員が必要とする情報を患者が入力することができます。必要な情報としては、例えば患者の名前、(医療保険のための) 保険会社、年齢、co-pay の額 (医療保険会社との契約に基づく自己負担額)、そしてもちろん患者の症状があります (訳注: ここで言う医療保険は日本の健康保険のようなものですが、当然米国と日本とではさまざまな面が異なっています)。これらをすべて念頭に置いた上で、典型的なデータ・モデルのインスタンスがどのようなものになるかを見てみます (リスト 1)。
リスト 1. 患者情報に関する典型的な XML インスタンス
<?xml version="1.0" encoding="UTF-8"?>
<Info>
<FirstName>John</FirstName>
<MiddleName>David</MiddleName>
<LastName>Smith</LastName>
<Age>33</Age>
<Insurer>HealthCo</Insurer>
<Id>555-69-1212</Id>
<PolicyHolder>true</PolicyHolder>
<Copay>10</Copay>
<Symptoms>Cough, Fever</Symptoms>
</Info>
|
これらの大部分は単純なストリングですが、いくつか例外があります。MiddleName (ミドルネーム) はオプションのフィールドにする必要があります。Age (年齢) は整数であり、マイナスであってはなりません。Copay (co-pay) も同様です。PolicyHolder (医療保険加入の有無) はブール値のフラグです。Id は 3 桁 + 2 桁 + 4 桁というパターンとなるように定義します。こうしたことを考慮した、患者のデータ・モデルを記述するための XML スキーマをリスト 2 に示します。
リスト 2. 患者のスキーマ
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://developerworks.ibm.com/patient"
xmlns="http://developerworks.ibm.com/patient"
elementFormDefault="qualified"
xmlns:this="http://developerworks.ibm.com/patient">
<xs:simpleType name="policyId">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{3}-[0-9]{2}-[0-9]{4}"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="Info">
<xs:complexType>
<xs:sequence>
<xs:element name="FirstName" type="xs:NMTOKENS"/>
<xs:element name="MiddleName" type="xs:NMTOKEN" minOccurs="0"/>
<xs:element name="LastName" type="xs:NMTOKENS"/>
<xs:element name="Age" type="xs:nonNegativeInteger"/>
<xs:element name="Insurer" type="xs:NMTOKENS"/>
<xs:element name="Id" type="this:policyId" />
<xs:element name="PolicyHolder" type="xs:boolean"/>
<xs:element name="Copay" type="xs:nonNegativeInteger"/>
<xs:element name="Symptoms" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
XML データ・モデルが定義できたら、今度はユーザーがこのデータ・モデルのインスタンスを作成するための XForms を作成します。
患者用の XForms
これで、患者がどんなデータをアプリケーションに入力するのかがわかったので、XForms を定義する作業は非常に単純になります。つまりデータ・モデルの中の要素に対応するフォーム要素を作成すればよいだけです。このフォーム要素の型はデータ要素のデータ型をベースにしています。それを考慮した患者情報用の XForms をリスト 3 に示します。
リスト 3. 患者情報用の XForms
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1" />
<title>Patient Information</title>
<xf:model>
<xf:instance>
<Info xmlns="">
<FirstName/>
<MiddleName/>
<LastName/>
<Age/>
<Insurer/>
<Id/>
<PolicyHolder/>
<Copay/>
<Symptoms/>
</Info>
</xf:instance>
<xf:submission action="http://localhost:3000/kiosk/create"
method="post" id="submit-info"/>
</xf:model>
</head>
<body>
<p>
<div id="firstName">
<xf:input ref="FirstName">
<xf:label>First Name:</xf:label>
</xf:input>
</div>
<div id="middleName">
<xf:input ref="MiddleName">
<xf:label>Middle Name:</xf:label>
</xf:input>
</div>
<div id="lastName">
<xf:input ref="LastName">
<xf:label>Last Name:</xf:label>
</xf:input>
</div>
<div id="age">
<xf:input ref="Age">
<xf:label>Age:</xf:label>
</xf:input>
</div>
<div id="insurer">
<xf:input ref="Insurer">
<xf:label>Name of Insurance Provider:</xf:label>
</xf:input>
</div>
<div id="idNumber">
<xf:input ref="Id">
<xf:label>Insurance ID Number(###-##-####):</xf:label>
</xf:input>
</div>
<div id="holder">
<xf:select1 ref="PolicyHolder">
<xf:label>Are you the policy holder?</xf:label>
<xf:item>
<xf:label>Yes</xf:label>
<xf:value>true</xf:value>
</xf:item>
<xf:item>
<xf:label>No</xf:label>
<xf:value>false</xf:value>
</xf:item>
</xf:select1>
</div>
<div id="copay">
<xf:input ref="Copay">
<xf:label>Co-pay :$</xf:label>
</xf:input>
</div>
<div id="symptoms">
<xf:textarea ref="Symptoms">
<xf:label>Please describe your symptoms:</xf:label>
</xf:textarea>
</div>
<div id="submit">
<xf:submit submission="submit-info">
<xf:label>Submit Information</xf:label>
</xf:submit>
</div>
</p>
<a href="kiosk/list">Back to List</a>
</body>
</html>
|
リスト 3 の XForms を簡単に説明しましょう。ここには 1 つのモデルと 1 つのインスタンスがあります。このインスタンスは先ほど定義した患者情報のデータ・モデルの空白版です。また、このモデルの一部に submission があります。submission はモデルのインスタンスを取得し、submission アクションの属性に指定されている URL にポストします。フォーム本体の中には一連の XForms 要素があります。各要素は ref XPath を使ってモデルのインスタンスにバインドされています。ユーザーがフォームの中のデータにどのような値を入力した場合も、それらの値はモデルのインスタンスにバインドされます。この XForms をブラウザーにロードすると、図 1 のようなものが表示されるはずです。
図 1. 患者用の XForms
あまり美しい Web ページではありませんが、XForms としては十分に機能します。ここで興味深い点をいくつか挙げてみましょう。
- 第 1 に、このページは XHTML ページであり、通常の HTML ページではありません。このページの拡張子を .html または .htm に変更すると、適切に表示されなくなります。これは、XForms には XHTML が必要であり、HTML ページでは Mozilla プラグインが有効にならないためです。
- 次に、これは XHTML なので、相変わらず CSS をサポートしています。ページの見栄えを変更するためには CSS を使う方法が最も簡単です。この後すぐ、データ検証の一部として少しばかり CSS を使います。
- 最後に、URL が http://localhost:3000/patient.xhtml だったことに注目してください。上記の例では、このページは Mongrel Web サーバーを使って Ruby on Rails アプリケーションから提供されました。このページは静的なページなので、このページを Rails アプリケーションの公開ディレクトリーに単純にコピーすることができます。デフォルトの WEBrick Web サーバーを使うこともできますが、XHTML ページを提供するためには WEBrick Web サーバーに追加の構成が必要です。一方、Mongrel は XHTML ページに関して追加の構成は何も必要ありません。
先ほど触れたように、フォームを送信するとモデルのインスタンスが POST されます。しかし、そのポストの処理を始める前に、少し検証を行う必要があります。
検証
データ入力で最も一般的なタスクの 1 つが入力データの検証です。システムのバックエンドに無効なデータが送信されるのを防ぐために、検証はクライアントで行った方が望ましいことが多いものです。通常、そのためにはデータを抽出する JavaScript を作成し、そしてそれに対してルール (例えば正規表現) を実行します。幸いなことに、XForms はこれを非常に単純化してくれます。XForms は XML のためのものであり、そしてデータ検証用に XML で適切に定義された構文、つまり XML スキーマがあります。作成した XForms に対して、先ほどリスト 2 で定義したスキーマを使って検証を追加します (リスト 4)。
リスト 4. 検証用のスキーマを持つ XForms モデル
<title>Patient Information</title>
<style type="text/css">
@namespace xf url("http://www.w3.org/2002/xforms");
xf|input {
display: table-row;
line-height: 2em;
}
xf|label {
display: table-cell;
text-align: right;
font-family: Ariel, Helvetica, sans-serif;
font-weight: bold;
font-size: small;
padding-right: 5px;
width: 250px;
}
xf|*{
display: table-row;
line-height: 2em;
}
#submitLabel{
display: table-row;
}
*:required {
background-color: yellow;
}
*:invalid {
background-color: yellow;
}
</style>
<xf:model id="patientModel" schema="patient.xsd">
<xf:instance xmlns="" id="patient">
<p:Info>
<FirstName></FirstName>
<MiddleName></MiddleName>
<LastName></LastName>
<Age></Age>
<Insurer></Insurer>
<Id></Id>
<PolicyHolder></PolicyHolder>
<Copay></Copay>
<Symptoms></Symptoms>
</p:Info>
</xf:instance>
<xf:submission action="http://localhost:3000/kiosk/create"
method="post"
id="submit-info"/>
</xf:model>
|
このモデルが今やスキーマ (patient.xsd) を参照していることに注目してください。このスキーマのロード、そしてこのスキーマに対する検証を XForms が自動的に行います。また、無効なデータを表示するための CSS も少し追加してあることに注目してください。これをブラウザーで表示すると、図 2 のようになるはずです。
図 2. 検証と CSS が加わった XForms
このフォームを送信するためには、ユーザーは必須のフィールドに入力する必要があります。有効なデータが入力されると色が自動的に変更され、新しいフォーム・フィールドにフォーカスが移ります。通常はこのために JavaScript を作成する必要があることを考えてみてください。ここではそれが XSD と少しの CSS だけですんだのです。
XForms を利用すると、さまざまな方法で検証を行うことができます。必ずしも XML スキーマを使う必要はありませんが、XML スキーマを使うと検証が非常に容易になり、特にスキーマによって容易に記述できるデータの場合にはそれが顕著です。クライアント・サイドの検証が用意できると、送信されたデータに対するサーバー上での処理に集中することができます。
フォームの送信
先ほど、フォーム送信用の URL が既に定義されていることに気付いた人がいるかもしれません。Ruby on Rails に慣れている人であれば、この URL はかなり馴染みがあるはずです。Convention over Configuration (設定より規約)、そして Rails の規約に従い、この /patient/create という URL は patient コントローラーに対する create アクションに対応します。この設定は Ruby の生成スクリプトを使って容易に行うことができます (リスト 5)。
リスト 5. 患者用の scaffold を生成する
>ruby script/generate scaffold patient kiosk
exists app/controllers/
exists app/helpers/
create app/views/kiosk
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
skip app/models/patient.rb
identical test/unit/patient_test.rb
identical test/fixtures/patients.yml
create app/views/kiosk/_form.rhtml
create app/views/kiosk/list.rhtml
create app/views/kiosk/show.rhtml
create app/views/kiosk/new.rhtml
create app/views/kiosk/edit.rhtml
create app/controllers/kiosk_controller.rb
create test/functional/kiosk_controller_test.rb
create app/helpers/kiosk_helper.rb
create app/views/layouts/kiosk.rhtml
identical public/stylesheets/scaffold.css
|
これによって必要以上に大量のファイルが生成されます。使用しないファイルは削除することができます。作成されるもののうち、最も重要なものは patient クラスと kiosk_controller クラスの 2 つです。kiosk_controller クラスはご想像のとおり、XForms からのリクエストを処理するコントローラーです。では、モデルとコントローラーを調べ、それらをどう変更すれば XForms からの XML を保存できるのかを見て行きましょう。
Rails のモデルとコントローラー
Rails の scaffold を利用するとアプリケーション開発が迅速になります。多くの Rails アプリケーションでは、scaffold を使うことで、コントローラーを何も変更しなくてもデータベースとの対話動作を実際に処理することができます。そのためには Rails の ActiveRecord クラスの中にあるオブジェクトとリレーショナルのマッピング・コードを活用します (ActiveRecord クラスは patient クラスを含めたすべてのモデルの基底クラスです)。ActiveRecord は XML を処理するように設計されていないため、生成されたクラスを変更する必要があります。まず、リスト 6 の patient クラスを見てください。
リスト 6. デフォルトの patient クラス
class Patient < ActiveRecord::Base
end
|
これを見るとわかるように、デフォルトで patient クラスは単純に ActiveRecord を継承します。ActiveRecord は自分のマップ先となるデータベース・テーブルの列の名前に基づいて動的にアクセサーを作成します。アクセサーは名前と値のペアのハッシュを引数に取るコンストラクターを持っています。アプリケーションのコントローラーはこのコンストラクターを使って単純にフォームのデータを直接渡します。これを利用して patient クラスを変更することができます (リスト 7)。
リスト 7. 変更された patient クラス
require 'rexml/document'
class Patient < ActiveRecord::Base
def information=(value)
self[:information] = value.to_s
end
end
|
ほんの少しの変更ですが、この変更は重要です。内部では情報の値をストリングとして保持する必要がありますが、これは ActiveRecord がその情報をデータベースに挿入する際にストリングとしてシリアライズされるようにするためです。しかしモデルに渡されるのは何なのでしょう。これはコントローラーによって処理されます。リスト 8 はデフォルトのコントローラーを示しています。
リスト 8. デフォルトの Kiosk コントローラー
class KioskController < ApplicationController
def index
list
render :action => 'list'
end
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :action => :list }
def list
@patient_pages, @patients = paginate :patients, :per_page => 10
end
def show
@patient = Patient.find(params[:id])
end
def new
@patient = Patient.new
end
def create
@patient = Patient.new(params[:patient])
if @patient.save
flash[:notice] = 'Patient was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
def edit
@patient = Patient.find(params[:id])
end
def update
@patient = Patient.find(params[:id])
if @patient.update_attributes(params[:patient])
flash[:notice] = 'Patient was successfully updated.'
redirect_to :action => 'show', :id => @patient
else
render :action => 'edit'
end
end
def destroy
Patient.find(params[:id]).destroy
redirect_to :action => 'list'
end
end
|
先ほど、XForms に対するアクションの URL を /kiosk/create として定義したので、呼び出されるのは上記の create メソッドです。Rails は、送信動作が行われる際に自分が取得するのは (XML 文書ではなく) 一連の HTML フォーム要素であると考えます。そこでこのメソッドを少し変更し、作成された XForms によって送信される XML 文書を構文解析するようにします。リスト 9 は変更された create メソッドを示しています。
リスト 9. 変更された create メソッド
def create
doc = REXML::Document.new("<Info></Info>")
params[:Info].each_pair do |key,value|
if (key.index(':') == nil) #namespace attributes
el = REXML::Element.new key
el.add_text value
doc.root.add el
else
doc.root.add_attribute key,value
end
end
@patient = Patient.new
@patient.information = doc
if @patient.save
flash[:notice] = 'Patient was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
|
最初に気付くことは、REXML を使って XML 文書を作成していることです。REXML は Ruby に含まれている標準ライブラリーです。XForms からの XML 文書は Info パラメーターとして受信され、オブジェクトとして構文解析されます。このオブジェクトに対し、単純に繰り返し処理により XML に逆変換します。こうするだけで、Rails は文書をデータベースに挿入するようになります。今度はこれをテストしましょう。
アプリケーションをテストする
アプリケーションをテストするためには、通常の ruby script/server start コマンドを使って単純にサーバーを起動します。次にブラウザーで XForms にアクセスします (図 2)。フォームに入力して Submit Information をクリックします。すると図 3 のような画面が表示されるはずです。
図 3. 患者情報のリスト
この UI はどこから来たのでしょう。これは Rails の scaffold に用意されているリスト用の標準 UI です。この UI では、体裁が整えられていない XML の一部を使って Info フィールドが表示されていることに注意してください。これを右にスクロールすると、患者の view (表示)、edit (編集)、delete (削除) のコントロールが見えるはずです。表示と削除はどちらも、そのままで動作します。編集は動作せず、またページの一番下にある New Patient (新規患者) のリンクも動作しません。これらのリンクでは、XForms を使ってデータの入力を行うので、XForms を使うようにリンクを変更する必要があります。このシリーズの第 3 回では、この点について説明します。
まとめ
この記事では、患者が自分の情報を入力するためのデータ入力フォームを XForms を使って作成する方法を説明しました。XForms は患者データを XML 文書にバインドするための処理を行い、さらには XML スキーマに対する妥当性検証も行います。アプリケーションはこの XML 文書を Ruby on Rails のコントローラーに送信します。Ruby のおかげで、この入力を XML として構文解析する作業とその XML をストリングとしてシリアライズする作業が容易になります。DB2 はシリアライズされた XML ストリングを理解し、そのストリングを pureXML 技術を使ってそのまま保存することができます。Rails はさらに、開発者が何も特別なことをしなくてもこのデータを表示することができます。これで、DB2 の XML/SQL 構文を使って XML データに対してクエリーを実行したり、XML データを取得したりすることができるようになり、また Ruby を使ってデータのナビゲーションや抽出を行うこともできます。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Part 2 sample code | part2_doctorsOffice.zip | 7KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Michael Galpin は 1998年から Web アプリケーションの開発に従事しており、California Institute of Technology で数学の学位を取得しています。現在は、カリフォルニア州サンノゼにある eBay にアーキテクトとして勤務しています。 |
記事の評価
|