鳳

[活動] 7/22 下午【攻殼機動隊】

「立思 2012 夏天‧愛思考‧系列講座」的第一場【攻殼機動隊】,周日即將在台大活動中心 B1 蘇格拉底廳舉行。

活動於下午一點開始報到進場,先由科幻專家難攻博士主講《殼中之靈的美麗與哀愁》,再由精神分析師楊明敏闡述《單身的義體人並不孤獨,還需要性嗎?》。

接著再由兩位講者對談,並留有時間給現場的攻殼同好、聽眾們發言,討論【攻殼機動隊】這個科幻史上的經典作品。

籌備這場講座時,主辦人參考了 Humble Indie Bundle 廣受好評的「自由訂價模式」,讓聽眾在實際入場聽講後,再自由決定給付少於、等於或多於原定票價 750 元的聽講費用。

所有現場實收費用的一半,也將捐贈給關心生命的非營利組織。

如此新穎的模式、精采的講題、內行的聽眾,會激盪出怎樣的迴響和火花呢?我們都很期待。

歡迎參加。星期日見!

Continue reading "[活動] 7/22 下午【攻殼機動隊】" »

July 16, 2012 at 01:51 AM in Current Affairs, Film | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

Socialtext 應用 Scrum 方法的三年(下)

(這是 Three Years of Scrum at Socialtext 這篇文章的中譯,接續上集的內容。)


改善 Standup 集會

彼此支援的互助方式,意味著開發人員不需要等開會,就能即時解決自己工作中的阻礙。

這樣一來,當我們每次在 Standup 集會上要列出自己遇到的阻礙時,實際上… 根本沒有任何阻礙。

此外,儘早消除阻礙還有另一個好處:我們通常都能完成每天的工作承諾。因此,“我昨天做了什麼”以及“上次 Standup 上我說過要做的事情”的實際內容是相同的,因此第二個狀態的更新也顯得多餘。

最後的結果是:原本每天要討論的三件事,最終被減少到一件。

突然之間,我們的開會時間大幅縮短,通常只需要 8-12 分鐘,有時候甚至更快。

為了加速集會進行,我們會在各自的微博上提前記錄內容,再由系統自動集中到共筆頁面上。在 Standup 集會裡,我們會直接將頁面進行畫面共享。當大家都習慣這樣的工作方式後,大部份人的狀態彙報通常只有一句:“就是各位看到的這樣”,或“沒什麼新內容,搞定”。

精通 Scrum 的專家看到這裡,可能會認為我們不太對勁:整個團隊似乎根本沒有溝通。但我認為實際情況正好相反:團隊的溝通非常順暢,只不過溝通都是在 Standup 之外進行的。我們每天的工作都列在看板系統上:這塊看板實際上就是一個自動產生的共筆頁面,其中列出我們目前流程中需要執行的每項工作,例如這樣:

(Socialtext 的看板內容節錄。此處的 WIP 是指“開發中的工作”。同一頁裡還包括“準備中”、“品管中”,以及“等待核可”等未顯示的部份。)

通過看板了解狀態,並隨時消除阻礙,這些做法使得 Standup 程序變得沒那麼重要。它像是解決早期問題的古老機制,已經不再適用於團隊的現況。

事實上,情況正是如此。當年 Schwaber 和 Sutherland 兩人在 EasleCorp 公司創建 Scrum 方法時,他們需要解決的具體問題是這樣的:各項需求之間彼此衝突和影響的程度如此之高,以至於技術人員根本無法開展工作 — 他們甚至無法發佈任何一項產品。

因此,您可以將 Scrum 模型裡的“衝刺周期”理解為改變過程中的喘息時間,或者直接想成是“請給我們一個月時間做出實際工作,然後再來修改計劃。”

最近一年多來,這樣的問題已經再也沒有在 Socialtext 出現過。於是我們繼續改善工作流程。

進一步改善流程

從 2007 年起,我們嚴格執行為期兩周的“衝刺周期”。這個模式運作了兩年多,直到 2010 年初為止。在這段時間裡,我們共進行了 52 次為期兩周的“衝刺”,其中 48 次準時發佈為新版的產品。(如果您想知道細節的話,有兩次失敗的衝刺是因為聖誕節和新年的延誤,一次是由於品質問題導致延期發佈,還有兩次是因為功能未臻完善,不適合發佈。)

這兩年間,我們也遇到了各種挑戰 — 有五六次升級造成運作上的瑕疵,需要開發人員提供支持。但產品的整體素質依然很高,同時幾乎沒有顧客抱怨功能上的問題。

在將我們的工作拆分為小塊的過程中,我們自己的技能也得以提高,同時對於功能可以正式完成的時間估計也更加精確。結果我們發現,如果將工作拆分為幾乎相同規模的小塊,通常根本不需要估算工作量,就能測量出預計的完成時間。

一旦所有工作項目都高度一致,團隊的流程就可以從“只要以高品質成功完成,我們可以每兩周發佈一次”,變成“只要這塊工作完成,我們就可以發佈” — 與此同時,管理層對於專案準時完工依然可以保持充足的信心。

如此一來,我們逐漸從傳統的 Scrum 模型轉變成“塊狀工作”法,也可以稱做“看板法”。

在這段時間裡,我們也對每天舉行的 Scrum 集會進行改進,首先在周三添加了“工程話題”環節,之後則將集會改為每天進行,但並不要求必須全員參加。(“工程話題”包括傳統員工會議裡常見的活動:宣布人員招募訊息、公司策略討論、層出不窮的員工意見建議、測試人員發現的潛在問題等。)

幸虧有了看板,會議的焦點不再是“我目前正在做什麼”,因為大家都可以一目了然地看到每個人正在做什麼。現在我們討論的是接下來需要做什麼,何時以及如何調整優先順序,以滿足每天的需求。如此一來,我們不再需要討論如何完成目標和任務,以及我們被什麼卡住,而是轉為討論團隊正在面臨哪些挑戰,以及我們可以採取的措施。

雖然沒有任何一種流程是完美的,但我必須說,我喜歡這種方式的發展方向。

