级别: 中级 Tyler Anderson (tyleranderson5@yahoo.com), 自由撰稿人, Stexar Corp.
2008 年 7 月 15 日
这个分为四个部分的系列文章 讨论如何使用 XForms、IBM® DB2® pureXML™ 和 Ruby 简化 Web 应用程序的开发,这是第四部分。本系列教程开发了一个虚构的管理诊所患者信息的应用程序。通过本教程,读者可以领略到这些强大的技术,知道如何将其结合起来。本系列的第 4 部分,我们将继续开发针对诊所的 XForms,增加了一个新表单按照姓氏查找患者。
简介
到目前为止,我们已经创建了帮助患者和护士管理患者数据的 DB2 数据库和三个 XForms。通过使用 Ruby on Rails 作为处理接口,患者可以向数据库添加新的记录,并在以后更新信息。因此护士可以查看并适当地修改这些信息,当患者可以诊治的时候批准输入的数据。
这是本系列的最后一部分,将为医生创建新的视图和控制器以及该视图的两个新表单。医生通过第一个表单可以查看由患者输入并经过护士批准的患者记录,并增加诊治过程中的新信息。然后开发一个表单让医生和护士按照姓氏查找患者信息。
先决条件
本文假设读者基本熟悉 XML 和 Web 应用程序。当然,事先涉猎过 XForms、DB2 pureXML 和 Ruby on Rails 这三种核心技术很有帮助,但不是必需的。本文使用了 Mozilla XForms 插件 0.8.0.3。它为所有的 Mozilla 浏览器提供了 XForms 运行时支持,如 Firefox。另一种有用的 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 即可)。参见 参考资料 部分获得下载链接。
医生访问 XForm
医生 XForm(doctorPatient.xhtml)的目的是让医生看到和护士在 kiosk 以及 approved 视图中所看到的相同的患者信息。在此基础上,这个 XForm 还需要让医生输入诊治病人的信息。因此除了少数区别外,doctorPatient 表单和 triagePatient 表单非常类似,如清单 1 所示。将该文件命名为 doctorPatient.xhtml 并保存到 public 文件夹。
清单 1. doctorPatient 表单
...
<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>
<BPressure></BPressure>
<Notes></Notes>
</p:Info>
</xf:instance>
<xf:submission action="http://localhost:3000/doctor/update/0"
method="post"
id="submit-info"/>
<xf:submission id="load_data"
action="http://localhost:3000/doctor/grab/0"
method="post"
replace="instance"
/>
<xf:action ev:event="xforms-ready">
<xf:dispatch name="xforms-submit" target="load_data"/>
</xf:action>
...
<xf:label>Please describe your symptoms:<br/></xf:label>
</xf:textarea>
</div>
<div id="bloodpressure">
<xf:input ref="BPressure">
<xf:label>Blood Pressure: </xf:label>
</xf:input>
</div>
<div id="notes">
<xf:textarea ref="Notes">
<xf:label>Notes regarding<br/>visit
with patient:<br/></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="doctor/list">Back to List</a>
</body>
</html>
|
首先要注意实例数据中新增加了两个元素 BPressure 和 Notes。这两个字段保存医生所测得的患者血压,并告诉医生可以输入文本字段。这里的 submit-info 和 load_data 提交元素的 action 属性指向医生控制器。最后,还可以看到 XForm 体中声明了两个新的 XForms 元素。
下面生成医生视图和控制器的 scaffolding。
医生视图和控制器
Rails scaffolding 为 Ruby 应用程序的开发提供了非常好的起点。生成的医生视图和控制器 scaffolding 如清单 2 所示。
清单 2. 生成医生视图和控制器的 scaffolding
ruby script/generate scaffold patient doctor
exists app/controllers/
exists app/helpers/
create app/views/doctor
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/doctor/_form.rhtml
create app/views/doctor/list.rhtml
create app/views/doctor/show.rhtml
create app/views/doctor/new.rhtml
create app/views/doctor/edit.rhtml
create app/controllers/doctor_controller.rb
create test/functional/doctor_controller_test.rb
create app/helpers/doctor_helper.rb
create app/views/layouts/doctor.rhtml
identical public/stylesheets/scaffold.css
|
要注意很多东西已经写好了,Rails 仅创建了还没有的文件。接下来需要定义 doctor 控制器中的 grab 和 update 方法,如清单 3 所示。
清单 3. 定义 doctor 控制器中的 grab 和 update 方法
def grab
id = @request.env["HTTP_REFERER"].split('=')[1]
@patient = Patient.find(id)
doc = REXML::Document.new(@patient.information)
if doc.root.elements["Notes"] == nil then
el = REXML::Element.new("Notes")
doc.root.add el
end
if doc.root.elements["BPressure"] == nil then
el = REXML::Element.new("BPressure")
doc.root.add el
end
@patient.information = doc
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
@patient.update_attributes(params[:patient])
redirect_to :action => 'list'
end
|
首先要注意,grab 方法和其他两个控制器(kiosk 与 triage)大不相同。这是因为,BPressure 和 Notes 这两个新元素目前对于数据库中新增和批准的记录还不存在。因此请求记录的时候,将搜索这两个新的 XML 元素,如果不存在,需要在通过 grab 视图返回 XForm 之前加上。XML 文档中 BPressure 和 Notes 元素都存在之后,将患者信息字段设置为包含在 doc 变量中的新 XML。
讨论 update 方法之前,首先将 grab.rhtml 从 triage 视图(app/views/triage/grab.rhtml)复制到 doctor 视图(app/views/doctor)。这样 grab 视图完成了。
update 方法没有增加新功能,但必须将新的 XML 数据保存到 DB2 。
下面我们看看如何更新医生列表视图。
医生的列表视图
通过这个视图,医生可以看到信息经过护士认可的所有患者的姓和名。修改 list 视图(app/views/doctors/list.rhtml)如清单 4 所示。
清单 4. 修改医生的 list 视图
<h1>Listing patients</h1>
<table>
<tr>
<th><%= "First Name" %></th>
<th><%= "Last Name" %></th>
</tr>
<% for patient in @patients %>
<% if patient.approved=="true" then %>
<tr>
<td><% doc = REXML::Document.new(patient.information) %>
<%= doc.root.elements["FirstName"] %>
</td>
<td>
<%= doc.root.elements["LastName"] %>
</td>
<td><%= "<a href=\"../doctorPatient.xhtml?id=" +
patient[:id].to_s + "\">View/Add Notes</a>" %></td>
<td><%= link_to 'Delete', { :action => 'destroy', :id => patient },
:confirm => 'Are you sure?',
:method => :post %></td>
</tr>
<% end %>
<% end %>
</table>
...
|
这里输出姓名供医生查看。清单 4 中还要注意 View/Add Notes 链接,它指向 doctorPatient 表单。用户单击该链接将打开一个 XForm,和前面表单不同的是,医生可以在这里输入信息(血压和注释)。
图 1. 医生 XForm
医生可以查看患者的姓名,单击 View/Add Notes 链接在表单中打开(如图 2 所示)。
图 2. 医生和患者 XForm
在这里医生可以添加 Please describe your symptoms 文本框,输入病人的血压,在 Notes 框中记录诊治过程中的特殊事项。提交后将返回到 图 1 所示的网页。
这样医生表单就完成了。接下来创建患者查找表单。
患者查找表单
如果有很多病人,该怎么办?在繁忙的诊所里查找病人要花很多时间。这个表单允许医生和护士按照姓氏查找患者,可以缩短手工搜索的时间。新表单需要一个新的 graball 视图,将所有病人放在一个 XML 文档中,然后将这个文档返回给 XForm。首先要创建一个新视图,然后创建新的 XForm。
创建 graball 控制器
graball 控件如其名字所示,是在单一 XML 文件中返回数据库中所有患者的新视图。在 doctor 视图目录(app/views/doctor/graball.rhtml)目录下创建一个新文件,内容如清单 5 所示。
清单 5. 编写 graball 视图
<% @headers["Content-Type"] = "text/xml; charset=utf-8" %><%=
"<Patients xmlns:xf=\"http://www.w3.org/2002/xforms\"
xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"
xmlns:p=\"http://developerworks.ibm.com/patient\"
xmlns:ev=\"http://www.w3.org/2001/xml-events\">" %>
<% for pat in @patient %>
<%= "<Info DbId=\"" + pat.id.to_s +
"\" ViewEditLinkDoc=\"\" ViewEditLinkTri=\"\">" %>
<% doc = REXML::Document.new(pat.information) %>
<%= doc.root.elements["LastName"].to_s %>
<%= "</Info>" %>
<% end %>
<%= "</Patients>" %>
|
需要注意的是,该视图和 grab 视图一样,首先将 Content-Type 头部设置为 “text/xml” 类型,这样 Mongrel 和 Firefox 就知道如何处理它了。然后打开 Patients 标签并设置名称空间。有趣的是 for 循环:数据库中的每位患者都遍历一次,在 XML 文章中生成对应的一组 <Info>...</Info> 标签。Info 标签有三个属性:
-
DbId
— 患者在数据库中对应的 ID。
-
ViewEditLinkDoc
— 这个局部变量包含打开 doctorPatient 表单显示患者信息的链接。
-
ViewEditLinkTri
— 类似于 ViewEditLinkDoc,这也是个局部变量,在 triagePatient 表单中显示患者信息。
显示 Info 起始标签之后,从患者 XML 记录中检索病人的姓氏,就像在列表视图中检索一样(如 清单 4 所示)。结束标签 </Info> 最后把患者记录存入 XML。患者全部遍历之后,for 循环执行结束,写上 </Patients> 标签完成 XML。
请注意,医生控制器中没有对应的 graball 方法。代码如清单 6 所示。
清单 6. 医生控制器中的 graball 方法。
def graball
@patient = Patient.find_by_sql("select * from patients");
end
|
不需要根据 ID 等唯一标识符去查找,只要一条 SQL 语句要求返回所有的患者。这就是为 清单 5 中的 graball 视图抓取所有患者的方法。
下面开始编写患者查找表单。
患者查找 XForm
这是本系列教程中的最后一个 XForm!有了这个表单之后,医生和护士很快就能按照姓氏找到病人。创建一个新的 XForm(lookupPatient.xhtml)保存到 public 目录下,其代码如清单 7 所示。
清单 7. lookupPatient 表单
<?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:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1" />
<title>Patient Lookup</title>
<xf:model id="patientModel">
<xf:instance xmlns="" id="searchLastName">
<search>
<LastName/>
</search>
</xf:instance>
<xf:instance xmlns="" id="patients">
<Patients>
<!--
<Info DbId="" ViewEditLinkDoc="" ViewEditLinkTri="">
<LastName/>
</Info>
...
-->
</Patients>
</xf:instance>
<xf:submission id="load_data"
action="http://localhost:3000/doctor/graball/0"
method="post"
replace="instance"
instance="patients"/>
<xf:action ev:event="xforms-ready">
<xf:dispatch name="xforms-submit" target="load_data"/>
</xf:action>
</xf:model>
</head>
<body>
<p>
<div id="searchLastName">
<xf:input ref="instance('searchLastName')//LastName"
incremental="true">
<xf:label>Search Last Name:</xf:label>
</xf:input>
</div>
<table>
<th>
<td width="200px">Last Name</td>
<td width="100px">View/Edit Doctor</td>
<td width="100px">View/Edit Triage</td>
</th>
</table>
<xf:repeat id="patientRepeat"
nodeset="instance('patients')//Info
[LastName=instance('searchLastName')//LastName]">
<table>
<tr>
<td width="200px">
<div id="LastName">
<xf:output ref="LastName"/>
</div>
</td>
<td width="100px">
<div id="viewDoctor">
<xf:trigger>
<xf:label>View/Edit Doc</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="@ViewEditLinkDoc"
value="concat('doctorPatient.xhtml?id=',../@DbId)"/>
<xf:load ref="@ViewEditLinkDoc" show="new"/>
</xf:action>
</xf:trigger>
</div>
</td>
<td width="100px">
<div id="viewTriage">
<xf:trigger>
<xf:label>View/Edit Tri</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="@ViewEditLinkTri"
value="concat('triagePatient.xhtml?id=',../@DbId)"/>
<xf:load ref="@ViewEditLinkTri" show="new"/>
</xf:action>
</xf:trigger>
</div>
</td>
</tr>
</table>
</xf:repeat>
</p>
<a href="doctor/list">Back to Doctor</a><br/>
<a href="triage/list">Back to Triage</a>
</body>
</html>
|
该表单中有几个迄今还没有遇到的新元素。此外,它有两个实例文档需要分别管理。id
为 searchLastName 的第一个实例文档包含要搜索的患者姓氏。患者实例文档包括 load_data 提交元素加载的 XML(加载 XForm 调用医生 graball 视图时引发)。加载该实例数据后 XForm 并没有特别的变化,除非输入和患者 XML 实例数据中的某个姓氏相匹配的姓氏(如图 3 所示)。
图 3. lookupPatient 表单
清单 7 的 XForm 中包含一个文本框,医生和护士可以输入匹配的姓氏。接下来设置了标题,一个用于要查找的姓氏,两个用于按钮,让医生和护士在不同的表单中(doctorPatient 和
triagePatient)打开找到的患者的信息。如果找到结果,就要用到 repeat 元素了。repeat 元素 nodeset 属性中的 XPath 语句指定了要在 repeat 语句中显示的 Info 元素。如果患者 XML 实例数据中有一个姓氏和文本框中输入的内容匹配,就通过 XPath 找到对应的信息。第一列中显示姓氏,第二列中的按钮在 doctorPatient 表单中显示患者信息,第三列的按钮在 triagePatient 表单中显示患者信息(如图 4 所示)。
图 4. lookupPatient 表单
匹配的患者数据显示在表格的一行中。这种方法非常快捷,类似于 Ajax,是通过 “Search
Last Name” 文本框的 incremental="true" 属性实现的(如 清单 7 所示)。在 doctorPatient 和 triagePatient 表单中显示数据只需要点击 图 4 中的两个按钮即可。
太方便了!我们已经完成了关于如何使用 Ruby 和 DB2 pureXML 开发 XForms 的系列文章。
结束语
真了不起!为了在本系列的最后一部分完成应用程序,我们开发了医生 XForm 和患者查找 XForm,以便简化查找患者的工作,因为可能有成千上万的患者记录等着去找。
掌握了这三种技术之后,您就会发现
XForms、Ruby on Rails 和 DB2 pureXML 真是巧妙的搭配。如果还没有掌握这些技术,请参阅 参考资料 部分。这些链接可以帮助您拓展这三种技术的知识和技能。祝您好运!
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| Part 4 sample code | part4_doctorsOffice.zip | 15KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
关于作者  | |  | Tyler Anderson 在 2004 年从 Brigham Young University 毕业并获得计算机科学学士学位,次年 12 月获得计算机工程硕士学位。Tyler 目前是一位自由作家,也是 Backstop Media 的开发人员。 |
对本文的评价
|