Taglib TMTOWTDI

与许多Perl系统一样,AxKit经常提供多种做事的方式。来自其他编程文化的开发者可能一开始会感到这些选择和自由有点令人困惑,但(希望)很快就会意识到这些选项提供了力量和自由。当一个工具集限制了你的选择太多,你最终会像用射钉枪拧螺丝一样做事。当然,选择太多并不一定是好事,但比选择太少要好。

上一次,我们看到了如何通过在Perl中实现一个简单的taglib模块My::WeatherTaglib,并将其部署到与其他XML过滤器一起的管道中,来构建一个天气报告应用程序。管道方法允许一种灵活性:根据手头的需求和支撑组织以最合适的方式分解应用程序的自由。

另一种灵活性是使用不同技术实现过滤器的自由。例如,有时明智的做法是以不同的方式构建taglib。在本文中,我们将看到如何使用两种其他方法构建相同的taglib。第一种重建使用Cocoon项目实施的技术,LogicSheets。第二种使用Jörg Walter的相对较新的SimpleTaglib,而不是之前文章中用于My::WeatherTaglib的TaglibHelper。SimpleTaglib比TaglibHelper更强大,但奇怪的是,也更复杂(尽管作者打算在不久的将来使其使用更简单一些)。

更改

AxKit v1.6现已发布,包含一些不错的错误修复和性能改进,主要由Matt Sergeant和Jörg Walter完成,以及Kip Hampton的几个新高级功能,我们将在未来的文章中介绍。

Matt还更新了他的AxKit兼容的AxPoint PowerPoint-like HTML/PDF等演示系统。如果你这个季度要参加任何大型Perl会议,那么你很可能会看到使用AxPoint构建的演示。这是一个很好的系统,也在Kip Hampton的XML.com文章中有介绍。

AxTraceIntermediate

我在撰写本文时使用的一个相当新颖的功能——比我愿意承认的更频繁——是Jörg Walter添加的调试指令AxTraceIntermediate。此指令定义了一个目录,其中AxKit将放置管道中每个过滤器之间传递的中间文档的副本。所以像这样

    AxTraceIntermediate /home/barries/AxKit/www/axtrace

的设置将在axtrace目录中为每个中间文档放置一个文件。本文中用于此的httpd.conf指令的完整集合稍后显示。

以下是请求URI /(来自第一篇文章),/02/weather1.xsp(来自第二篇文章),/03/weather1.xsp/03/weather2.xsp(都来自本文)后的axtrace目录

    |index.xsp.XSP         # Perl source code for /index.xsp
    |index.xsp.0           # Output of XSP filter

    |02|weather1.xsp.XSP   # Perl source code for /02/weather1.xsp
    |02|weather1.xsp.0     # Output of XSP
    |02|weather1.xsp.1     # Output of weather.xsl
    |02|weather1.xsp.2     # Output of as_html.xsl

    |03|weather1.xsp.XSP   # Perl source code for /03/weather1.xsp
    |03|weather1.xsp.0     # Output of XSP
    |03|weather1.xsp.1     # Output of weather.xsl
    |03|weather1.xsp.2     # Output of as_html.xsl

    |03|weather2.xsp.XSP   # Perl source code for /02/weather2.xsp
    |03|weather2.xsp.0     # output of my_weather_taglib.xsl
    |03|weather2.xsp.1     # Output of XSP
    |03|weather2.xsp.2     # Output of weather.xsl
    |03|weather2.xsp.3     # Output of as_html.xsl

每个文件名是URI的路径部分,其中/被替换为|,并附加一个步骤号(或.XSP)。编号文件是中间文档,.XSP文件是任何为本次请求编译的XSP过滤器的Perl源代码。将|03|weather2.xsp.*文件与/03/weather2.xsp请求的管道图进行比较。

注意那些“|”字符:在大多数shell中,它们会强制您引用文件名(因此阻止了通配符的使用)。

    $ xmllint --format "www/axtrace/|03|weather2.xsp.3"
    <?xml version="1.0" standalone="yes"?>
    <html>
      <head>
        <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
        <title>My Weather Report</title>
      </head>
      <body>
        <h1><a name="title"/>My Weather Report</h1>
        <p>Hi! It's 12:43:52</p>
        <p>The weather in Pittsburgh is Sunny
        ....