請不要誤會本文的意圖。我並不是說傳統的每日 Scrum 方法不好,我也並不是要推廣說我們的方法更好。準確來說,我只是想提供某種類型的最佳實踐紀錄。

筆者認為,要形成良好的流程,需要不斷地反省、調整,再做出改變。成長過程中可能會承受痛苦,但決不要放棄追求改善:沒有最好,只有更好。您肯定也明白這一點。

變得“更好”,並不意味著原來的方法就是錯誤的。 Scrum 方法並非一成不變,而是一種不斷持續改進的流程。就像煮一碗湯,您可以品嘗它、改進它,讓它每次的味道都變得更好。

一旦走上這條道路,您就不僅是在開發軟體,更是在創造文化。

創造文化的過程或許艱難,也許會充滿挑戰與辛酸。

不過,最終還是值得的。

April 11, 2011 at 09:12 PM in Craft | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

Socialtext 應用 Scrum 方法的三年(上)

Scrum 方法乍看簡單,但長期採用它會產生怎樣的影響呢? Matt Heusser 花了一些時間反思,並寫下三年來每天在 Scrum 團隊裡工作,所經歷的實際情況。

(這是 Three Years of Scrum at Socialtext 這篇文章的中譯。)


這年頭,從樓上扔下一塊磚,都能砸到幾個向您推薦“如何使用 Scrum 方法過渡到敏捷開發”的專家們。這項工作非常重要,我也很高興看到有人這樣做,但本文的重點不在這裡。

相反的,我想講一講我們公司 Socialtext 採用 Scrum 方法的真實故事。

我在 2007 年秋天加入 Socialtext 時,我們才剛開始使用敏捷流程。在接下來的三年多裡,我們不斷針對開發流程進行改進。

本文的重點,並不在介紹敏捷開發的具體轉換過程,而是介紹 Scrum 方法,特別是每天的 Standup 會議,以及多年來的改善過程。

關於 Socialtext

Socialtext 的總部位於矽谷的核心地帶,主要負責營銷方面的運作。

雖然產品管理、工程總監,以及少數開發人員在總部上班,但絕大部分的技術人員,都是在遍布全球的分部,或是在自己家裡進行工作。

我們開發的產品,是給企業用的社會軟體。應用這個平台,可以在任何能上網的地方工作。這種靈活的工作方式讓團隊獲益匪淺:我們可以不受地理位置的限制,招募到最能勝任工作的人。

如果您由於地理位置或時區差異等原因,不確定 Scrum 方法是否適用,請別擔心;我們以前也有過這些疑慮,但後來都找到了合適的解決之道。

促進溝通協作

許多 Scrum 團隊認為開會是高成本、又容易浪費時間的事,因此嚴格限制團隊的溝通時間。我們嘗試了一種不同的策略:“盡量降低溝通成本”。

為了讓溝通更方便,我們團隊採用了 Astertisk 這項開源工具,作為 VoIP 語音服務器,並提供會議室功能。美西時間的早上十點到下午兩點是我們的共同工作時段,也就是可供開會溝通的時間。

一開始,我們採用定期兩周的“開發周期”,並且每周三次在下午一點舉行 Standup 集會。

在 Scrum 模式裡,每個人在 Standup 裡輪流報告三部分的訊息:我昨天做了些什麼,我今天打算做什麼,以及目前遇到的阻礙。

阻礙會大幅降低生產力,因為每位成員每天通常只專注於單一項目,所以如果項目卡住了,會讓開發人員變成非常昂貴的花瓶。

不過,即使是被卡住一天,也令人相當頭疼。要等一整天,在下次會議上再告訴別人,這個方法並不怎麼高明。因此,我們決定使用微博和聊天室作為彼此求助的管道。

此時團隊面臨一個決策:如果我們允許遇到阻礙的團隊成員打擾其他同事,那麼可能直接對他人的工作產生影響。好比說在聊天室裡提到某人的名字時,會讓他的電腦發出提示音,打斷他目前正在進行的工作。

最後我們決定允許這種打擾,因為被卡住會造成更大的浪費。

同時,我們也運用各種協作工具,例如用 GNU Screen 共享畫面、用 VNC 進行圖形界面測試,來進行結對開發(Pair programming)。在 2011 年初,我們大約有 80% 的開發工作是用結對方式合作進行的。


(請繼續閱讀下集。)

April 10, 2011 at 05:08 PM in Craft | Permalink | Comments (3) | TrackBack (0)

| Digg This | Save to del.icio.us |

開發經驗談 (六之六)

筆者在 2009 年 6 月時加入 SocialCalc 專案團隊。經過四個月的密集作業,我們在 10 月 19 日(VisiCalc 的 30 周年紀念日)發表了 SocialCalc 1.0 版。

從 WikiCalc 到 SocialCalc,中間經歷了三年的開發。相形之下,最後這四個月只是一段短暫的時間。儘管如此,這段和 Socialtext 同事在 Dan Bricklin 的指導下進行協作的過程,仍使我獲益匪淺。在此,筆者想將那段時間學到的經驗分享給大家。

願景清晰的首席設計師非常重要

在《設計的設計(The Design of Design)》一書中,Fred Brooks 認為,在建構複雜系統時,若能專注於清晰連貫的設計概念,而非各自為政的想法,那麼溝通成本將會大幅下降。

依 Brooks 的想法,這種連貫式設計概念的構想,最好能由一個人來主導:

    概念的完整性是偉大設計中最重要的特性。由於完整的概念只能出自一人或
    少數人的合作構想,因此明智的管理者會大膽委託才華出眾的首席設計師,
    來承擔整個設計任務。

對於 SocialCalc 這個專案來說,讓 Tracy Ruggles 擔任首席用戶體驗設計師,是讓我們成功實現原初構想的關鍵。由於 SocialCalc 的底層引擎十分易於擴展,因此各種新功能的想法往往層出不窮;Tracy 透過草圖進行原型設計的才能,為我們提供了巨大的幫助,讓我們能用最直觀的方式呈現出所有的功能。

