[转载] 使用Maypole进行快速Web应用开发

转载自中国Perl协会 用户推广组(Foundation of Perlchina)
翻译者:Joe Jiang
原文作者:Simon Cozens
原文地址:http://www.perl.com/pub/a/2004/04/15/maypole.html --April 22, 2004


You have a database. You have a web server. You have a deadline.

Whether it's bringing up an e-commerce storefront for a new venture, implementing a new front-end to HR's employee database, or even providing a neat way to track citations for U.S. English slang terms, it's always the same story -- and the deadline is always yesterday.

你有个数据库,你有个Web服务器,你有个最后期限。

无论是将一个为一个新的机遇设计e-commerce的橱窗,或者为人事部门的数据库设计一个新的界面,或甚至是为美国英语俚语用法提供一个跟踪的方法,这都是一回事,而且最后期限总是昨天。

For this month of April, I'm working on a Perl Foundation sponsorship to develop a project of mine called Maypole, which enables Perl programmers to get web front-ends to databases, as well as complex web-based applications, up and running quickly.

今年4月份我在为perl基金会工作来开发一个名叫Maypole的项目,这个项目是的perl程序员可以为数据库提供web界面,也就是一个基于web的应用程序,上手和运行都很快。

Extremely quickly, and with very little Perl coding required. I've used Maypole to set up an Intranet portal, a database and display system for choosing menus and recipes, song lyric and chord sheet projection software, an open-source social network site, and a web database of beer-tasting notes; and that just was in the past two weeks.

快的不得了,也只需要些很少的perl代码,我就能够用maypole来建立一个内网的入口,一个数据库和显示系统用于选择菜单和菜谱,歌词琴谱投影软件,一个开源社会网络的站点,一个啤酒品味web数据库,所有这些只在两周以内完成。

Maypole's flexibility stems from three fundamentals:

   * Clear separation of concerns
   * Intelligent defaults
   * Ease of extensibility

Maypole的灵活性来源于三个基础:

   * 清晰的概念区分
   * (敏感的)人性化的默认值
   * 容易扩展
To demonstrate these three principles, we're going to look at a bread-and-butter web application -- an online shop's product catalogue -- and see how quickly we can put it together with Maypole.

为了清晰的解释这三个概念,我们马上开始看一个快餐式的web应用(一个网上商店的产品目录),并看看我们能用多久完成Maypole的拼装。


Separation of Concerns
清晰的概念区分