注意: 只有当XSP表重新编译时,才会生成.XSP文件,因此您可能需要使用touch命令更新源文档或重启服务器以生成一个新的。另一个需要注意的问题是,如果在处理管道的中间发生错误,则可能会留下陈旧的文件。在这种情况下,较低编号的文件(由成功的过滤器生成的文件)将来自此请求,但较高编号的文件将是陈旧的,是来自之前请求的遗留文件。使用动态管道配置(我们将在以后讨论)时,可能会出现一个稍微不同的问题:您可能会得到一个较短的管道,它只覆盖较低编号的文件,而留下陈旧的较高编号文件。

与这个功能的有用性相比,这些小问题并不算什么,您只需要注意它们以避免混淆。在为这篇文章调试时,我使用了一个Perl脚本来做类似以下操作

    rm -f www/axtrace/*
    rm www/logs/*
    www/bin/apachectl stop
    sleep 1
    www/bin/apachectl start
    GET http://localhost:8080/03/weather1.xsp

以在每个测试运行开始时创建一个干净的文件集。

在XSP之下

在我们继续到示例之前,让我们快速看看AxKit是如何处理XSP页面的。这将帮助我们理解不同方法固有的权衡。

AxKit通过编译源XSP页面到一个名为handler()的函数来实现XSP过滤,该函数被调用来生成输出页面。这被编译成Perl字节码,然后运行以生成XSP输出文档

XSP架构 这意味着XSP页面不是直接执行,而是通过运行相对高效的编译Perl代码来执行。字节码被保存在内存中,因此对于每个请求都不需要解析和代码生成的开销。

在构建输出文档时使用了三种类型的Perl代码:用于构建静态内容部分的代码、在源文档中以原始形式存在的代码(如<xsp:logic><xsp:expr>标签中包围的代码),以及实现由已注册的标签库模块(如上篇文章中提到的My::WeatherTaglib)处理的标签的代码。

标签库模块通过将自己注册为命名空间的处理器并抛出要编译到handler()例程中的代码片段来挂钩到XSP编译器。

XSP与标签库模块挂钩 代码片段可以调用回标签库模块或调用其他模块,如需要。例如,我们用来构建My::WeatherTaglib的TaglibHelper模块,以及我们在本文后面用于构建My::SimpleWeatherTaglib的SimpleTaglib模块,这些模块自动化了构建标签库模块的繁琐工作,因此您不需要解析XML,甚至通常不需要生成XML。

您可以通过将AxDebugLevel设置为10(将代码放入Apache的ErrorLog中)或使用上述提到的AxTraceIntermediate指令来查看AxKit生成的源代码。然后您必须说服AxKit重新编译XSP页面,方法是重启服务器并请求一个页面。如果必要的指令已经在运行的服务器中存在,那么简单地使用touch命令更新文件的修改时间就足够了。

这可以用来深入理解底层的工作原理。我鼓励新的标签库作者这样做,以了解您的标签库代码是如何实际执行的。您最终会发现,为了调试,您不得不这样做(相信我 :))。

LogicSheets: 上游标签库

AxKit使用管道处理模型,XSP包括如<xsp:logic><xsp:expr>这样的标签,允许你在XSP页面中嵌入Perl代码。这使得标签库可以实施为XML过滤器,放置在XSP处理器之前。这些通常使用XSLT将标签库调用转换为使用XSP标签的行内代码。

上游LogicSheets为XSP处理器提供数据事实上,XSP最初就是按照这种方式设计的,Cocoon至今仍然独家采用这种方法(但使用的是内联Java而不是Perl)。我没有在第一篇文章中展示这种方法,因为它比AxKit提供的标签库模块方法要笨拙得多,灵活性也差。

Cocoon项目将实现标签库的XSLT表单称为LogicSheets,我在本文中遵循这一惯例(我将全部使用Perl的标签库实现称为“标签库模块”)。

weather2.xsp

在查看天气报告标签库的logicsheet版本之前,这里先更新了上一篇文章中的XSP页面,以便使用它。

<?xml-stylesheet href="my_weather_taglib.xsl" type="text/xsl"?>
<?xml-stylesheet href="NULL"                  type="application/x-xsp"?>
<?xml-stylesheet href="weather.xsl"           type="text/xsl"?>
<?xml-stylesheet href="as_html.xsl"           type="text/xsl"?>

<xsp:page
    xmlns:xsp="http://apache.org/xsp/core/v1"
    xmlns:util="http://apache.org/xsp/util/v1"
    xmlns:param="http://axkit.org/NS/xsp/param/v1"
    xmlns:weather="http://slaysys.com/axkit_articles/weather/"
>
<data>
  <title><a name="title"/>My Weather Report</title>
  <time>
    <util:time format="%H:%M:%S" />
  </time>
  <weather>
    <weather:report>
      <!-- Get the ?zip=12345 from the URI and pass it
           to the weather:report tag as a parameter -->
      <weather:zip><param:zip/></weather:zip>
    </weather:report>
  </weather>
</data>
</xsp:page>

本系列文章

介绍AxKit
Barrie Slaymaker一系列文章的第一篇,关于设置和运行AxKit。AxKit是一个mod_perl应用程序,用于动态转换XML。在这第一篇文章中,我们专注于开始使用AxKit。

XSP、标签库和管道
Barrie解释了“标签库”是什么,以及如何使用它们在AxKit内部创建动态页面。

处理指令<?xml-stylesheet href="my_weather_taglib.xsl" type="text/xsl"?>会导致my_weather_taglib.xsl(我们将在下一篇文章中介绍)应用于XSP处理器看到的weather2.xsp页面之前。其他三个PI与上一版本相同:调用XSP处理器,然后是上次使用的相同的展示HTML化 XSLT样式表。

与上一版本相比的唯一其他更改是,这一版本使用了XSP标签的正确URI。我在上一篇文章中意外使用了已弃用的XSP标签URI,并在使用此LogicSheet的更新URI时遇到了麻烦。这就是尖括号迷的生活。

在不更改(大量)代码的情况下切换实现的能力是XSP相对于诸如内联Perl代码之类的东西的优势之一:实现与API(标签)的解耦做得很好。我们之所以需要更改weather1.xsp,唯一的理由是我们正在从配置在httpd.conf文件中的更高级的方法(一个标签库模块,My::WeatherTaglib)切换到LogicSheets,而使用<xml-stylesheet>样式表规范时需要每个文档的配置。AxKit有更灵活的httpd.conf、插件和基于Perl的样式表规范机制,我们将在未来的文章中介绍;我在这里使用处理指令,因为它们简单且明显。

由处理指令构建的管道看起来像

weather2.xsp的管道(不显示最终压缩阶段)。

my_weather_taglib.xsl

现在我们已经看到了源文档和整体管道,这里是My::WeatherTaglib重铸为LogicSheet,即my_weather_taglib.xsl

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xsp="http://apache.org/xsp/core/v1"
  xmlns:weather="http://slaysys.com/axkit_articles/weather/"
>

<xsl:output indent="yes" />

<xsl:template match="xsp:page">
  <xsl:copy>
    <xsp:structure>
      use Geo::Weather;
    </xsp:structure>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="weather:report">
  <xsp:logic>
    my $zip = <xsl:apply-templates select="weather:zip/*" />;
    my $w = Geo::Weather->new->get_weather( $zip );
    die "Could not get weather for zipcode '$zip'\n" unless ref $w;
  </xsp:logic>
  <state><xsp:expr>$w->{state}</xsp:expr></state>
  <heat><xsp:expr>$w->{heat}</xsp:expr></heat>
  <page><xsp:expr>$w->{page}</xsp:expr></page>
  <wind><xsp:expr>$w->{wind}</xsp:expr></wind>
  <city><xsp:expr>$w->{city}</xsp:expr></city>
  <cond><xsp:expr>$w->{cond}</xsp:expr></cond>
  <temp><xsp:expr>$w->{temp}</xsp:expr></temp>
  <uv><xsp:expr>$w->{uv}</xsp:expr></uv>
  <visb><xsp:expr>$w->{visb}</xsp:expr></visb>
  <url><xsp:expr>$w->{url}</xsp:expr></url>
  <dewp><xsp:expr>$w->{dewp}</xsp:expr></dewp>
  <zip><xsp:expr>$w->{zip}</xsp:expr></zip>
  <baro><xsp:expr>$w->{baro}</xsp:expr></baro>
  <pic><xsp:expr>$w->{pic}</xsp:expr></pic>
  <humi><xsp:expr>$w->{humi}</xsp:expr></humi>
</xsl:template>

<xsl:template match="@*|node()">
  <!-- Copy the rest of the doc almost verbatim -->
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

第一个<xsl:template>在页面顶部插入一个<xsp:structure>,其中包含一些Perl代码来use Geo::Weather;,这样后面的<xsl:logic>元素中的Perl代码就可以引用它。您还可以在httpd.conf中预加载Geo::Weather,以便在httpd进程之间共享它并简化此样式表,但这会引入一些维护困难:保持服务器配置和LogicSheet的同步。

第二个 <xsl:template> 替换了所有 <weather:report> 的出现(假设 weather: 前缀恰好映射到标签库URI;有关详细信息,请参阅 James Clark的命名空间介绍)。在 <weather:report> 标签(s)的位置,将有一些被 <xsp:logic><xsp:expr> 标签包围的Perl代码。<xsp:logic> 标签用于包围仅仅是逻辑的Perl代码:代码返回的任何值都将被忽略。被 <xsp:expr> 标签包围的Perl代码将返回一个值,该值将作为文本输出到结果文档中。

get_weather() 调用返回一个描述最近在给定邮编附近某处天气观测的散列表

{ ‘city’ => ‘Pittsburgh’, ‘state’ => ‘PA’, ‘cond’ => ‘Sunny’, ‘temp’ => ‘77’, … };

所有这些 <xsp:expr> 标签逐个从散列表中提取值并构建一个XML数据结构。生成的XSP文档看起来像

    <?xml version="1.0"?>
    <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"
 xmlns:util="http://apache.org/xsp/util/v1"
 xmlns:param="http://axkit.org/NS/xsp/param/v1"
 xmlns:weather="http://slaysys.com/axkit_articles/weather/">
      <xsp:structure>
          use Geo::Weather;
      </xsp:structure>
      <data>
        <title><a name="title"/>My Weather Report</title>
        <time>
          <util:time format="%H:%M:%S"/>
        </time>
        <weather>
          <xsp:logic>
            my $zip = <param:zip/>;
            my $w = Geo::Weather->new->get_weather( $zip );
            die "Could not get weather for zipcode '$zip'\n" unless ref $w;
          </xsp:logic>
          <state><xsp:expr>$w->{state}</xsp:expr></state>
          <heat><xsp:expr>$w->{heat}</xsp:expr></heat>
          <page><xsp:expr>$w->{page}</xsp:expr></page>
          <wind><xsp:expr>$w->{wind}</xsp:expr></wind>
          <city><xsp:expr>$w->{city}</xsp:expr></city>
          <cond><xsp:expr>$w->{cond}</xsp:expr></cond>
          <temp><xsp:expr>$w->{temp}</xsp:expr></temp>
          <uv><xsp:expr>$w->{uv}</xsp:expr></uv>
          <visb><xsp:expr>$w->{visb}</xsp:expr></visb>
          <url><xsp:expr>$w->{url}</xsp:expr></url>
          <dewp><xsp:expr>$w->{dewp}</xsp:expr></dewp>
          <zip><xsp:expr>$w->{zip}</xsp:expr></zip>
          <baro><xsp:expr>$w->{baro}</xsp:expr></baro>
          <pic><xsp:expr>$w->{pic}</xsp:expr></pic>
          <humi><xsp:expr>$w->{humi}</xsp:expr></humi>
        </weather>
      </data>
    </xsp:page>

而这个XSP页面的输出文档看起来像

    <?xml version="1.0" encoding="UTF-8"?>
    <data>
      <title><a name="title"/>My Weather Report</title>
      <time>17:06:15</time>
      <weather>
        <state>PA</state>
        <heat>77</heat>
        <page>/search/search?what=WeatherLocalUndeclared
        &where=15206</page>
        <wind>From the Northwest at 9 gusting to 16</wind>
        <city>Pittsburgh</city>
        <cond>Sunny</cond>
        <temp>77</temp>
        <uv>4</uv>
        <visb>Unlimited miles</visb>
        <url>http://www.weather.com/search/search?
        what=WeatherLocalUndeclared&where=15206</url>
        <dewp>59</dewp>
        <zip>15206</zip>
        <baro>29.97 inches and steady</baro>
        <pic>http://image.weather.com/web/common/wxicons/52/30.gif</pic>
        <humi>54%</humi>
      </weather>
    </data>

逻辑表优势

  • 一个标签库可以生成调用另一个标签库的XML。标签库模块可以在Perl级别相互调用,但标签库模块是XSP编译器插件,并且不会级联:XSP编译器生活在管道环境,但内部不使用管道。
  • 无需添加 AxAddXSPTaglib 指令,每次编写标签库时都无需重新启动Web服务器。

在某些环境中,仅仅因为标签库已更改而重新启动Web服务器可能有些尴尬,但这似乎很少见;在开发环境中重新启动Apache服务器通常很快,而且在生产环境中最好不太频繁地这样做。

在Cocoon社区中,逻辑表可以被注册和共享,这与Perl社区使用CPAN来共享模块的方式有些相似。这是Cocoon的一个额外好处,但在Perl世界并没有什么分量,因为Perl已经有CPAN(CPAN上有许多标签库模块)。目前还没有广泛使用的Java CPAN等价物,因此Cocoon逻辑表需要自己的机制。

逻辑表缺点

逻辑表有两个基本缺点,每个缺点都有几个症状。许多症状都是微不足道的,但它们加在一起

  1. 需要内联代码,通常在XSLT样式表中。
    • 在XML中放置Perl代码很麻烦:您无法轻松地进行语法检查(我在编写Perl代码时经常运行 perl -cw ThisFile.pm)或利用面向语言的编辑器功能,如自动缩进、标签和语法高亮。
    • 标签库作者需要在四种语言/API上工作:XSLT(通常是)、XSP、Perl以及正在开发的标签库。XSLT和Perl远远不是微不足道的,尽管XSP相当简单,但在它们之间进行上下文切换时很容易出错。
    • 逻辑表远不如标签库模块灵活。例如,将 my_weather_taglib.xsl 的输出结构与 My::WeatherTaglib 或 My::SimpleWeatherTaglib 的结构进行比较。逻辑表方法需要硬编码结果值,而这两个标签库模块只是将天气报告数据结构中的任何内容转换为XML。
    • XSLT需要相当多的额外样板代码来复制XSP页面上的非标签库部分。这通常可以设置为样板代码,但程序中的样板代码只是另一种妨碍和维护的东西。
    • 逻辑表本质上是单用途的。另一方面,标签库模块可以用作常规Perl模块。例如,认证模块可以用作标签库和常规模块。
    • LogicSheets 需要一个可工作的 Web 服务器来进行最基本的测试,因为它们需要在 XSP 环境中运行,而 AxKit 目前还不支持 Web 服务器之外的 XSP。编写 taglib 模块允许编写简单的测试套件来检查 taglib 的代码,而不需要可工作的 Web 服务器。
    • 在 XML 编辑器中编写 LogicSheets 效果最佳,否则您至少需要转义所有的 < 字符,阅读和编写 XML 转义过的 Perl 和 Java 代码可能会令人不悦。
    • 接受和扩展 LogicSheet 是一件困难的事情:源 XSP 页面需要知道它所使用的 taglib 正在使用基本 taglib,并声明它们的命名空间。使用 taglib 模块,可以使用 Perl 的标准函数导入机制来减轻 XSP 编写者的这项责任。
  2. 需要额外的样式表进行处理,通常是 XSLT。这意味着
    • 更复杂的处理链,这会导致 XSP 页面复杂度增加(从而增加了出现错误的可能性),因为每个页面都必须声明 taglib 标签的命名空间和运行 taglib 的处理指令。例如,我在 weather2.xsp 中使用了过时的 XSP 命名空间 URI,而在 my_weather_taglib.xsl 中使用了当前的 URI。这让我有些困惑,但 AxTraceIntermediate 指令帮助我澄清了一些问题。
    • 每次服务 XSP 页面时都需要检查更多磁盘文件以确定是否有更改。由于每个 LogicSheet 都会影响输出,因此每个 LogicSheet 都必须通过 stat() 检查,以确定它自上次 XSP 页面编译以来是否已更改。

如您所知,我认为 LogicSheets 是一种比使用辅助库将 taglibs 编写为 Perl 模块更为笨拙且灵活性较差的方法。尽管如此,使用上游 LogicSheets 是一种有效且可能偶尔有用的技术,用于编写 AxKit taglibs。

上游过滤器有什么好处?

那么,XSP 处理器上游的 XSLT 有什么好处?您可以用它来做很多除了实现 LogicSheets 以外的事情。一种用途是实现品牌:更改像标志、站点名称和颜色这样的内容,或者进行其他自定义,例如在多个子站点共享的登录页面上的管理员电子邮件地址。

在 XSP 处理器上游进行转换的关键优势是,XSP 进程缓存了上游转换的结果。XSP 将其接收到的任何文档转换为内存中的 Perl 字节码,然后仅在没有任何上游文档更改的情况下运行该字节码。

另一种用途是将声明页面应包含什么的源文档转换为实现页面机械的 XSP 文档。例如,一个调查网站可能有其源文档声明要提出的问题。

    <survey>
      <question>
        <text>Have you ever eaten a Balut</text>
        <response>Yes</response>
        <response>No</response>
        <response>Eeeewww</response>
      </question>
      <question>
        <text>Ok, then, well how about a nice haggis</text>
        <response>Yes</response>
        <response>No</response>
        <response>Now that's more like it!</response>
      </question>
      ...
    </survey>

可以使用 XSLT 将调查定义转换为使用 PerForm taglib 的 XSP 页面,以自动化表单填写等。这种方法允许页面根据其内容定义,而不是根据其工作方式定义。

您还可以在 XSP 处理器上游使用 XSLT 做其他事情,例如将有限或简单的领域特定标记集转换为更复杂或通用目的的 taglib,该 taglib 被编写为 taglib 模块。这允许您定义更易于使用(但令人恐惧!)的 taglib,这些 taglib 被加载到 XSP 处理器中。

My::SimpleWeatherTaglib

在最近AxKit版本中捆绑了一个新的标签库辅助模块:Jörg Walter的SimpleTaglib(完整模块名为Apache::AxKit::Language::XSP::SimpleTaglib)。这个模块的功能大致与Steve Willer的TaglibHelper相同,但支持命名空间,并使用Perl的新特性,子程序属性,来指定参数和结果格式化,而不是字符串。

以下为My::SimpleWeatherTaglib

    package My::SimpleWeatherTaglib;

    use Apache::AxKit::Language::XSP::SimpleTaglib;

    $NS = "http://slaysys.com/axkit_articles/weather/";

    package My::SimpleWeatherTaglib::Handlers;

    use strict;
    require Geo::Weather;

    ## Return the whole report for fixup later in the processing pipeline
    sub report :  child(zip) struct({}) {
        return 'Geo::Weather->new->get_weather( $attr_zip );'
    }

    1;

$NS变量定义了这个标签库的命名空间。此模块使用与my_weather_taglib.xsl和My::WeatherTaglib相同的命名空间,因为这三个都实现了相同的标签库(这种重复是为了展示不同方法之间的差异)。参见混合和匹配标签库部分,了解My::WeatherTaglib和My::SimpleWeatherTaglib如何同时用于同一个服务器实例。

My::SimpleWeatherTaglib随后切换到一个新的package,My::SimpleWeatherTaglib::Handlers,以定义标签库标签的子程序。使用这样一个原始包可以提供一个干净的声明标签处理程序的地方。SimpleTaglib如果被Foo包中的use()使用,则会在Foo::Handlers包中查找模块(不要为此使用require!)。

My::SimpleWeatherTaglib require了Geo::Weather并声明了一个标签,它处理weather1.xsp中的<weather:report>标签(我们稍后展示)。

使用require Geo::Weather;而不是use Geo::Weather;是为了避免将子程序导入到我们的...::Handlers命名空间,这可能会像处理程序一样。

sub report的声明中有些新内容:子程序属性。子程序属性是Perl(从perl5.6起)的一个新特性,它允许我们在子程序声明上附加一些额外的信息,以更详细地描述它。《perldoc perlsub》提供了此语法的详细信息。一些属性是由Perl预定义的,但模块也可以为其自己的目的定义其他属性。在这种情况下,SimpleTaglib模块定义了一些属性,其中一些描述了标签库标签可以接受哪些参数,其他一些描述了如何将标签库实现的结果值转换为XML输出。

child(zip)子程序属性告诉SimpleTaglib模块,此处理程序期望在标签库的命名空间中有一个名为zip的单个子元素。在weather1.xsp中,它最终看起来像这样:

    <weather:report>
      <!-- Get the ?zip=12345 from the URI and pass it
           to the weather:report tag as a parameter -->
      <weather:zip><param:zip/></weather:zip>
    </weather:report>

<weather:zip>元素中的文本(将使用param:标签库从URI查询字符串中填充)将在请求时间以名为$attr_zip的变量提供。从元素中显示的文本出现在以$attr_开头的变量中可能令人困惑,但它确实如此工作。

struct({})属性指定此标签的结果将以Perl数据结构返回,该数据结构将被转换为XML。

{ ‘city’ => ‘Pittsburgh’,‘state’ => ‘PA’,‘cond’ => ‘Sunny’,‘temp’ => ‘77’,… };

struct属性告诉SimpleTaglib将其转换为XML,如下所示:

    <city>Pittsburgh</city>
    <state>PA</state>
    <cond>Sunny</cond>
    <temp>77</temp>
    ....

struct({})属性中的花括号指定结果节点不应在命名空间中(因此没有命名空间前缀),就像我们的weather1.xsp文档的静态部分一样。这是SimpleTaglib相对于其他方法的优点之一:更容易在不同的命名空间中发出节点。要在特定命名空间中发出节点,将那个命名空间的命名空间URI放在括号内:struct({http://my.namespace.com/foo/bar})。花括号表示法被称为James Clark(或jclark)表示法

现在,让我们来看看复杂的地方。回顾一下我们关于如何实现XSP的讨论(如何实现XSP),记住XSP处理器将XSP文档编译成Perl代码,用于生成输出文档。当XSP编译页面时,它会寻找在AxAddXSPTaglib中配置的taglib模块处理的命名空间中的标签。当XSP看到这些标签之一时,它会调用该命名空间下的taglib模块——在这里是My::SimpleWeatherTaglib——请求一段Perl源代码,用以替换标签。

在此处介绍的SimpleTaglib模块实现的taglib覆盖了每个标签的处理程序(例如,sub report)。这个处理程序子例程在解析时被调用,而不是在请求时。它的任务是返回将要编译并稍后运行的代码块。因此,report()返回一个包含调用Geo::Weather的Perl代码片段的字符串。这段Perl代码将只编译一次,然后在每次请求时运行。

这是My::WeatherTaglib在前一篇文章中使用的TaglibHelper模块和这里使用的SimpleTaglib模块之间的一个关键区别。SimpleTaglib在编译时调用My::SimpleWeatherTaglib的report()子例程,而TaglibHelper则在请求时自动、安静地安排调用My::WeatherTaglib的report()子例程。

这种差异使得SimpleTaglib并不简单,除非你习惯于编写生成将编译并稍后运行的代码的代码。另一方面,“编写程序的程序是世界上最快乐的程序”(据网上一些地方引用的Andrew Hume)。在这里这是真实的,因为我们能够返回适合当前任务所需的任何代码。在这种情况下,代码非常简单,我们可以直接返回它。如果要做的工作更复杂,我们也可以返回对我们自己设计的子例程的调用。所以,尽管比TaglibHelper的方法简单得多,但这种方法确实提供了一些灵活性。

SimpleTaglib的作者承诺,SimpleTaglib的新版本将提供“在请求时调用此子例程”的API,这在大多数情况下是我(以及我怀疑大多数人)更愿意使用的。

我要提醒你,SimpleTaglib的文档并不独立,因此你需要一些示例模块的源代码来将其全部组合起来。除了这里过于简单的示例之外,文档还指向其他几个示例。请记住,我在我的玻璃房子里扔石头,因为没有人指责我完全记录了我的模块。

为了参考,以下是前一篇文章中的weather1.xsp,我们在这里直接重用它作为示例

    <?xml-stylesheet href="NULL"        type="application/x-xsp"?>
    <?xml-stylesheet href="weather.xsl" type="text/xsl"?>
    <?xml-stylesheet href="as_html.xsl" type="text/xsl"?>

    <xsp:page
        xmlns:xsp="http://www.apache.org/1999/XSP/Core"
        xmlns:util="http://apache.org/xsp/util/v1"
        xmlns:param="http://axkit.org/NS/xsp/param/v1"
        xmlns:weather="http://slaysys.com/axkit_articles/weather/"
    >
    <data>
      <title><a name="title"/>My Weather Report</title>
      <time>
        <util:time format="%H:%M:%S" />
      </time>
      <weather>
        <weather:report>
          <!-- Get the ?zip=12345 from the URI and pass it
               to the weather:report tag as a parameter -->
          <weather:zip><param:zip/></weather:zip>
        </weather:report>
      </weather>
    </data>
    </xsp:page>

处理流程和中间文件也与前一篇文章中的相同,所以这里不再重复。

使用httpd.conf混合和匹配Taglibs

正如本系列第一篇文章中详细描述的,AxKit与Apache和Apache的配置引擎紧密结合。Apache允许不同的文件和目录应用不同的配置,包括使用的taglib。在实际应用中,有时需要网站的一部分使用可能破坏旧部分的taglib的新版本。

例如,我在这篇文章的示例中使用的服务器中,02/目录仍然使用前一篇文章中的My::WeatherTaglib,而03/目录则使用本文章的其中一个示例的my_weather_taglib.xsl和My::SimpleWeatherTaglib。这是通过结合Apache的<Directory>部分和AxAddXSPTaglib指令来实现的。

## ## 初始化 httpd 以使用我们的“私有安装”库 ## PerlRequire startup.pl

    ##
    ## AxKit Configuration
    ##
    PerlModule AxKit

    <Directory "/home/me/htdocs">
        Options -All +Indexes +FollowSymLinks

        # Tell mod_dir to translate / to /index.xml or /index.xsp
        DirectoryIndex index.xml index.xsp
        AddHandler axkit .xml .xsp

        AxDebugLevel 10

        AxTraceIntermediate /home/me/axtrace

        AxGzipOutput Off

        AxAddXSPTaglib AxKit::XSP::Util
        AxAddXSPTaglib AxKit::XSP::Param

        AxAddStyleMap application/x-xsp \\
                      Apache::AxKit::Language::XSP

        AxAddStyleMap text/xsl \\
                      Apache::AxKit::Language::LibXSLT
    </Directory>

    <Directory "/home/me/htdocs/02">
        AxAddXSPTaglib My::WeatherTaglib
    </Directory>

    <Directory "/home/me/htdocs/03">
        AxAddXSPTaglib My::SimpleWeatherTaglib
    </Directory>

请参阅 Apache httpd 文档(v1.3 或 2.0)中的目录、位置和文件部分的说明,以了解如何使用 <Directory> 和其他 httpd.conf 部分来完成此类操作。

帮助与感谢

Jörg Walter 以及 Matt Sergeant 在撰写本文时给予了极大的帮助,尤其是在我不熟悉 LogicSheets 的情况下。Jörg 还在极短的时间内修复了一个错误,并编写了 SimpleTaglib 模块和 AxTraceIntermediate 功能。

如有问题,请参阅我们在第一篇文章中列出的一些有用的资源

版权所有 2002,Robert Barrie Slaymaker, Jr. 保留所有权利。

标签

反馈

这篇文章有什么问题吗?请在GitHub上打开一个 issue 或 pull request 来帮助我们。