共筆確保專案延續

在我加入專案之前,SocialCalc 已有超過兩年的設計和開發歷史,但我卻能在幾天之內立刻趕上進度,開始作出貢獻。

這是因為「所有資訊都在共筆裡」– 從早期的設計草稿到最新的瀏覽器支援清單,整個流程都詳細記載在共筆頁面和 SocialCalc 試算表裡。

通過閱讀該專案的相關記錄,讓我能跳過新成員加入時常見的上手期,快速跟上目前的進度。

這在傳統的開源專案比較少見。傳統專案中大部分的交流都是在 IRC 和郵件列表中進行的,而共筆(如果有使用的話)往往只用來存放相關資源的記錄和鏈結 – 如果有新人加入,要想通過結構混亂的 IRC 紀錄和郵件存檔追趕進度,無疑會更加困難。

善加運用時區差異

Ruby on Rails 的發明人 David Heinemeier Hansson 在加入 37signals 時,曾這樣評價分散式團隊的優勢:

    哥本哈根和芝加哥之間相隔的七個時區,實際上減少了我們受到的打擾,
    讓我們做出更多工作。

在 SocialCalc 的研發過程中,對於台北和帕羅奧圖之間相隔的九個時區,我們亦有同感。

通常我們會在一天 24 小時內,完成一次「設計-開發-品管」反饋循環,每個環節用到某個人的八小時。這種非同步式的協作,促使我們運用能自我描述的完整作品(設計草圖、代碼,以及測試)來溝通,從而大幅增進相互之間的信任。

樂趣最優

筆者在 2006 年於 CONISLI 會議上所作的主題演講「-OFun:樂趣最優(Optimizing for Fun)」裡,將自己領導分散式團隊開發 Perl 6 語言的經驗,總結為幾個重點。其中「隨時保持藍圖清晰」、「寬恕 > 許可」、「打破僵局」、「不求共識,只求創意」,以及「使用代碼描述概念」這幾項,特別適合小型的分散式團隊。

因此,在開發 SocialCalc 時,我們特別重視團隊成員間的知識分享,以確保每個人都不會成為主要瓶頸。

另外,當設計過程出現多種備選方案時,我們會主動將每個方案都進行實做,以深入探勘它們的設計空間,並先一步解決可能出現的衝突。如果在此過程裡發現有更好的設計,我們也不怕將整個原型打掉重寫。

雖然缺乏面對面交流,但這些文化特質幫我們培養相互之間的信任和情誼,並將爭議降到最低,讓 SocialCalc 的開發成為一件樂事。

通過故事測試推動開發工作

在加入 Socialtext 前,我曾倡導「將測試寫進規格裡」的方法,這一點在 Perl 6 語言規格 中就能看到:我們逐段幫規格加上測試,並持續確保兩者之間的同步。

然而 SocialCalc 專案品管團隊的成員 Ken Pier 和 Matt Heusser 卻讓我大開眼界 – 原來這種做法還可以更進一步,將測試提昇到「可以執行的規格」這個境界。

在《美妙的測試(Beautiful Testing)》一書第 16 章,「剝開 Socialtext 的玻璃洋蔥」裡,Matt 用如下方式解釋了我們由「故事測試」推動的開發流程:

    工作的最基本單元是一系列「故事」,也就是一系列非常輕量級的需求文件。

    每個故事只包含對一個功能的簡要描述,以及此功能的運作實例,用最直白的
    文句進行描述。我們稱這些實例為「接納度測試」。

    在故事形成初期,設計師會先寫出初步的接納度測試,隨後由開發人員和測試
    人員進行討論,之後開發人員才開始編寫代碼。

隨後這些故事測試會被轉換為「共筆測試(wikitests)」。這是一種基於表格的語言,脫胎自 Ward Cunningham 的 FIT 框架。用此語言寫成的測試存進共筆系統後,會自動連接到 Test​::WWW​::Mechanize 和 Test​::WWW​::Selenium 等測試框架,成為自動測試流程的一環。

用故事測試作為「表達需求」和「驗證需求」的共通語言,這種做法的好處一言難盡。它不但大幅節省了溝通成本,也讓我們能每個月對系統進行一次改版時,不用擔心已經修復的瑕疵會再次出現。

CPAL 開源授權

最後,我們針對 SocialCalc 而設計的開源授權,也令我們受益匪淺。

Socialtext 為了 SocialCalc,設計出通用公共授權(Common Public Attribution License)。CPAL 以 Mozilla 公共授權為基礎,並讓原作者可以要求在軟體的界面上顯示自己的標誌。CPAL 還加上了「網絡使用條款」,讓衍生作品在透過網路提供服務時,也授予一般使用者取得源碼的權利。

在獲得開放源碼促進會和自由軟體基金會的認可後,已經有不少知名網站(例如 Facebook 和 Reddit)選擇用 CPAL 方式發布自己的平台源碼,這對我們無疑是莫大的鼓勵。

因為 CPAL 屬於「較弱型著佐權(weak copyleft)」授權,因此開發人員可以將它整合進任何自由軟體或私有軟體裡,只需要分享出針對 SocialCalc 本身的修改。這讓各個社群都可以採用 SocialCalc ,並對其進行改進。

底下是一些以 SocialCalc 為基礎的開源專案:

  • Drupal 的 Sheetnode 專案,及其自行維護的 SocialCalc 分支。
  • Luke Closs 發起的 OLPC/XOCOM 平台移植版。
  • SEETA 的 OLPC/Sugar 平台移植版,由 Luke 的版本衍生而來。
  • SEETA 的 Palm Pre 平台移植版。
  • Ramu Ramamurthy 的 Scala/Java 試算表伺服器專案。

本章中的範例,包括豐富文本和協作編輯等功能,都能在 http://github.com/audreyt/socialcalc 下載。WikiCalc 1.0 代碼的歷史存檔,則可由http://github.com/audreyt/wikicalc 取得。

開源的網頁試算表引擎,有許多有趣的應用可能。如果您能將 SocialCalc 嵌入到您自己的專案中,這絕對是我們喜聞樂見的。