Maypole was originally called Apache::MVC, reflecting its basis in the Model-View-Controller design pattern. (I had to change it firstly because Maypole isn't tied to Apache, and secondly because Apache::MVC is a really dull name.) It's the same design pattern that forms the foundation of similar projects in other languages, such as Java's Struts framework.

开始Maypole叫做Apache::MVC,这反应了它是基于M-V-C的模式的。(我必须改掉这个名字首先是因为Maypole不是捆绑在 Apache上的,而且Apache::MVC也实在是个土名字。)也就是这个模式在很多类似的语言中被很多项目使用,例如Java的Structs框架。

This design pattern is found primarily in graphical applications; the idea is that you have a Model class that represents and manipulates your data, a View class that is responsible for displaying that data to the user, and a Controller class that controls the other classes in response to events triggered by the user. This analogy doesn't correspond precisely to a web-based application, but we can take an important principle from it. As Andy Wardley explains:

这个设计模式在很多图形软件中被广泛使用,这个主意的核心是用一个模板来表达和处理数据,用一个视图来负责显示数据给用户看,用一个控制类来保证用户事件正确驱动其他类的活动。这个比喻不是完全准确的映射到web应用,但是我们可以得到一个重要的概念。Andy Wardley这么解释:


What the MVC-for-the-web crowd is really trying to achieve is a clear separation of concerns. Put your database code in one place, your application code in another, your presentation code in a third place. That way, you can chop and change different elements at will, hopefully without affecting the other parts (depending on how well your concerns are separated, of course). This is common sense and good practice. MVC achieves this separation of concerns as a byproduct of clearly separating inputs (controls) and outputs (views).

M-V-C web的各种形式真正力图实现的是概念上的清晰分离。把数据库代码放在一处,应用代码在另一处,表示相关的代码在第三处。这样你可以任意的调整每个元素,不用担心影响到其他部分(当然还要取决于你如何有效的分解问题)。这通常是一个好的尝试。通过有效的分格输入和输出(控制和视图),MVC的副产品就是达到概念分离的目标。

This is what Maypole does. It has a number of database drivers, a number of front-end drivers, and a number of templating presentation drivers. In common cases, Maypole provides precisely what you need for all of these areas, and you get to concentrate on writing just the business logic of your application. This is one of the reasons why Maypole lets you develop so rapidly -- because most of the time, you don't need to do any development at all.

这就是Maypole所做的,他有很多的数据库驱动,很多前端驱动和很多模板表示的驱动。通常情况下,Maypole总是能够在这些领域精确的表达你的要求,你也可以专心编写应用程序的业务逻辑。这就是为什么Maypole能够让你快速开发的原因(大多数情况下你都不需要做任何开发)。

Let's begin, then, by choosing what elements are going to make up our product database. We will actually be using what is by far the most common configuration of model, view, and controller classes: Maypole provides a model class based on Class::DBI, a view class based on Template::Toolkit, and a controller class based on Apache mod_perl. We'll come to what all of this means in a second, but because this configuration is so common, it is the default; no code is required to set that up.

那么让我们开始吧,首先是挑选我们要用来实现产品数据库的技术元素。我们实际上会用目前来说最常用的模型,视图,控制的配置。 Maypole提供了一个基于Class::DBI的模型,基于Template::Toolkit的视图和基于Apache mod_perl的控制。我们会稍后介绍这些东西的具体含义,但是现在因为这个配置是如此通用,所以他是默认的配置,也不需要为了达到这个而写任何代码。

We will, however, need a database. Our client is going to be iSellIt, a fictitious supplier of computer components and software. We will have database tables for products, manufacturers, and categories of stuff, and subcategories of categories. Here's what that database might look like.

我们还需要一个数据库。我们的假想客户名字叫做iSellIt,一个计算机设备和软件供应商。我们需要有产品表,供应商表,产品目录,次分类目录。这就是数据库看起来的样子。


CREATE TABLE product (
   id int NOT NULL auto_increment primary key,
   category int,
   subcategory int,
   manufacturer int,
   part_number varchar(50),
   name varchar(50),
   cost decimal(6,2),
   description text
);

CREATE TABLE manufacturer (
   id int NOT NULL auto_increment primary key,
   name varchar(50),
   url varchar(255),
   notes text
);

CREATE TABLE category (
   id int NOT NULL auto_increment primary key,
   name varchar(50)
);

CREATE TABLE subcategory (
   id int NOT NULL auto_increment primary key,
   name varchar(50),
   category integer
);

We're going to assume that we've loaded some data into this database already, but we're going to want the sales people to update it themselves over a web interface.

我们假定数据已经被加到数据库中了,但是我们还需要销售人员可以用一个web界面来更改数据库内容。

In order to use Maypole, we need what's called a driver module. This is a very short Perl module that defines the application we're working with. I say it's a Perl module, and that may make you think this is about writing code, but to be honest, most of it is actually configuration in disguise. Here's the driver module for our ISellIt application. (The client may be called iSellIt, but many years exposure to Perl module names makes me allergic to starting one with a lowercase letter.)

为了使用Maypole,我们需要一个驱动模块。这是一段非常短的用来定义我们的应用程序的模块。我说模块可能会让你们误认为是和写代码想关的,但是实际上主要的是隐性的配置。这就是我们的ISellIt应用程序的驱动模块。(这个客户端可以叫做iSellIt,但是多年和perl模块打交道使我习惯于用小写字母打头命名)

package ISellIt;
use base 'Apache::MVC';
use Class::DBI::Loader::Relationship;

ISellIt->setup("dbi:mysql:isellit");
ISellIt->config->{uri_base} = "http://localhost/isellit";
ISellIt->config->{rows_per_page} = 10;
ISellIt->config->{loader}->relationship($_) for
 ("a manufacturer has products", "a category has products",
  "a subcategory has products", "a category has subcategories");

1;

Ten lines of code; that's the sort of size you should expect a Maypole application to be. Let's take it apart, a line at a time:

十行代码,这是你可以盼望的Maypole应用的大小。下面我们一行行地解释:
package ISellIt;

This is the name of our application, and it's what we're going to tell Apache to use as the Perl handler for our web site.

这是我们的应用程序的名字,也是我们要告诉我们web服务器上的Apache使用的Perl服务的名字。

use base 'Apache::MVC';


This says that we're using the Apache front-end to Maypole, and so we're writing a mod_perl application.

这告诉大家我们用Apache作为Maypole的前端,这样我们就可以开始写一个mod_perl的程序了。

use Class::DBI::Loader::Relationship;

Now we use a Perl module that I wrote to help put together Maypole driver classes. It allows us to declare the relationships between our database tables in a straightforward way.

现在我们用一个我编写的Perl模块来把Maypole的驱动类协调在一起。这是我们可以相当直白的描述我个的数据库中表的关系。

ISellIt->setup("dbi:mysql:isellit");

We tell ISellIt to go connect to the database and work out the tables and columns in our application. In addition, because we haven't changed any class defaults, it's assumed that we're going to use Class::DBI and Template Toolkit. We could have said that we want to use Apache::MVC with DBIx::SearchBuilder and HTML::Mason, but we don't.

我们让ISellIt连接数据库而且自己分析出表和字段的情况。并且因为我们还没有改变类的默认配置,就可以肯定的说我们会使用Class:: DBI和Template Toolkit。我们也可以说要是用Apache::MVC和DBIx::SearchBuilder和HTML::Mason,但是没说。

Maypole's Class::DBI-based class uses Class::DBI::Loader to investigate the structure of the database, and then map the product table onto a ISellIt::Product class, and so on. You can read more about how Class::DBI's table-class mapping works in Tony's article about it.

Maypole的基于Class::DBI的类使用Class::DBI::Loader来调查数据库的结构,并且把里面的产品表映射到 ISellIt::Product类,其他表的也一样。你可以在Tony的文章里面读到更多关于Class::DBI的表和类的映射的信息。

ISellIt->config->{uri_base} = "http://localhost/isellit";

ISellIt sometimes needs to know where it lives, so that it can properly produce links to other pages inside the application.

有时ISellIt也需要知道他在那个URL地址运行的,这样可以正确的在应用内部产生链接。

ISellIt->config->{rows_per_page} = 10;

This says that we don't want to display the whole product list on one page; there'll be a maximum of 10 items on a page, before we get a page-view of the list.

这行说我们不打算在一页里显示所有的产品,而是最多每页十个。

ISellIt->config->{loader}->relationship($_) for
 ("a manufacturer has products", "a category has products",
  "a subcategory has products", "a category has subcategories");

Now we define our relationship constraints, in reasonably natural syntax: a manufacturer has a number of products, and a category will delimit a collection of products, and so on.

现在我们用理想的自然语言定义关系中的约束,一个供应商有多个产品,一个分类目录有多个产品,如此类推。

Ten lines of code. What has it got us?

十行代码,这产生什么结果?


Sensible Defaults
敏感的默认行为

The second foundation of Maypole is its use of sensible defaults. It has a system of generic templates that "do the right thing" for viewing and editing data in a database. In many cases, web application programmers won't need to change the default behavior at all; in the majority of cases, they only need to change a few of the templates, and in the best cases, they can declare that the templating is the web design group's problem and not need to do any work at all.

Maypole的第二个根基是使用了敏感的默认行为。他用一个通用模板系统来很好的支持浏览和编辑库里的数据。大多数情况下,web应用都不需要改变它的默认行为。绝大多数情况下,可以稍微改掉模板的中的几个就可以。精华在于程序员可以说这是web设计组的事情然后就不用做任何工作了。

So, if we install the application and the default templates, and go to our site, http://localhost/isellit; we should see this:

好,既然我们已经把应用和默认的模板安装上去,就可以用http://localhost/isellit来登录网站。就会看到:
http://www.perl.com/2004/04/21/graphics/maypole1.png

Which is only fair for 10 lines of code. But it gets better, because if we click on, say, the product listing, we get a screen like so:

这区区十行代码就做到这么多,而且还有更精彩的,如果我们在产品列表上点击,就会去到这个屏幕:
http://www.perl.com/2004/04/21/graphics/maypole2.png
Now that's something we could probably give to the sales team with no further alterations needed, and they could happily add, edit, and delete products.

现在我们已经可以把这些给销售人员使用了,他们不用做更多的工作就可以很愉快的增添,修改,删除产品了。

Similarly, if we then click on a manufacturer in that products table, we see a handy page about the manufacturer, their products, and so on:

类似的,如果我们在产品列表中点击供应商的链接就会看到一个很乘手的关于供应商的页面。里面有他们所有的产品。还可以依此类推。
http://www.perl.com/2004/04/21/graphics/maypole3.png

Now I think we are getting some worth from our 10 lines. Next, we give the templates to the web designers. Maypole searches for templates in three different places: first, it looks for a template specific to a class; then it looks for a custom template for the whole application; finally, it looks in the factory directory to use the totally generic, do-the-right-thing template.

我想写这十行代码应该值了。然后我们让web设计人员来处理模板。Maypole在三个不同的地方搜索模板,首先看看有没有和类相关的模板,然后看看有没有应用相关的模板,最后就用模板工厂的默认的模板。

So, to make a better manufacturer view, we tell them to copy the factory/view template into manufacturer/view and customize it. We copy factory/list into product/list and customize it as a listing of products; we copy factory/header and factory/footer into the custom/ directory, and turn them into the boilerplate HTML surrounding every page, and so on.

这样的话,我们可以让他们把factory/view模板拷贝成供应商模板而且定制修改一下。我们把factory/list拷贝到 product/list定制修改一下,也把factory/header和factory/footer拷贝到custom/目录,然后把他们做成每个 HTML页面的框架,如此类推。

Now, I am not very good at HTML design, which is why I like Maypole -- it makes it someone else's problem -- but this means I'm not very good at showing you what sort of thing you can do with the templates. But here's a mock-up; I created product/view with the following template:

目前我还不怎么擅长HTML设计,这也是我喜欢Maypole的原因,它让别人来帮你解决问题,但这也意味着我没法有效的展示可以用模板做到的境界。但是有个例外,我创建了一个带有下面模板的product/view。

[% INCLUDE header %]
[% PROCESS macros %]

<DIV class="nav"> You are in: [% maybe_link_view(product.category) %] >
[% maybe_link_view(product.subcategory) %] </DIV>

<h2> [% product.name %]</h2>
<DIV class="manufacturer"> By [% maybe_link_view(product.manufacturer) %]
</DIV>
<DIV class="description"> [% product.description %] </DIV>

<TABLE class="view">
<TR>
 <TD class="field"> Price (ex. VAT) </TD>
 <TD> £ [% product.cost %] </TD>
</TR>
<TR>
 <TD class="field"> Part number </TD>
 <TD> [% product.part_number %] </TD>
</TR>
</TABLE>

[% button(product, "order") %]


Producing the following screenshot. It may not look better, but at least it proves things can be made to look different.
从下面的屏幕截图可以看到结果。他可能说不上更好,但起码证明了template可以使视觉效果有所不同。
[img]http://www.perl.com/2004/04/21/graphics/maypole4.png[/img]
We've written a Template Toolkit template; the parts surrounded in [% ... %] are templating directives. If you're not too familiar with the Template Toolkit, the Maypole manual's view documentation has a good introduction to TT in the Maypole context.
我们已经写了一个Template Toolkit的模板,在[% ... %]之间的部分是模板的打头标致。如果你不熟悉Template Toolkit,Maypole手册的视图部分是个好的简介(在Maypole中用TT)。
Maypole provides a number of default Template macros, such as maybe_link_view, which links an object to a page viewing that object, although all of these can be overridden. It also passes in the object product, which it knows to be the one we're talking about.
Maypole支持一些默认的Template宏,比如maybe_link_view,这个宏把对象和页面链接起来,当然所有这些也都可以覆盖。这个宏还参与到对象的产生过程,也就是它知道我们正在输出的信息是普通信息还是对象(若是对象就产生链接)。
In fact, that's what Maypole is really about: we've described it in terms of putting a web front-end onto a database, but fundamentally, it's responsible for using the URL /product/view/210 to load up the product object with ID 210, call the view method on its class, and pass it to the view template. Similarly, /product/list calls the list method on the product class, which populates the template with a page full of products.

实际上,这就是Maypole的核心,我们虽然主要说的是为数据库产生web界面,但是实际上他也可以为 /product/view/210这样的URL提供读出产品编号为210的服务(调用相应的显示方法并套上模板)。以此类推, /product/list调用产品类的list方法,并为所产生的一页产品列表套用模板。
The interesting thing about this template is that very last line:
这个模板有趣的地方就是最后一行:

[% button(product, "order") %]


This produces a button which will produce a POST to the URL /product/order/210, which does the same as view except this time calls the order method. But Maypole doesn't yet know how to order a product. This is OK, because we can tell it.

这就产生了一个链接到/product/order/210这个URL的调用,这和视图所做的类似,区别只在于调用的方法是order。Maypole虽然不知道如何订购一个产品,但是我们可以告诉它。


Ease of Extensibility
易于扩展


Maypole's third principle is ease of extensibility. That is to say, Maypole makes it very easy to go from a simple database front-end to a full-fledged web application. Which is just as well; as has been simulated above, once the templates come back from the web designers, you find that what you thought was just going to be a product database has become an online shop. And you've still got a deadline.

Maypole的第三个原则是易于扩展。也就是说Maypole使得为数据库提供一个羽翼丰满的web界面非常容易。但是有可能在web设计人员做好了模板以后,需求从产品数据库变成了网上商店,你又回到了最后期限的烦恼中。

But before we start extending our catalogue application to take on the new specifications (which we'll do in the second article about this), let's take a look at what we've achieved so far and what we need immediately.

但是在我们开始把这个产品目录应用程序扩展以完成新的需求之前(我们会有另外一篇文章描述这个),我们看看目前为止我们已经做到了什么和需要立即增加什么。

We've got a way to list all the products, manufacturers, categories, and subcategories in our database; we have a way to add, edit and delete all of these things; we can search for products by manufacturer, price, and so on. What's to stop us deploying this as a customer-facing web site, as well as for Intranet updates to the product catalogue?
我们有个为数据库中产品,供应商,分类和子类提供列表的方法,也可以增加,修改,删除所有的东西。还可以搜索供应商提供的产品的价格等等。阻挠我们把这个应用安装在web上或者Intranet上的东西是什么呢?
The immediate problem is security. We can add, edit, and delete products -- but so can anyone else. We want to allow those coming from the outside world only to view, list and search; for everything else, we require the user to be coming from an IP address in our internal range. (For now; we'll add the concept of a user when we're adding the shopping cart, and the idea of privileged user won't be far off that.)
最明显的问题就是安全性了,我们可以增/删/改所有产品,可是其他的任何人也可以。我们需要允许外网的人自能看和查找,对内网受限的IP地址范围的人才提供所有的服务。(现在因为需要购物车,我们有了用户的概念,特权用户的概念也很接近了)
Unfortunately, now we want some user-defined behavior, we have to start writing code. Thankfully, we don't have to write much of it. We add a few lines to our driver class, first to define our private IP address space as a NetAddr::IP object, since that provides a handy way of determining if an address is in a network:
不幸的是,现在我们有了一个用户定制的行为,所以必须开始写代码了,好在不会很多代码。我们在驱动类里面增加几行,首先是为了设置授权用户的IP地址范围,这可以用NetAddr::IP来实现。这个类提供了判断地址是否位于某个范围的乘手的方法。

use constant INTERNAL_RANGE => "10.0.0.0/8";
use NetAddr::IP;
my $range = NetAddr::IP->new(INTERNAL_RANGE);

Now we write our authentication method; Maypole's default authenticate allows everyone access to everything, so we need to override this.
下面我们来编写客户鉴权方法,Maypole的默认鉴权方法允许每个人使用每样功能,我们需要重新定义。

use Maypole::Constants;
sub authenticate {
 my ($self, $r) = @_;
 # Everyone can view, search, list
 return OK if $r->action =~ /^(view|search|list)$/;
 # Else they have to be in the internal network
 my $ip = NetAddr->IP->new($r->{ar}->connection->remote_ip);
 return OK if $ip->within($range);
 return DECLINED;
}

The authenticate class method gets passed a Maypole request object; this is like an Apache request object, but at a much, much higher level -- it contains information about the web request, the class that's going to be used to fulfill the request, the method we need to call on the class, the template that's going to be processed, any objects, form parameters, and query parameters, and so on.
这个鉴权方法过滤一个Maypole请求对象,有点像Apache的request对象,但是更加高级点(包含一个web请求的类所提供的信息和可调用方法,还有待处理的模板和form参数,以及查询参数等等)。
At this point, Maypole has already parsed the URI into its component database table, action, and additional arguments, so we first check to see if the action is one of the universally permitted ones.
在此Maypole已经把URI转化成行为,相关的表,和额外的参数。这样我们就可以判断这个行为是不是每个人都许可的。
If not, we extract the Apache::Request object stashed inside the Maypole object, and ask it for the remote IP address. If it's in the private range, we can do everything. If not, we can do nothing. Simple enough.
若不是人人许可的行为,我们就拿出内嵌的Apache::Request对象并且看看他的远端地址。如果是在许可范围的,就允许他所有的操作,否则拒绝,就是这么简单。
It's almost ready to go live, when the design guys tell you that they'd really love to put a picture alongside the description of a product. No problem.
就在万事具备的时候,设计人员让你在产品描述里增加一个图片信息,没问题。
There's two ways to do this; the way that seems really easy uses the file system to store the pictures, and has you put something like this in the template:
有两个方法可用,看上去比较容易的方案是用文件系统来存储图片,并用模板来增加图片链接。
But while that's very simple for viewing pictures, and makes a great mockup, it's not that easy to upload pictures. So you decide to put the pictures in the database. You add a "picture" binary column to the product table, and then you consult the Maypole manual.
这虽然使得看图片变得非常容易,但也有把事情弄糟的时候,因为上传图片并不容易。这就使你决定要用数据库在存储图片。在产品表里面增加一个picture的二进制列,然后你就开始看Maypole的手册。
One of the great things about this Perl Foundation sponsorship is that it's allowing me to put together a really great manual, which contains all sorts of tricks for dealing with Maypole; the Request chapter contains a couple of recipes for uploading and displaying photos.
Maypole项目是Perl基金会赞助的这件事的好处在于我可以写很好的手册,这里面有使用Maypole的每个技巧。Request这个章节有不少上传和显示图片的秘笈。
What we need to do is create some new actions -- one to upload a picture, and one to display it again. We'll only show the one to display a picture, since you can get them both from the manual, and because looking at this turns out to be a handy way to understand how to extend Maypole more generally.
目前我们需要几个动作,一是上传,一是显示。我们下面只做这些因为你不但可以看手册,而且这些就足够你很简单的了解如何更进一步的扩展Maypole。
It's useful to visualize what we're going to end up with, and work backwards. We'll have a URL like /product/view_picture/210 producing an image/png or similar page with the product's image. This allows us to put in our templates:
最好是先让大家看到我们要做的结果再反过来解释如何做。我们的/product/view_picture/210这个URL会产生一个带有image/png的页面来展示产品。这也使我们可以在模板中加入如下的:
And have the image displayed on our product view page. In fact, we're more likely to want to say:
这就可以实现显示产品图片的功能。实际上我们可能更倾向于这样写:

[% IF product.picture %]
[% ELSE %]
[% END %]

Now, we've explained that Maypole turns URLs into method calls, so we're going to be putting a view_picture method in the product's class; this class is ISellIt::Product, so we begin like this:
这样我们就向大家解释了如何使Maypole把URL转换成对象的方法调用,接着我们就要对产品类增加view_picture方法,类的名字是ISellIt::Product,我们用下面的代码开始:
package ISellIt::Product;
sub view_picture {
   my ($self, $r) = @_;
   # ...
}

This has a big problem. We don't actually want people to be able to call any method on our class over the web; that would be unwise. Maypole will refuse to do this. So in order to tell Maypole that we're allowed to call this method remotely, we decorate it with an attribute:
这还有个大问题,我们不希望外网的任何人都可以调用类的任何方法,那不太好。让Maypole来拒绝这个,为了告诉Maypole我们有权远程调用这个方法,我们用属性来修饰它:
sub view_picture :Exported {
   my ($self, $r) = @_;
}

At this point, we can call view_picture over the Web; we now need to make it populate the Maypole request with the appropriate data:
在此我们既然希望允许从外网调用view_picture,就需要用合适的数据来填充Maypole请求。
sub view_picture :Exported {
   my ($self, $r, $product) = @_;
   if ($product) {
     $r->{content_type} = "image/png";
     $r->{content} = $product->picture;
   }
}

This is a slightly unusual Maypole method, because we're bypassing the whole view class processing and templating stages, and generating content manually, but it serves to illustrate one thing: Maypole arranges for the appropriate object to be passed into the method; we've gone from URL to object without requiring any code of our own.
这是一个不太寻常的Maypole方法,因为我们跳过了类的处理和套用模板的过程,直接手工产生结果。但这也有效的显明一件事:Maypole为方法调用而维护相应的对象,帮助我们直接从URL跳到对象,不用写程序。
When we come to implementing ordering, in our next article, we'll be adding more actions like this to place the product in a user's shopping cart, check out, validate his credit card and so on. But this should be good enough for now: a templated, web-editable product database, with pictures, without stress, without too much code, and within the deadline. Well, almost.
在下篇文章我们将实现订单,我们得为产品增加更多的行为例如加到购物车,结帐,检查信用卡有效等等。但目前这就足够了:一个基于模板的,网上编辑产品数据库,还带图片的系统。没什么压力也没几行代码,还在期限内就完成了。几乎完美了。
SummaryMaypole is evolving rapidly, thanks primarily to the Perl Foundation who have enabled me to work on it for this month; it's allowed me to write many thousands of words of articles, sample applications, and Maypole-related code, and this has helped Maypole to become an extremely useful framework for developing web applications.
感谢Perl基金会的的帮助,Maypole正在快速发展。基金会不但使Maypole有可能出现,也使我有机会写几千字的文档和例子,以及和Maypole相关的代码。这些都使得Maypole成为一个非常有效的web应用框架。
建议有时间保证的人加入 : http://www.perlchina.net/
-----
EXCERPT: