本系列文章的第 1 部分设计了一个 Web 应用程序,让患者在医生办公室里输入信息。讨论了如何使用 XForms、DB2 pureXML 和 Ruby on Rails 创建这样的应用程序,并对这些应用程序的用法做了一些实验。第 2 部分开始实现这个应用程序。设计了第一个 XForm,然后创建了将表单数据插入 DB2 的 Ruby on Rails 后端。我们将继续通过这三种技术在整个应用程序中利用 XML。
第 3 部分将进一步细化 Ruby 的用户界面,增加两个新表单来查看和编辑已有的患者:一个 Kiosk 视图编辑患者信息,一个新的 Triage 视图让护士查看、编辑和批准输入的患者数据。输入的数据经护士批准后,患者就可以去看医生了。患者看医生的时候,医生可以查看患者的数据并根据的自己的观察添加数据。第 4 部分介绍医生表单的结构,它允许医生阅读和编辑经过护士批准的任何患者数据,并增加医生诊断的结果。
本文假设读者对 XML 和 Web 应用程序有一定的了解。事先对这三种核心技术,XForms,DB2 pureXML 以及 Ruby on Rails,有所涉猎当然很有帮助,不过绝对不是必需的。本文使用 Mozilla XForms
插件版本 0.8.0.3 编写。它为所有 Mozilla 浏览器,如 Firefox,提供了 XForms 运行时支持。另一种有用的 Mozilla 插件是 XForms Buddy,提供了一种 XForms 调试器。本文使用的是 0.5.6 版。还需要 IBM 的 DB2
数据库服务器。本文采用 DB2 Express-C 9.5,支持 Windows®、Linux® 和 UNIX® 系统。此外还需要 Ruby on Rails。本文使用的是 Ruby 1.8.6 和 Rails 1.2.5。还用到了与 Rails 结合使用的 Mongrel Web 服务器。可通过 Ruby Gems 安装(只要在命令行中输入 gem install mongrel 即可)。下载链接参见 参考资料 小节。
虽然患者能够向系统输入数据了,但是返回的患者如何更新他们的信息呢?目前只能创建新的患者和查看结果。现在需要开发一个表单来编辑和更新原来的患者信息。
查看和编辑已有数据的功能是任何应用程序都不能缺少的,我们将通过一个新的表单来完成。在 public 目录下创建一个新的 XHTML 文件 editPatient.xhtml,同时将第 2 部分的 patient.xhtml 表单更名为 newPatient.xhtml。新的 editPatient 表单和 newPatient 很相似。清单 1 显示了两者的区别。
清单 1. 创建 editPatient 表单
...
<xf:model id="patientModel">
<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/update/0"
method="post"
id="submit-info"/>
<xf:submission id="load_data"
action="http://localhost:3000/kiosk/grab/0"
method="post"
replace="instance"
/>
<xf:action ev:event="xforms-ready">
<xf:dispatch name="xforms-submit" target="load_data"/>
</xf:action>
</xf:model>
...
<a href="kiosk/list">Back to List</a>
</body>
</html>
|
非常简单吧?首先要注意,在 id 为 “submit-info” 的提交元素中修改了动作 URL,指向 Ruby 更新脚本。还增加了一个新的动作元素并由 xforms-ready 事件触发,即当表单加载的时候触发。然后触发的这个动作分派 id 为 “load_data” 的提交元素从新的 grab 视图提取数据。后面我们将看到这个新的视图。
grab 视图根据引用 URL 的 id 变量查询数据库中的患者记录,对 XML 进行适当地格式化,把数据返回 XForm。实现这个新视图需要在 app\views\kiosk 目录下增加一个新文件 grab.rhtml。定义如下(一行)。
<% @headers["Content-Type"] = "text/xml; charset=utf-8" %><%= @patient.information %> |
这里将头部 Content-Type 设置为 text/xml,这是 XForms 处理程序要求的格式。然后直接输出患者信息变量,即 DB2 数据库为该记录定义的 XML。
但是这里还需要编辑 kiosk 布局(app/views/layouts/kiosk.rhtml),其中只剩下一行:<%= yield %>。
这样做是为了删除 XForms 处理程序所需要的 XML 数据之前和之后的所有多余标签。下一步是在 kiosk 控制器(app/controller/kiosk_controller.rb)中定义 grab,如清单 2 所示。
清单 2. 在控制器中定义 grab
def grab
id = @request.env["HTTP_REFERER"].split('=')[1]
@patient = Patient.find(id)
end
|
请注意,代码中包含从引用 URL 抓取的客户 id。这样就克服一个限制:XForms 不能在 POST 请求中提交接收到的 GET 请求变量。因此,如果 URL 是:
http://localhost:3000/editPatient.xhtml?id=60 |
从数据库中提取的记录的 id 是 60。数据然后传递给 清单 1 定义的 grab 视图,后者传递给 XForms。
然后需要修改列表视图(app/views/kiosk/list.rhtml),为当前列出的患者加上链接,以便转到 editPatient 表单查看和编辑患者。修改后的视图如清单 3 所示。
清单 3. 修改 list 视图
<h1>Listing patients</h1>
<table>
<tr>
<th><%= "ID" %></th>
</tr>
<% for patient in @patients %>
<tr>
<td><%=h patient.id %></td>
<td><%= "<a href=\"../editPatient.xhtml?id=" +
patient[:id].to_s + "\">Show/Edit</a>" %></td>
<td><%= link_to 'Delete', { :action => 'destroy',
:id => patient },
:confirm => 'Are you sure?',
:method => :post %></td>
</tr>
<% end %>
</table>
...
<br />
<a href="/newPatient.xhtml">New patient</a>
|
请注意显示的列的变化。这里显示了 ID 列,并且 Show/Edit 链接指向 editPatient 窗体,ID 附加在 URL 后面(因此 grab 视图知道从数据库读取哪一条记录并返回 XForm)。注意下方添加新患者的链接。单击该链接将打开本系列第 2 部分创建的表单。
该表单如图 1 所示。
图 1. 患者列表
单击链接就会看到包含已有数据的 editPatient 表单,如图 2 所示。
图 2. 在 editPatient 表单查看已有的数据
在这里可以根据需要编辑数据。要注意,提交该表单的时候,按照 清单 1 的定义,执行的是 kiosk/update 脚本而不是 kiosk/create 脚本。还有 URL 中的 id 变量,清单 2 中的 grab 控制器依靠它才知道从数据库获取哪个 id。
选择 Submit Information 按钮之前,应该修改 kiosk 控制器的 update 方法。
需要修改 update 控制器来处理 XForms 提交数据,存储到数据库中替代原来的 XML 数据。修改后的 kiosk 控制器如清单 4 所示。
清单 4. 修改 kiosk 控制器的 update 方法
def update
doc = REXML::Document.new("<Info></Info>")
params[:Info].each_pair do |key,value|
if (key.index(':') == nil) #removes
el = REXML::Element.new key
el.add_text value
doc.root.add el
else
doc.root.add_attribute key,value
end
end
id = @request.env["HTTP_REFERER"].split('=')[1]
@patient = Patient.find(id)
@patient.information = doc
@patient.update_attributes(params[:patient])
redirect_to :action => 'list'
end
|
注意和 create 控制器的相似性。差别在于如何使用引用 URL 检索原有的患者信息。首先,该方法创建了 XForms 提交数据的 XML 文档,然后从数据库检索已有的患者,用新数据替换已有的 XML 数据并保存到数据库。
这样就行了!对用户界面略加修改就能大大加强应用程序的功能,真是令人吃惊。现在可以查看和更新已有的患者记录了。
护士审批表单(triagePatient.xhtml)让护士从完全不同的视图(triage)编辑和审查患者输入的数据。triagePatient 表单允许护士使用 Submit Information 按钮编辑患者信息,使用 Approve 按钮编辑和批准患者信息。但是创建该表单之前,需要为患者表添加一个新的字段。
患者模式中新增一个 approve 字段,用于确定哪些信息已经经过审核批准,哪些还没有。要修改患者模式请打开 DB2 Control Center,找到 Patients 表,如图 3 所示。
图 3. 修改患者模式
右键单击 Patient 表,像上图那样单击 Alter。在打开的窗口中单击 Add,输入新的字段信息,如图 4 所示。
图 4. 添加 approved 字段
请注意字段名、数据类型、默认值和 Nullable。连续单击 OK。Control Center 将通知您修改已经成功,可以看到修改后的表模式,如图 5 所示。
图 5. 修改后的患者模式
如果使用 DB2 Control Center,实际上不需要使用命令行工具。
现在可以创建 Triage 视图了。
需要把 triage 视图、控制器以及护士 XForm 的患者模型联系起来。创建新的视图和控制器,请输入清单 5 所示的代码。
清单 5. 创建 triage 视图和控制器
ruby script/generate scaffold patient triage
exists app/controllers/
exists app/helpers/
create app/views/triage
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/triage/_form.rhtml
create app/views/triage/list.rhtml
create app/views/triage/show.rhtml
create app/views/triage/new.rhtml
create app/views/triage/edit.rhtml
create app/controllers/triage_controller.rb
create test/functional/triage_controller_test.rb
create app/helpers/triage_helper.rb
create app/views/layouts/triage.rhtml
identical public/stylesheets/scaffold.css
|
现在更新 triage 控制器(app/controllers/triage_controller.rb),定义 grab 并修改 update 控制器,如清单 6 所示。
清单 6. 编辑 triage 控制器
def grab
id = @request.env["HTTP_REFERER"].split('=')[1]
@patient = Patient.find(id)
end
def update
doc = REXML::Document.new("<Info></Info>")
params[:Info].each_pair do |key,value|
if (key.index(':') == nil) #removes
el = REXML::Element.new key
el.add_text value
doc.root.add el
else
doc.root.add_attribute key,value
end
end
id = @request.env["HTTP_REFERER"].split('=')[1]
@patient = Patient.find(id)
@patient.information = doc
approvedCode = params[:id]
if approvedCode == "1" then
@patient.approved = "true"
end
@patient.update_attributes(params[:patient])
redirect_to :action => 'list'
end
|
其中的 grab
和 kiosk(如 清单 2 所示)控制器的 grab 相同。update 惟一的不同是使用了数据库新增的 approved 字段。请注意,该字段的值从 ID 字段抓取,因此如果更新该记录的 URL 是 http://localhost:3000/triage/update/1,那么该患者就通过批准了。
最后还需要修改一下 triage 布局(app/view/layouts/triage.rhtml),并按照 kiosk 布局的方式定义(<%= yield %>)。要使 grab 视图正常工作需要做这样的修改。既然说到了 grab 视图,将 grab 视图从 kiosk 视图(app/view/kiosk/grab.rhtml)复制到 triage 视图文件夹(app/view/triage)。这样 grab 视图和控制器就准备好了。
接下来需要修改 patient 类(patient.rb)以便容纳新增的数据库字段。
为了在数据库中保存 approved 变量,需要修改 patient 类(app/model/patient.rb),如清单 7 所示。
清单 7. 修改 patient 类
class Patient < ActiveRecord::Base
def information=(value)
self[:information] = value.to_s
end
def approved=(value)
self[:approved] = value.to_s
end
end
|
patient 类修改好后,接下来要修改 list 视图。
这是运行护士表单需要修改的最后一个视图。正确的 list 视图应该允许单击患者的名字来编辑其信息。修改 list 视图(app/view/triage/list.rhtml),如清单 8 所示。
清单 8. 修改 list 视图
<h1>Listing patients</h1>
<table>
<tr>
<th><%= "First Name" %></th>
<th><%= "Last Name" %></th>
<th><%= "Approved" %></th>
</tr>
<% for patient in @patients %>
<% if patient.approved=="false" then %>
<tr>
<td><% doc = REXML::Document.new(patient.information) %>
<%= doc.root.elements["FirstName"] %>
</td>
<td>
<%= doc.root.elements["LastName"] %>
</td>
<td><%=h patient.approved %></td>
<td><%= "<a href=\"../triagePatient.xhtml?id=" +
patient[:id].to_s + "\">Edit/Approve</a>" %></td>
<td><%= link_to 'Destroy', { :action => 'Delete', :id => patient },
:confirm => 'Are you sure?',
:method => :post %></td>
</tr>
<% end %>
<% end %>
</table>
...
|
这里我们学习了从 XML 向列表添加新的列,包括显示患者的姓氏和名字,以及患者的信息是否已经过批准。通过 for 循环之后的 if 语句,可以仅向护士显示信息没有经过批准的患者。这里的关键在于如何从 XML 检索姓和名。首先创建一个新的 XML 文档,传入 XML 信息,然后调用 doc.root.elements["FirstName"] 检索 FirstName 元素。姓也通过同样的方式检索。此外还有 Edit/Approve 链接。单击该链接将打开后面将创建的 triagePatient 表单。
编写 triagePatient 表单之前,先看一看图 6 所示的 triage list 视图。
图 6. triage list 视图
现在开始创建 triagePatient 表单
接受 XForms 提交数据的 Ruby 脚本都已就绪。只需要定义 triagePatient XForm 了。将其命名为 triagePatient.xhtml 并放在 public 文件夹中。如清单 9 所示。
清单 9. triagePatient XForm
...
</p:Info>
</xf:instance>
<xf:submission action="http://localhost:3000/triage/update/0"
method="post"
id="submit-info"/>
<xf:submission action="http://localhost:3000/triage/update/1"
method="post"
id="approve"/>
<xf:submission id="load_data"
action="http://localhost:3000/triage/grab/0"
method="post"
replace="instance"
/>
<xf:action ev:event="xforms-ready">
<xf:dispatch name="xforms-submit" target="load_data"/>
</xf:action>
...
<xf:label>Submit Information</xf:label>
</xf:submit>
</div>
<div id="approve">
<xf:submit submission="approve">
<xf:label>Approve</xf:label>
</xf:submit>
</div>
</p>
<a href="triage/list">Back to List</a>
</body>
</html>
|
清单 9 中的表单和 editPatient(清单 1)类似,上面的代码显示了两者的区别。load_data 和 submit-info 提交元素没有变,只不过现在指向了 triage 脚本。还有一个新的提交元素(id 为 “approve”)指向下面的 URL,如 清单 9 所示:
http://localhost:3000/triage/update/1 |
URL 的 id 是 1,因此将患者记录的 approved 字段设为真,如 清单 5 中的 triage update 控件所示。新的提交按钮 Approve 将触发这个新的提交元素。该 XForm 的执行结果如图 7 所示。
图 7. 在 triagePatient 表单中查看待审批的患者数据
要将数据库中的 approved 字段设置为真,单击 Approve。这样做的同时也把它从 list 视图中删除,如图 8 所示。
图 8. 审批患者信息
这样就完成了本文所计划的任务。
您的 Ruby、XForms 和 DB2 技能越来越完美了。您掌握了如何结合使用 Ruby 和 XForms,并领略了使用 DB2 及其 Control Center 的乐趣和用户友好性。
第 4 部分将创建医生表单,包括供医生护士按照姓氏查找患者的新表单。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 第 3 部分的样例代码 | part3_doctorsOffice.zip | 11KB | HTTP |
学习
- 您可以参考本文在 developerWorks 全球站点上的 英文原文。
-
访问 Ruby and Rails 技术资源中心,这里整理了和 Ruby 动态语言以及十分流行的开源 Web 开发框架 Ruby on Rails 相关的技术文章、教程和相关资源。
-
使用 XForms 和 Ruby on Rails 开发小型门诊管理系统,第 1 部分:设置 IBM DB2 9 pureXML(Michael Galpin,developerWorks,2007 年 12 月):分析了如何利用 XForms、DB2 pureXML 和 Ruby on Rails 加速以 XML 为中心的 Web 应用程序的开发。
-
使用 XForms 和 Ruby on Rails 开发小型门诊管理系统,第 2 部分:实现患者信息 XForm(Michael Galpin,developerWorks,December 2007):结合使用 XForms、DB2 pureXML 和 Ruby 方便地创建 Web 应用程序。第 2 部分开始创建管理诊所患者信息的虚构应用程序。
- 关于 XForms 的入门教程,请参阅分为三部分的系列文章 XForms 入门简介(Chris Herbroth,developerWorks,2006 年 9 月)。
- 希望了解如何结合 XForms 和 Ajax?请参阅 XForms 技巧:XForms 技巧: 结合使用 Ajax 与 XForms(Nicholas Chase,developerWorks,2006 年 10 月)。
-
将 XForms 与 Google Web Toolkit 相结合,第 1 部分: 介绍 GWT 的 JavaScript Native Interface(Michael Galpin,developerWorks,2007 年 10 月):与 Google Web Toolkit 结合使用,进一步了解 XForms 和 Ajax。
-
面向 Java 开发人员的 Ajax: 探索 Google Web Toolkit(Philip McCarthy,developerWorks,2006 年 7 月):深入介绍 GWT 的第一个教程。
-
An introduction to Ruby on Rails for DB2 developers(Edd Dumbill,developerWorks,2006 年 6 月):了解 Ruby on Rails 和 DB2。
-
DB2 与 Ruby on Rails,第 1 部分:DB2 与 Ruby on Rails 入门(John Chun 等,developerWorks,2007 年 12 月):结合使用 DB2 和 Ruby on Rails。
-
DB2 与 Ruby on Rails,第 2 部分:DB2 的 pureXML 特性与 Ruby on Rails(John Chun 等,developerWorks,2007 年 12 月):了解如何使用 DB2 pureXML 和 Ruby on Rails。
-
DB2 与 Ruby on Rails,第 3 部分:使用 DB2 和 Ruby on Rails 进行测试(John Chun 等,developerWorks,2007 年 12 月):了解如何使用 DB2 增强 Rails 内置的单元测试功能。
-
DB2 Version 9.5 pureXML 新增强概述(Manaj Sardana,developerWorks,2007 年 12 月):了解 DB2 的最新改进。
-
使用 XQuery 查询 DB2 XML 数据(Don Chamberlin 和 Cynthia Saracco,developerWorks,2006 年 5 月):使用 XQuery 和 DB2。
-
XML 问题:REXML 库(David Mertz,developerWorks,2002 年 6 月):了解在 Ruby 中处理 XML 的 REXML 库。
- 请访问 IBM developerWorks Ajax 资源中心
- 访问 W3C 的 XForms 主页。
-
IBM XML 认证:了解如何才能成为一名 IBM 认证的 XML 及相关技术的开发人员。
-
XML 技术库:developerWorks XML 专区提供了大量技术文章和技巧、教程、标准以及 IBM 红皮书。
-
developerWorks 技术活动和网络广播:随时关注技术的最新进展。
-
技术书店:关于本文主题和其他技术主题的书籍。
-
developerWorks
podcasts:聆听软件开发人员之间有趣的交流和讨论。
获得产品和技术
- 下载 Mozilla、Firefox 或 Seamonkey 的 XForms 扩展。
-
XForms Buddy,这个 Mozilla/Firefox 扩展可以查看 XForms 的实例数据。随着实例数据的更新自动改变。
- 从 IBM 下载 DB2 Express-C version 9.5。
- 下载 Ruby 1.8.6 with Rails 1.2.5。
-
IBM
试用软件:使用这些试用软件开发您的下一个项目,可直接从 developerWorks 下载,包括来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 参与论坛讨论。
-
XML 专区讨论论坛:参与和 XML 有关的讨论。
-
developerWorks XML 专区:分享您的观点:阅读本文后在这个论坛上提交您的见解和建议。XML 专区编辑负责该论坛,欢迎您的参加。
-
developerWorks blogs:访问这些 blog,加入 developerWorks 社区。