Happy Hacking!

唐鳳

 

March 05, 2011 at 10:43 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

即時多人協作 (六之五)

我們接下來研究的一個例子,是在共享的試算表裡進行多人即時協同編輯。

這乍看之下也許有點複雜,但是感謝 SocialCalc 的模組化設計,我們只需讓每位使用者將自己的指令傳播給其他參與者執行即可。

為了對本地指令與遠端指令作出區分,我們為 ScheduleSheetCommands 方法增加了 isRemote 參數:

    SocialCalc.ScheduleSheetCommands =
        function(sheet, cmdstr, saveundo, isRemote) {
            if (SocialCalc.Callbacks.broadcast && !isRemote) {
                SocialCalc.Callbacks.broadcast('execute', {
                    cmdstr: cmdstr,
                    saveundo: saveundo
                });
            }
            // ...original ScheduleSheetCommands code here...
        };

現在只需定義一個合適的 SocialCalc.Callbacks.broadcast 回喚函數,即可讓所有連入此試算表的客戶端執行相同的指令。

當 SEETA Sugar Labs 於 2009 年在 OLPC 上首次實作這項功能時,broadcast 函式是用 XPCOM 框架寫成,並在 OLPC/Sugar 的標準傳輸層 D-Bus/Telepathy 網路上運行:

這樣的運行方式,讓 Sugar 網路裡的 XO 電腦能對共同的 SocialCalc 試算表進行協作,但也只能在 Mozilla/XPCOM 瀏覽器平台與 D-Bus/Telepathy 訊息平台上適用。

跨瀏覽器傳輸

為了達成跨瀏覽器、跨作業系統的目標,我們使用 Web::Hippie 框架,作為 JSON 在 WebSocket 上傳輸的抽象層。它提供方便的 jQuery 綁定,並且當 WebSocket 不適用時,也能利用 MXHR (multipart XMLHttpRequest) 作為備用傳輸機制.

對於安裝了 Adobe Flash 插件但沒有原生 WebSocket 支持的瀏覽器,我們使用 web_socket.js 專案的 Flash WebSocket 模擬器,這通常比 MXHR 更快也更可靠。

運作流程看上去是這樣的:

客戶端的 SocialCalc.Callbacks.broadcast 函式定義如下:

    var hpipe = new Hippie.Pipe();

    SocialCalc.Callbacks.broadcast = function(type, data) {
        hpipe.send({ type: type, data: data });
    };

    $(hpipe).bind("message.execute", function (e, d) {
        var ss = SocialCalc.CurrentSpreadsheetControlObject;
        ss.context.sheetobj.ScheduleSheetCommands(
            d.data.cmdstr,
            d.data.saveundo,
            true // isRemote = true
        );
        break;
    });

儘管這已經能順利運行,我們仍然有兩個問題需要解決。

衝突解決

第一個就是為了執行指令而產生的爭用狀態:如果使用者 A 與 B 同時執行某個影響相同儲存格的操作,之後才接收到與對方傳播出來的指令,那麼雙方將會停在不同的狀態:

我們可以通過 SocialCalc 內置的還原/重作機制來解決這個問題,如下圖所示:

  • 當客戶傳播出一個指令時,會將指令添加到待辦佇列。
  • 當客戶接收到一個指令時,檢查待辦佇列:
    • 如果待辦佇列為空,則直接執行這項遠端指令。
    • 如果它符合待辦佇列裡的本地指令,則將它將從佇列中移除。
    • 否則,檢查佇列中是否有指令與接收到的指令相衝突:
      • 如果存在衝突指令,則先還原這些指令,並將其標記為稍後重作。
      • 在還原所有的衝突指令之後,將遠端指令按正常狀態執行。
  • 當從伺服器上接收到標記為重做的指令時,客戶端將再次執行指令,再從佇列中將其移除。

遠端游標

盡管爭用狀態已經得到解決,但「偶爾會覆蓋掉其他使用者正在編輯的儲存格」這樣的不理想情形還是存在。我們可以更進一步,將每位使用者的游標位置傳播給其他使用者,讓每個人都能看到有哪些儲存格正在編輯。

為了實做這一想法,我們為 MoveECellCallback 事件添加了另一個 broadcast 處理程序:

    editor.MoveECellCallback.broadcast = function(e) {
        hpipe.send({
            type: 'ecell',
            data: e.ecell.coord
        });
    };

    $(hpipe).bind("message.ecell", function (e, d) {
        var cr = SocialCalc.coordToCr(d.data);
        var cell = SocialCalc.GetEditorCellElement(
            editor, cr.row, cr.col
        );
        // ...decorate cell with styles specific
        //    to the remote user(s) on it...
    });

在試算表中標記儲存格焦點時,通常會使用帶有顏色的邊框。但是,該儲存格也許已經有自己的 border 屬性了。而由於 border 為單一顏色,因此在相同的儲存格只能表現一個游標。

因此,在支援 CSS3 的瀏覽器上,我們使用 box-shadow 功能來表現多個游標:

    /* Two cursors on the same cell */
    box-shadow: inset 0 0 0 4px red, inset 0 0 0 2px green;

如此一來,當四人編輯同一個試算表時,螢幕看起來會像這樣:

(未完,待續。)

March 04, 2011 at 01:59 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

豐富文本編輯 (六之四)

我們對 SocialCalc 所做的第一項改進,是讓文字儲存格能夠使用共筆語法,以及直接在表格編輯器中實現的豐富文本繪製:

在 1.0 版發布之後不久,為了回應用戶希望通過統一語法插入圖片、鏈接,以及文字標記的需求,我們幫 SocialCalc 加上了這項功能。由於 Socialtext 提供自己的開源共筆平台,因此自然會希望能在試算表中直接使用相同的語法。

為了實現此功能,我們需要將預設的文字儲存格格式(即 textvalueformat 屬性)設為 text-wiki,並為它提供自定的繪製器。

至於 textvalueformat 屬性是什麼呢?請見下文。

類型與格式

在 SocialCalc 中,每個儲存格都有一個 datatype 及一個 valuetype 屬性。包含文字/數字資料的儲存格分別對應到文字/數字值類型,而具備 datatype="f" 的公式儲存格則可能會生成數字或文字值。

在前一節介紹的繪製步驟中,Sheet 物件會為每個儲存格生成 HTML。為此,它會檢查每個儲存格的 valuetype:如果以 t 開頭,則該儲存格的textvalueformat 屬性會決定如何進行生成。如果以 n 開頭,則使用 nontextvalueformat 屬性進行判斷。

如果該儲存格的 textvalueformat 或 nontextvalueformat 屬性沒有定義,則會通過其 valuetype 屬性查詢預設格式,如下圖所示:

對 text-wiki 值格式的支援,寫在 SocialCalc.​format_text_for_display 中:

     if (SocialCalc.Callbacks.expand_wiki
         && /^text-wiki/.test(valueformat)
     ) { 
         // do general wiki markup 
         displayvalue = SocialCalc.Callbacks.expand_wiki(
             displayvalue, sheetobj, linkstyle, valueformat
         ); 
     } 

此處我們並非將「共筆文字轉 HTML」的轉換器嵌入到 format_text_for_display 裡,而是在 SocialCalc.​Callbacks 裡定義一個新的掛鉤。這是 SocialCalc 代碼裡推薦的方法:這種模組化的設計,讓應用程式可以支援各種不同共筆文字的語法。如果應用程式用不到 text-wiki 格式,也可以直接忽略 expand_wiki 掛鉤。

繪製共筆文字

隨後,我們使用了 Wikiwyg,這是一個 JavaScript 程式庫,可在共筆文字和 HTML 之間提供雙向轉換。

我們定義下列 expand_wiki 函式,將儲存格的值透過 Wikiwyg 的 Wikitext 解析器和 HTML 產生器轉換成 HTML:

     var parser = new Document.Parser.Wikitext(); 
     var emitter = new Document.Emitter.HTML(); 
     SocialCalc.Callbacks.expand_wiki = function(val) { 
         // Convert val from Wikitext to HTML 
         return parser.parse(val, emitter); 
     } 

最後一個步驟則需要在試算表初始化完畢後,將 set sheet defaulttextvalueformat text-wiki 加入命令佇列:

     // Assume there's a <div id="tableeditor"/> in DOM 
     var spreadsheet = new SocialCalc.SpreadsheetControl(); 
     spreadsheet.InitializeSpreadsheetControl(
         "tableeditor", 0, 0, 0
     ); 
     spreadsheet.ExecuteCommand(
         'set sheet defaulttextvalueformat text-wiki'
     ); 

將上述部份搭配起來後,繪製步驟的工作流程將類似這樣:

