级别: 中级 Tyler Anderson (tyleranderson5@yahoo.com), 自由撰稿人, Stexar Corp.
2008 年 7 月 07 日
本系列文章 介绍如何结合使用 XForms、IBM® DB2® pureXML™ 和 Ruby 来简化 Web 应用程序的开发,分为四部分,这是第三部分。本系列文章中将开发一个虚构的应用程序来管理医生办公室中的患者信息。您将看到每种技术的强大功能,同时还将看到如何将其结合起来。第 3 部分将开发护士用的表单来编辑患者数据,学习如何用 Ruby 实现这样的功能。
简介
本系列文章的第 1 部分设计了一个 Web 应用程序,让患者在医生办公室里输入信息。讨论了如何使用 XForms、DB2 pureXML 和 Ruby on Rails 创建这样的应用程序,并对这些应用程序的用法做了一些实验。第 2 部分开始实现这个应用程序。设计了第一个 XForm,然后创建了将表单数据插入 DB2 的 Ruby on Rails 后端。我们将继续通过这三种技术在整个应用程序中利用 XML。
第 3 部分将进一步细化 Ruby 的用户界面,增加两个新表单来查看和编辑已有的患者:一个 Kiosk 视图编辑患者信息,一个新的 Triage 视图让护士查看、编辑和批准输入的患者数据。输入的数据经护士批准后,患者就可以去看医生了。患者看医生的时候,医生可以查看患者的数据并根据的自己的观察添加数据。第 4 部分介绍医生表单的结构,它允许医生阅读和编辑经过护士批准的任何患者数据,并增加医生诊断的结果。
先决条件
 |
经常用到的缩写词
- CSS:级联样式表
- URL:统一资源定位符
- XML:可扩展标记语言
|
|
本文假设读者对 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 视图提取数据。后面我们将看到这个新的视图。
Kiosk 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。
Kiosk 列表视图
然后需要修改列表视图(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 方法。
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 视图和控制器
需要把 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)以便容纳新增的数据库字段。
Patient 类
为了在数据库中保存 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 视图。
Triage 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 表单
Triage XForm
接受 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 |
|---|
参考资料 学习
获得产品和技术
讨论
关于作者  | |  | Tyler Anderson 在 2004 年从 Brigham Young University 毕业并获得计算机科学学士学位,次年 12 月获得计算机工程硕士学位。Tyler 目前是一位自由作家,也是 Backstop Media 的开发人员。 |
对本文的评价
|