首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 用 Groovy 服务器页面(GSP)改变视图(2)

精通 Grails 用 Groovy 服务器页面(GSP)改变视图(2)

自定义标记库虽然标准 Grails 标记很有帮助,但是最终会遇到需要自定义标记的情况。许多资深 Java 开发人员(包括我自己)公开表示,“自定义 TagLib 是合适的架构性解决方案。”,然后却偷偷地编写 scriptlet,以为别人看不到。编写自定义 JSP TagLib 需要的工作太多,所以难以抵抗 scriptlet 的诱惑。scriptlet 并不是正确的方法,但不幸的是,它是一种容易实现的方法。
Scriptlet 破坏了 HTML 基于标记的范式,将原始代码直接引入视图。错误的并不是代码本身,而是它们缺少封装和重用的潜力。重用 Scriptlet 的惟一方式就是 “复制-粘贴”。这导致 bug、代码膨胀,并严重违背了 DRY 原则。更不用说 Scriptlet 在可测试性方面的匮乏了。
这就是说,我必须坦白,随着开发期限越来越紧迫,我写的 JSP scriptlet 的比例也相当大。JPS 标准标记库(JSP Standard Tag Library,JSTL)在这方面帮助了我很多,但是编写我自己的自定义 JSP 标记则完全是另一回事。在我用 Java 代码编写自定义 JSP 标记、编译标记,并将大量时间浪费在将标记库描述符(Tag Library Descriptor,TLD)设置为正确的格式和位置时,我已经完全忘记了当初编写这个标记的理由是什么。编写测试来验证我的新 JSP 标记是否正确也同样麻烦 — 只能说我的出发点是好的。
对比之下,用 Grails 编写自定义 TagLibs 简直就是举手之劳。Grails 框架使得做正确的事(包括编写测试)变得很容易。例如,我经常需要在 Web 页面底部加上标准的版权声明。版权声明应该是这样的:          © 2002 - 2008, FakeCo Inc. All Rights Reserved.。问题在于,我希望第二个年份总是当前的年份。清单 6 显示了用 scriptlet 如何完成这个任务:
清单 6. 用 scriptlet 完成的版权声明
1
2
3
4
<div id="copyright">
&copy; 2002 - ${Calendar.getInstance().get(Calendar.YEAR)},
    FakeCo Inc. All Rights Reserved.
</div>




既然知道了如何处理当前年份,那么下面就要创建一个执行同样任务的自定义标记。请输入 grails create-tag-lib Date,这会创建两个文件:grails-app/taglib/DateTagLib.groovy(TagLib)和 grails-app/test/integration/DateTagLibTests.groovy(测试)。将清单 7 中的代码添加到 DateTagLib.groovy:
清单 7.简单的自定义 Grails 标记
1
2
3
4
5
class DateTagLib {
  def thisYear = {
    out << Calendar.getInstance().get(Calendar.YEAR)
  }
}




清单 7 创建一个 <g:thisYear> 标记。可以看到,年份直接写入输出流。清单 8 显示了新标记的效用:
清单 8.使用自定义标记的版权声明
1
2
3
<div id="copyright">
&copy; 2002 - <thisYear />, FakeCo Inc. All Rights Reserved.
</div>




您可能以为这就完成了。非常抱歉,这只完成了一半。
测试 TagLibs即使现在看起来一切正常,还是应该编写一个测试,确保这个标记日后不会出错。Working          Effectively with Legacy Code 的作者 Michael Feathers 说过,任何没有测试的代码都是遗留代码。为了防止 Feathers 先生大发雷霆,请将清单 9 的代码添加到 DateTagLibTests.groovy:
清单 9.自定义标记的测试
1
2
3
4
5
6
7
8
9
10
11
12
class DateTagLibTests extends GroovyTestCase {
  def dateTagLib

  void setUp(){
    dateTagLib = new DateTagLib()
  }

  void testThisYear() {
    String expected = Calendar.getInstance().get(Calendar.YEAR)
    assertEquals("the years don't match", expected, dateTagLib.thisYear())
  }
}




GroovyTestCase 是在 JUnit        3.x TestCase 之上一层薄薄的 Groovy 层。为只有一行代码的标记编写测试看起来似乎有些过分,但是很多时候问题的源头正是这一行代码。编写测试并不难,而且保证安全要比说抱歉更好。请输入 grails test-app 运行测试。如果一切正常,应该看到如清单 10 所示的信息:
清单 10.在 Grails 中通过测试
1
2
3
4
5
6
7
8
-------------------------------------------------------
Running 2 Integration Tests...
Running test DateTagLibTests...
                    testThisYear...SUCCESS
Running test TripTests...
                    testSomething...SUCCESS
Integration Tests Completed in 506ms
-------------------------------------------------------




如果 TripTests 的样子让您感到惊讶,请不要担心。在输入        grails create-domain-class Trip 时,将会为您生成一个测试。实际上,每个 Grails        create 命令都会生成对应的测试。确实,测试在现代软件开发中如此 之重要。如果以前没有编写测试的习惯,那么 Grails 将优雅地将您带到正确的方向上来,您肯定不会后悔。
grails test-app 命令除了运行测试之外,还会创建很好的 HTML 报告。请在浏览器中打开 test/reports/html/index.html,查看标准的 JUnit 测试报告,如图 1 所示。
图 1.单元测试报告编写并测试过简单的自定义标记之后,现在要构建一个略微复杂一些的标记。
返回列表