大功告成!改進後的 SocialCalc 能夠支持豐富的共筆標記語法:

    *bold* _italic_ `monospace` {{unformatted}}
    > indented text
    * unordered list
    # ordered list
    "Hyperlink with label"<http://softwaregarden.com/>
    {image: http://www.socialtext.com/images/logo.png}

請嘗試在 A1 中輸入 *bold* _italic_ `monospace`,隨後即可看到繪製後的豐富文本內容:

(未完,待續。)

March 03, 2011 at 01:54 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

SocialCalc (六之三)

這是 SocialCalc 在運行過程中的畫面:

底下則是它的類型圖:

相較於 WikiCalc,伺服器的角色已大幅減輕。現在伺服器只需要負責回應 HTTP GET 請求,提供整份表格內容的序列化字串即可。瀏覽器在收到資料後,所有計算、變動追蹤,以及使用者的互動都是通過 JavaScript 達成的。

JavaScript 元件在設計上使用了層次式 MVC(模型/視圖/控制器)樣式,每個類型都只專注於某部分的功能:

  • Sheet 是資料模型,代表試算表在記憶體中的結構。 

    模型中包含從座標指向 Cell 物件的字典,每個物件代表一個儲存格。空儲存格所在的座標不需要有對映的物件,因此完全不占用記憶體。

  • Cell 代表儲存格的內容和格式。 

    下面列出的是一些常見的 Cell 物件屬性:

        datatype    t
        datavalue   1Q84
        color       black
        bgcolor     white
        font        italic bold 12pt Ubuntu
        comment     Ichi-Kyu-Hachi-Yon
  • RenderContext 用於實現視圖,需要負責將表格繪製為相應的 DOM 物件。

  • TableControl 則是主控制器,負責接收滑鼠和鍵盤事件。 

    在接收到視圖事件,例如滾動和調整大小後,就會對相關 RenderContext 物件進行更新。

    如果收到應用於試算表內容的更新事件,則會在試算表的指令佇列中加入新的指令。

  • SpreadSheetControl 負責繪制頂層界面,包括工具欄、狀態欄、對話框,以及顏色選擇器。

  • SpreadSheetViewer 是另一套頂層界面,主要提供唯讀的互動視圖。

我們採用了基於類型的輕量級物件系統,僅使用了簡單的組成/委派機制,完全沒有使用繼承或物件原型。所有符號都位於 SocialCalc.* 名稱空間裡,以避免命名衝突。

對試算表的全部更新都需要通過 ScheduleSheetCommands 方法進行,因此需要通過指令字串來代表編輯操作。常用的指令如下:

    set     sheet defaultcolor blue
    set     A width 100
    set     A1 value n 42
    set     A2 text t Hello
    set     A3 formula A1*2
    set     A4 empty
    set     A5 bgcolor green
    merge   A1:B2
    unmerge A1
    erase   A2
    cut     A3
    paste   A4
    copy    A5
    sort    A1:B9 A up B down
    name    define Foo A1:A5
    name    desc   Foo Used in formulas like SUM(Foo)
    name    delete Foo
    startcmdextension UserDefined args

嵌入 SocialCalc 的應用程序可以自行定義額外的指令,只需要將命名的回調函數添加到 SocialCalc.​SheetCommandInfo.​CmdExtensionCallbacks 物件,即可使用 startcmdextension 指令進行呼叫。

指令的循環運行

為改善回應速度,SocialCalc 會在背景執行全部的重算和 DOM 更新,因此在使用者對多個儲存格進行修改時,試算表引擎會同時在指令佇列裡處理先前的改動。

在運行指令時,TableEditor 物件會將其 busy 屬性設為 true;後續指令則需加入到 deferredCommands 佇列,以確保指令能循序執行。事件循環看起來像這樣:

如上圖所示,Sheet 物件會持續發送 StatusCallback 事件,以提醒使用者當前的指令執行狀態,這一過程都可以分為下列四個步驟:

  • 執行指令 

    啟動時發送 cmdstart,執行完成後則發送 cmdend。

    如果指令間接更改了某儲存格的值,則進入重算步驟。

    否則,如果指令更改了一個或多個已在螢幕上顯示的儲存格的視覺外觀,則進入繪製步驟。

    如果上述情況都不符合(例如在使用copy指令時),則跳到位置計算步驟。

  • 重算(如果需要的話) 

    啟動時發送 calcstart,在檢查儲存格的依存鏈時每隔 100ms 發送 calcorder,完成檢查時則發送 calccheckdone,並在所有受影響儲存格獲得重算後的值後發送 calcfinished。

    這一步驟之後總是需要執行繪製步驟。

  • 繪製(如果需要的話) 

    啟動時發送 schedrender,如果使用格式化後的儲存格更新了 <table> DOM 物件,則發送 renderdone。

    這一步驟之後總是需要執行位置計算步驟。

  • 位置計算 

    啟動時發送 schedposcalc,並在更新了滾動條、目前儲存格游標,以及 TableEditor 的其他視覺組件後發送 doneposcalc。

因為所有指令在執行後即被保存,因此等於對所有操作都可獲得執行紀錄。為實現稽核追蹤,Sheet.CreateAuditString 方法會傳回以換行隔開的字符串,每行內容對應到一個指令的相關記錄。

ExecuteSheetCommand 還可為執行的每個指令創建還原指令。舉例來說,如果儲存格 A1 包含 Foo,而使用者執行了 set A1 text Bar,則還原指令set A1 text Foo 會被推送到 UndoStack 裡。如果使用者進行還原操作,則會通過執行還原指令來讓 A1 的內容回到原先的值。

試算表編輯器

接著一起來看看 TableEditor 層。該層可計算 RenderContext 的螢幕顯示內容,並通過兩個 TableControl 實例來管理水平/垂直捲動軸。

視圖層主要由 RenderContext 類型負責。與 WikiCalc 的設計不同的是,我們並非將每個儲存格對映到一個 <td> 元素,而是直接創建固定大小的 <table>,使它充分填滿瀏覽器的可視區域,並為其預先填充 <td> 元素。

當使用者通過自定義的滾動條拖動試算表後,可以動態更新預先繪制的 <td> 元素的 innerHTML。這意味著在很多常見情況下,我們並不需要創建/刪除任何 <tr> 或 <td> 元素,因此大幅提升了回應速度。

因為 RenderContext 只繪製可視區域,所以無論試算表多大,執行效能也不受影響。

TableEditor 還包含一個 CellHandles 物件,可用於處理附加到目前儲存格(即ECell)右下角的圓盤形填充/移動/滑動選單指令:

輸入框則由兩個類型負責管理:InputBox 和 InputEcho。前者主要管理網格上的編輯行,後者主要用於在輸入內容時提供及時更新的預覽層,並覆蓋ECell的內容。

通常,SocialCalc 引擎只有在打開試算表並進行編輯時,以及將內容保存回伺服器時才需要與伺服器通訊。因此,Sheet.ParseSheetSave 方法可在Sheet 物件中解析儲存格式字串,而 Sheet.CreateSheetSave 方法可將 Sheet 物件序列化為儲存格式。

通過使用 URL,公式可引用任何遠端試算表中的值。recalc 指令會重新抓取被引用的外部電子試算表,並使用 Sheet.ParseSheetSave 對其進行解析,然後將其存儲在暫存區中,這樣使用者即可在不重新抓取內容的情況下,直接引用相同遠端表格中其他儲存格的內容。

儲存格式

儲存格式是一種標準的 MIME multipart/mixed 格式,主要由四個 text/plain; charset=UTF-8 部件組成,每部件包含以換行隔開的文字,並用冒號劃分資料欄位。這些部件包括:

  • meta 部件列出其他部件的型別。
  • sheet 部件列出每個儲存格的格式和功能、每個列的寬度(如果不是預設寬度)、表格的預設格式,以及該試算表中用到的字體、顏色,及邊框列表。
  • 可選的 edit 部件可保存 TableEditor 的編輯狀態,包括 ECell 的最後一個位置,以及行/列窗格的固定大小。
  • 可選的 audit 部件包含上一次編輯會話中執行過的指令歷史記錄。

舉例來說,下面是一個包含三個儲存格的試算表,A1 作為 ECell,其內容為 1874,A2 中是公式 2^2*43,A3 中的公式 SUM(Foo) 則顯示為粗體字,代表命名範圍從 Foo 到 A1:A2:

這份試算表經過序列化後,儲存格式會像這樣:

    socialcalc:version:1.0
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary=SocialCalcSpreadsheetControlSave
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    # SocialCalc Spreadsheet Control Save
    version:1.0
    part:sheet
    part:edit
    part:audit
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    version:1.5
    cell:A1:v:1874
    cell:A2:vtf:n:172:2^2*43
    cell:A3:vtf:n:2046:SUM(Foo):f:1
    sheet:c:1:r:3
    font:1:normal bold * *
    name:FOO::A1\cA2
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    version:1.0
    rowpane:0:1:14
    colpane:0:1:16
    ecell:A1
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    set A1 value n 1874
    set A2 formula 2^2*43
    name define Foo A1:A2
    set A3 formula SUM(Foo)
    --SocialCalcSpreadsheetControlSave--

上述格式在設計上可讓人直接讀取,並且也很容易通過程式生成。因此,Drupal 項目的 Sheetnode 插件即可使用 PHP 在此格式以及其他流行的試算表格式,例如 Excel (.xls) 及 OpenDocument (.ods) 之間進行轉換。

至此,我們已經簡要介紹了 SocialCalc 各個元件和組成方式。接下來,讓我們透過兩個實際的範例,一起來看看如何擴展 SocialCalc 的功能。

(未完,待續。)

March 02, 2011 at 05:02 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

WikiCalc (六之二)

WikiCalc 一問世就帶來許多獨特的功能,是同時期的試算表所沒有的:

  • 純文字、HTML,以及共筆式的文本標記支援。
  • 共筆文字包含插入鏈接、圖片,以及和從儲存格引用值的功能。
  • 公式儲存格可以引用放在其他網站的 WikiCalc 網頁裡的值。
  • 支援輸出到靜態網頁,以及將動態資料內嵌至其他網頁。
  • 儲存格能使用 CSS 來改變樣式。
  • 記錄所有編輯操作,以供稽核紀錄。
  • 和共筆系統一樣,保留每一個版本,並可以隨時回復。

從幾個小型試算表組建一個主試算表的能力,是 WikiCalc 的一大強項。舉例來說,每位銷售員可以把營業額放在自己的試算表頁面裡;然後銷售經理可以綜合這些資料到該區的試算表中,之後銷售副總再綜合各區域的數字,構成主試算表。

每次有試算表更新時,所有綜合它的試算表都會即時反映出這次更新。如果主試算表的讀者想要瞭解更多細節,只需要點擊鏈結,即可查看試算表後面的試算表。這項功能讓使用者不再需要在多個地方更新數字,從而減少了多餘而容易出錯的操作,並確保所有資訊的視圖總在最新狀態。

為了即時重新計算的需求,WikiCalc 採用了輕客戶端的設計,將所有要顯示的資訊都放在伺服器端。每個試算表在瀏覽器上以 <table> 元素呈現;編輯儲存格時,瀏覽器會發送一個 ajaxsetcell 指令到伺服器,然後伺服器告訴瀏覽器哪個儲存格需要更新。

當然,這項設計依賴於瀏覽器與伺服器之間的快速連接。當網路出現延遲的時候,使用者在更新儲存格和看到它的新內容之間,會看到 Loading... 訊息頻繁出現;這問題對於需要即時調整、預覽公式結果的使用者特別嚴重。

此外,因為 <table> 元素與試算表有著相同大小,一個 100x100 試算表會在 DOM 裡創建上萬個 <td> 元素,大量消耗瀏覽器的記憶資源,進一步限制頁面的大小。

由於這些缺點,雖然 WikiCalc 作為在本地主機運行的獨立伺服器時尚稱實用,但要當作網頁內容管理系統的一部分,卻超出了它的能力。

在 2006 年,Dan Bricklin 與 Socialtext 團隊開始開發 SocialCalc 專案,用 JavaScript 語言對 WikiCalc 原本的 Perl 源碼作出改寫,目標是能支援大型試算表、分散式協作流程,以及與桌面應用程式一樣的操作界面。

這裡是 SocialCalc 的一些設計目標:

  • 處理十萬個儲存格的能力。
  • 進行編輯操作時提供快速回應。
  • 客戶端的稽核紀錄和還原/重作支援。
  • 善用 Javascript 和 CSS ,提供完整的視覺呈現功能。
  • 用 Javascript 提昇效能,並加強對各種不同瀏覽器的支援。

經過三年的開發和發佈許多次測試版之後,Socialtext 在 2009 年釋出 SocialCalc 1.0,成功實現了設計目標。現在,讓我們來看看 SocialCalc 的系統架構。

(未完,待續。)

March 01, 2011 at 09:33 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

SocialCalc: 緣起 (六之一)

(This is the first part in a series of Chinese translations of the SocialCalc chapter in the upcoming book, The Architecture of Open Source Applications.)
(本文採用 CC0 條款,屬於公共領域。作者的全部版稅將捐贈給國際特赦組織。)


試算表的應用歷史,已經超過 30 年了。

第一個試算表程序 VisiCalc 是由 Dan Bricklin 在 1978 年著手開發,於 1979 年問世。

最初的設計非常直接了當:可以在兩個維度上任意擴展的表格,其中每個儲存格可以存放文字、數字,以及公式。公式由普通的數學運算操作以及各種內建函數組成,並能使用其他儲存格的值作為參數。

試算表的理念看似簡單,但卻可應用於各種不同的實務領域:會計、庫存清單、建模預測、列表管理... 不同用法有著幾乎無限的可能性。這些應用使得 VisiCalc 成為第一款個人電腦領域裡的「殺手級程序」。

在隨後的幾十年里,雖然 Lotus 1-2-3 及 Excel 等後繼者提出了各種改進,但核心原理都是相同的;大部分試算表都以硬碟檔案的形式保存,並在打開或編輯時讀入記憶體。

在基於檔案的模式下,協作顯得異常困難,主要難點體現在:

  • 每位使用者都需要安裝特定版本的試算表編輯器。
  • 電子郵件往來、共享文件夾,或安裝一套專用的版本控制系統,都會增加額外的管理成本。
  • 變動追蹤功能非常有限;舉例來說,Excel 無法對格式和單元格注釋內容的變動保留歷史記錄。
  • 更新模板中的格式或公式後,還需對使用該公式的所有試算表文件進行更繁瑣的手工更新。

好在一種新的協作模式出現了,可以用非常簡單的方式解決這些問題。這就是共筆模式,由 Ward Cunningham 於 1994 年開發,並由維基百科在 21 世紀前期使其廣為人知。

與檔案模式不同,共筆模式以保存在伺服器上的頁面為基礎,不需要任何額外的軟體即可在瀏覽器中編輯。這些超文本頁面可以直接相互鏈接,甚至可以包含某個大型頁面中的部分內容。預設情況下,所有參與者都可查看並編輯最新版本,並可在伺服器上自動保存版本修訂的歷史紀錄。

受到共筆模式的啟發,Dan Bricklin 從 2005 年開始著手開發 wikiCalc。它結合了共筆系統的易於創建、多人編輯等特性,而仍然保有試算表系統中常用的視覺格式和計算理念。

(未完,待續。)

February 28, 2011 at 01:41 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

企業「人際層」: 化願景為現實(下)

(This is the second half of Social is a Layer's translation; cf. the first part.)
(Many thanks to keichii@freebsd for suggesting the translation of PubSubHubbub.)

運用現有標準,實作明日願景

為了實現「人際層」的願景,時下正出現一組新興協定,讓人們跨越各項工具之間的斷層,跟上彼此的最新動態。

看著開發者社群將這些標準和協定,逐漸交織成嶄新的人際層,著實讓筆者雀躍不已。

標準與自訂內容格式

新興的「動流(ActivityStrea.ms)」協定,提供一組標準的語彙,用來涵括社會軟體的各項常用活動:加為朋友、成為粉絲、發表、編輯、說「讚」等等。

企業若建置這項標準,即可越過組織和系統間的藩籬,集成各系統間「訂閱更新」、「加入書籤」等動作。

動流協定原本是 Atom 標準的延伸,但目前已能用 JSON 格式呈現,因此得以嵌入到「推特批注」之中。

批注

最近推特宣布支援「批注(Annotations)」協定,作為在社交訊息內,嵌入資料及影音內容的通用格式。

以 JSON 格式呈現的動流訊息,即可作為批注,隨推特一併傳送。

對企業來說,由於批注可以承載任何資料,因此很適合用來傳遞各式系統的自訂訊息。

互動式訊息

社交網站的現況更新,目前大都以靜態訊息為主,但某些互動訊息也開始興起(如「按我轉噗分享」等)。

企業用戶也可利用動態訊息,來銜接既有的業務系統(如「按我核准這張申請單」等)。

雙向即時通訊

透過「博溯互播(PubSubHubbub)」協定,可將訊息即時饋送到網絡上的訂閱者手中。

開發者也可使用「網勾(WebHook)」協定,為系統自訂事件通知,以供用戶訂閱。

若將兩者搭配運用,那麼各個系統毋需主動擷取更新,便能隨時保持同步。

舉例來說,新興的「鮭魚(Salmon)」協定,便結合了上述兩項技術,來讓作者在訊息發佈後,只要有人回應,無論回應發佈在哪個網絡裡,都能立刻獲得通知。

以上這些新協定,都遵循著萬維網的架構理念,跨越了組織和系統間的鴻溝,為人們提供即時的警示與互動。

身分與認證

企業界和私人使用的協定,在這方面的差異最大。

稍有規模的公司,大都藉由自己的 LDAP/ActiveDirectory 目錄伺服器,來提供身分及認證服務。

登入企業防火牆裡的應用系統時,往往會需要單一登入的解決方案,近年來逐漸風行的「安全宣示標記語言(SAML)」即是一例。

對於私人的社交網絡來說,臉書已成為主要的身分提供者,分散式的 OpenID 系統則與其分庭抗禮。如果臉書能繼續積極參與制定網路標準,這兩項協定很有可能逐漸整合。

此外,針對內部網路設計的單一登入系統,並不適用於供私人用途的網站服務上;後者因此更需要由 OAuth 協定提供的委託認證機制,讓使用者免於不斷鍵入帳號密碼之苦。

我想,隨著企業對防火牆外雲端服務的倚重,以及部門間更緊密的系統整合需求,網際網路的各層新興協定,未來當會有更廣泛的的企業應用。


多層協定,熔於一爐

以上所述的各層協定,需要彼此合作無間,才能實際發揮效果。

為此,新興的 OStatus 專案便建立了結合多層協定的使用案例,來提供各項服務間的互通測試。

這些協定一旦成功互通,人們便能擺脫特定工具的限制,在網路世界裡自由進行社會活動。

而對企業來說,上述願景的中心思想,就是運用「人際層」的架構,來連繫互相無法溝通、徒有社群功能的眾多內部服務。

對網路標準及互通性的良好支援,則是達成這個理念的關鍵。

「人際層」跨越應用程式與科層組織間的藩籬,將人們直接連繫起來。

「人際層」讓組織能更順利地解決當前的挑戰,為企業創造新的價值。

July 04, 2010 at 02:18 AM in Web/Tech | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

Next »

About

Map

  • Locations of visitors to this page

Recent Posts

  • [活動] 7/22 下午【攻殼機動隊】
  • Socialtext 應用 Scrum 方法的三年(下)
  • Socialtext 應用 Scrum 方法的三年(上)
  • 開發經驗談 (六之六)
  • 即時多人協作 (六之五)
  • 豐富文本編輯 (六之四)
  • SocialCalc (六之三)
  • WikiCalc (六之二)
  • SocialCalc: 緣起 (六之一)
  • 企業「人際層」: 化願景為現實(下)

Recent Comments

  • audreyt on Socialtext 應用 Scrum 方法的三年(上)
  • OOBE on Socialtext 應用 Scrum 方法的三年(上)
  • Ali on Socialtext 應用 Scrum 方法的三年(上)
  • randomly on 鳳たんです!
  • audreyt on 心慟凡例 / An Instance of Sinthome
  • audreyt on 心慟凡例 / An Instance of Sinthome
  • Johann Chiang on Our paroqial fermament, one tide on another.
  • Kim Feraday on Our paroqial fermament, one tide on another.
  • twitter.com/lazinet on 文章造天下,功業還蒼生。
  • Eric Hellman on Our paroqial fermament, one tide on another.

Categories

  • Books
  • Craft
  • Current Affairs
  • Film
  • Lingua
  • Meta
  • People
  • Trans
  • Web/Tech

July 2012

Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Archives

  • July 2012
  • April 2011
  • March 2011
  • February 2011
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • November 2009
Subscribe to this blog's feed

License


  • Public Domain Dedication
    This work is dedicated to the Public Domain.

Audrey

  • Audrey060602
My Photo

Twitter

    follow me on Twitter