任性的人
軟件開發人員,我能問您一個問題嗎?雖然我們中有許多人都非常熟悉 Web 技術,如 Html、CGI、Perl、Python、Java 技術和 XML,但為什麼我們自己的網站 ― 那些專用於我們珍愛的開發項目的網站 ― 卻看似由一大群任性活潑的 12 歲孩子胡亂拼湊起來的呢?為什麼,為什麼會這樣?
難道是因為在大多數時間裡,我們對自己的網站放任不管,而揮霍了寶貴的時間來修改我們的自由軟件項目?答案,至少在我的個案中,是最明確的“是的”。
當我不為 IBM developerWorks 撰寫文章或剛作爸爸時,我和我那組技術高超的志願者瘋狂地投入了 Gentoo Linux 下一個發行版的工作中。是的,Gentoo Linux 有其自己的網站。目前(2001 年 3 月),我們的網站還沒那麼特別;那是因為我們沒有花很多時間在它上面,因為我們通常將精力都投入到改進 Gentoo Linux 上。的確,我們的站點有幾個我用 Xara X很快設計出的、公認的可愛徽標,但在這賞心悅目的外觀之下,我們的站點還有許多不足之處。也許您的站點也是這樣。如果是這樣,那麼我有句話要對您說 ― 歡迎來到俱樂部。
www.gentoo.org
在我們的案例中,由於項目在成長,而網站卻沒有,因而使網站陷入了進退兩難的局面。現在,Gentoo Linux 正准備出 1.0 發行版(當它可以正式向非開發人員發布時),而且日趨普及,我們需要開始認真地研究網站如何能夠更好地為其用戶服務。以下是 www.gentoo.org的快照:
圖 1:www.gentoo.org 上事件的當前(2001 年 3 月)狀態
查看原圖(大圖)
可以看到,我們有了所有最基本的要素 ― Gentoo Linux 的描述、功能部件列表、每日“更改日志”(由 Python 自動更新),以及許多重要鏈接(鏈接到下載站點、郵件列表注冊頁面和 cvsWeb)。我們還有到三個文檔資源的鏈接 ― Gentoo Linux 安裝指南和開發指南,以及 Christian Zander 的 NVIDIA 故障排除指南。
然而,雖然站點看起來不錯,但我們遺漏了很多東西。最明顯的是文檔 ― 我們的安裝和開發指南需要大量工作。然後,我們需要添加 FAQ、新鏈接、新用戶信息……這個清單是沒完沒了的。
內容與顯示
現在,我們討論第二個問題。目前,所有工作都以原始 HTML 格式完成;我不斷修改 index.html 文件,直到它看上去讓我滿意為止。更糟的是,我們的 Web 文檔都是以原始 HTML 格式編寫的。從開發角度看,這並不是件好事,因為原始內容(包括段落、節、章)與許多與顯示相關的 Html 標記混合在一起。當然,這會使更改內容和站點外觀變得更困難。雖然這種方法目前還有用,但在我們的站點繼續成長時,它必定會引起某些問題。
顯然,我們需要暗中使用更好的 技術。我們需要使用類似於 XML、XSLT 和 Python 的東西,而不是直接使用 Html。目標是盡可能多地采用自動化,這樣我們就可以輕松添加和擴充站點。如果我們很好地完成了任務,那麼將來對站點的重大更改應該會相對沒有痛苦。
策略!
顯然,我們還需要做大量工作。實際上,有許多工作要完成,而我卻不知道從哪裡開始。正當我嘗試理清頭緒時,無意中發現了 Laura Wonnacott 的“Site Savvy” InfoWorld專欄。在這個專欄中,她說明了“以用戶為中心”設計的概念 ― 如何在關注目標觀眾(在此案例中是指 Gentoo Linux 用戶和開發人員)需要的同時改進網站。閱讀這篇文章並查看該文章中的“以用戶為中心設計手冊”鏈接幫助我制訂重新設計的策略 ― 行動計劃:
首先,明確定義網站的正式目標 ― 寫下來。其目的是什麼,它應該做什麼?
確定使用您站點的不同類別的用戶 ― 您的目標觀眾。按優先級的次序排列他們:哪些觀眾對您來說是最重要的?
建立一個系統來獲取目標觀眾的反饋,這樣他們可以讓您知道您所做的什麼是對、什麼是錯。
評估反饋,使用它來確定站點的哪些部分需要改進或重新設計。應該首先解決高優先級部分。
一旦選擇了站點中要改進的部分,就開始工作吧!在實現期間,請確認:新增部分的內容和設計要明確迎合目標觀眾的需要,而且修復了所有已知的不足。
當完成了部分重新設計時,即使它看來與當前站點有著明顯差別,也應將它添加到正在運轉的站點。這樣,您的用戶可以立即開始受益於最近重新設計的部分。如果重新設計有問題,您可以更快得到用戶反饋。最後,不斷改進站點(而不是重建整個站點,然後突然推出它 ― 令人吃驚!)有助於防止用戶對(可能是巨大的)站點更改產生陌生感。
完成了第 6 步之後,跳到第 4 步,然後重復。
任務說明
我很高興地發現我們已經完成了第 3 步。我們已經接收到了幾封來自站點訪問者的電子郵件建議,而我們的開發人員郵件列表也提供了一種交換建議和意見的方法。然而,我還沒有真正完成步驟 1 和 2。雖然答案也許看似明顯,但我確實發現真正坐下寫出任務說明很有幫助:
www.gentoo.org 是為了幫助使用和開發 Gentoo Linux 的人們而存在的,它提供了關於 Gentoo Linux 和一般 Linux 的最新信息,主要關注與 Gentoo Linux 安裝、使用、管理和開發有關的主題。作為有關 Gentoo 所有事情的主要中心,該站點還應該提供與 Gentoo Linux 用戶和開發人員相關的重要新聞。除了迎合 Gentoo Linux 用戶和開發人員, www.gentoo.org 還有第二個目的,那就是滿足 潛在 Gentoo Linux 用戶的需要,它提供了這些用戶確定 Gentoo Linux 是否適合他們所需的信息。
目標觀眾
到目前為止,一切順利。現在,到了第 2 步 ― 定義目標觀眾:
www.gentoo.org 有三種目標觀眾 ― Gentoo Linux 開發人員、用戶和潛在的用戶。雖然沒有一組肯定比另一組擁有更高的優先級,但目前 Gentoo Linux 開發人員的需要是我們的最高優先級,其次是 Gentoo Linux 用戶,然後是潛在用戶。這是因為 Gentoo Linux 當前處於預先發行狀態。當 Gentoo Linux 達到版本 1.0 時,Gentoo Linux 用戶和潛在用戶也將得到優先級。
意見和建議
好,現在該評估我們收集的建議和意見了:
在過去的幾個月中,我們收到了許多來自網站訪問者的建議。絕大多數時候,人們都要求更好的文檔 ― 無論是開發人員還是用戶。有一些開發人員詢問我們是否可以創建一個郵件列表,它將專用於描述 CVS 提交。
有趣的是,我們還接收到一些電子郵件,詢問 Gentoo Linux 是商業產品還是免費產品。我猜想那是因為我們的主徽標上銘刻了“Gentoo TechnologIEs, Inc.”(我們的法定公司名稱),人們認為我們有商業目的。修改徽標,以使它顯示“Gentoo Linux”,並在主頁面上添加小的起始段落以說明我們是自由軟件項目,這樣會有所幫助。
改進列表
好,現在讓我們將這些建議轉變成可能改進的列表:
重建主頁面
實現:更新徽標並添加自由軟件簡介
目的:明確說明我們是自由軟件項目
目標組:潛在的用戶
難度:中等
改進基本用戶文檔
實現:新的 XML/XSLT 系統,詳細文檔
目的:使用戶安裝 Gentoo Linux 更容易
目標組:新用戶
難度:中等
改進/創建開發人員文檔
實現:新的 XML/XSLT 系統、CVS 指南、開發系統、移植指南
目的:幫助開發人員出色地完成工作
目標組:開發人員
難度:大
添加 CVS 郵件列表
實現:使用現有郵差郵件列表管理器
目的:更好地通知開發人員
目標組:開發人員
難度:小
選擇!
出於不同的原因,列表中有兩件事引人注意。第一件是 CVS 郵件列表 ― 這件事無須費神,因為它很容易實現。通常,首先實現最簡單的更改比較合理,這樣用戶可以立即從中獲益。
列表中第二件值得注意的事就是開發人員文檔的需要。這是一個長期項目,會需要很多工作。根據我與其他開發人員的交談,我們都認同某種 XML/XSL 方法是正確的解決方案。
XML/XSL 原型
為了幫助啟動過程,我開發了原型 XML 語法,以用於所有在線文檔。通過使用這個 XML 語法(稱作“guide”),文檔將明確地組織成段落、節和章(使用類似於 <section>, <chapter> 等的 XML 標記),而同時不包含任何與顯示相關的標記。為了創建在站點上顯示的 HTML,我創建了 XSL 轉換的原型集合。通過使用諸如 Sablotron 的 XSLT 處理器,guide XML 文件可以按以下方式轉換成 Html:
devguide.XML + guide.xsl ---XSLT processor---> devguide.Html
這個 XML/XSLT 方法的優點是它將原始內容(XML)與 guide.xsl(XSLT)文件中的與顯示相關的信息隔離開。如果需要更新 Web 頁面的外觀,只需修改 guide.xsl 文件並通過 XSLT 處理器(Sablotron)運行所有 XML,就能創建更新的 HTML 頁面。或者,如果需要向開發指南添加幾章,則可以修改 devguide.xml。完成之後,通過 Sablotron 運行 XML,就會生成完全格式化的 devguide.Html 文件,其中包含了添加的幾章。將 XML 看作是內容,將 XSLT 看作是與顯示相關的格式化宏。
雖然整個小組都相信 XML/XSLT 是正確方法,但我們對正式 XML 語法還沒有達成一致意見。Achim,我們的開發領導,建議我們使用 docbook 來代替使用我們自己的 XML 語法。然而,原型指南 XML 格式已經促使我們開始了決策過程。因為我們開發人員每天都會使用 XML/XSL,因此選擇一個我們都感到滿意且滿足我們所有需要的解決方案至關重要。在下一篇文章中,我將向您演示有效的 XML/XSL 文檔系統。
技術演示:pytext
一般情況下,我們的當前網站不使用值得一提的任何新的或超酷的技術。然而,這裡有一個值得注意的例外 ― 我們的小 pytext,這是一個嵌入式 Python 解釋器。
就象你們中的許多人一樣,我是個超級 Python 愛好者,在所有腳本語言中我最喜歡它,因此當向網站添加一些動態內容時,我當然想要使用 Python。您也許知道,在編碼動態 HTML 內容時,與其它方法相比,通常將語言命令嵌入到 Html 內部更為方便。因此,需要一個嵌入式 Python 解釋器,它可以接受如下文檔:
<p>
Yeah, sure; I got some questions:<br>
<!--code
names=["bob","jimmy","ralph"]
items=["socks","lunch","accordion"]
for x in items:
for y in names:
print "Anyone seen",y+"'s",x+"?<br>"
-->
See, told you so.
</p>
……然後將它轉換成:
<p>
Yeah, sure; I got some questions:<br>
Anyone seen bob's socks?<br>
Anyone seen jimmy's socks?<br>
Anyone seen ralph's socks?<br>
Anyone seen bob's lunch?<br>
Anyone seen jimmy's lunch?<br>
Anyone seen ralph's lunch?<br>
Anyone seen bob's accordion?<br>
Anyone seen jimmy's accordion?<br>
Anyone seen ralph's accordion?<br>
See, told you so.
</p>
下面是 pytext 的源代碼:
pytext 嵌入式 Python 解釋器
#!/usr/bin/env Python
# pytext 2.1
# Copyright 1999-2001 DanIEl Robbins
# Distributed under the GPL
import sys
def runfile(myarg):
"interprets a text file with embedded elements"
mylocals={}
try:
a=open(myarg,'r')
except IOError:
sys.stderr.write("!!! Error opening "+myarg+"!\n")
return
mylines=a.readlines()
a.close()
pos=0
while pos<len(mylines):
if mylines[pos][0:8]=="<!--code":
mycode=""
pos=pos+1
while (pos<len(mylines)) and (mylines[pos][0:3]!="-->"):
mycode=mycode+mylines[pos]
pos=pos+1
exec(mycode,globals(),mylocals)
else:
sys.stdout.write(mylines[pos])
pos=pos+1
if len(sys.argv)>1:
for x in sys.argv[1:]:
runfile(x)
sys.exit(0)
else:
sys.stderr.write
("pytext 2.1 -- Copyright 1999-2001 DanIEl Robbins. ")
sys.stderr.write
("Distributed under the\nGNU Public License\n\n")
sys.stderr.write
("Usage: "+sys.argv[0]+" file0 [file1]...\n")
sys.exit(1)
pytext 工作原理
這裡說明它是如何工作的。它會掃描每一個輸入行,而且在大多數時間裡,每個輸入行都只是回顯到標准輸出。但是,如果 pytext 遇到以 <!--code 開頭的行,那麼直到第一行以 --> 開頭的每一行的內容都會被附加到稱作 mycode 的字符串中。然後 pytext 使用內置 exec() 函數執行 mycode 字符串,從而有效地創建嵌入式 Python 解釋器。
關於這個特殊的實現,確實有一些美妙的東西 ― 我們以這種方式調用 exec(),這樣會保存對全局和本地名稱空間的所有修改。這樣,就可以導入模塊或在某嵌入塊中定義變量,然後在以後創建的塊中訪問這個先創建的對象,如以下示例明確演示的那樣:
<!--code
import os
foo=23
-->
Hello
<!--code
print foo
if os.path.exists("/tmp/mytmpfile"):
print "it exists"
else:
print "I don't see it"
-->
很方便,是嗎?pytext 服務是 Python 強大功能的極佳演示,並且對於 Python 愛好者來說是極其有用的工具。對於當前站點,我們從 cron 作業中調用 pytext,使用它定期為主頁面“更改日志”生成 Html 代碼:
pytext index.ehtml > index.Html
就寫到這吧;下次見面時,我會討論 www.gentoo.org 重新設計的第一階段!