[轉載] 使用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: