over 5 years ago

這週繼續在實作 http://ruby-taiwan.org 上的 features。

既然要打造一個讓程式設計師很願意發表的社區,介面就要對 developer content 非常 friendly。

如何友善法?除了發文介面要做的棒,讓人很容易發表文章、寫回應之外,還要容易貼 code!社區不能貼 code 還叫程式社區嗎?

Topic 與 Reply 支援多種語法、程式碼高亮

所以這週的主要進度是:Topic 支援程式高亮了。(by @yorkxin ),詳細的內容在這篇公告:新增多種語法支援:粗體、斜體、底線、程式碼高亮

Wiki

Wiki 是當初第二個看不順眼的東西。

在程式社區中 Wiki 也算是很重要的角色,但是一個爛的介面和動線也會讓人不怎麼想要貢獻內容。

http://ruby-taiwan.org 的 Wiki 其實還是只具備非常初階的功能,底層是用 DB 實作,而表單也是簡單的用 Bootstrap 套,雖然支援 Markdown 語法,但 Wiki 內連結什麼都還是要自己 hard link。貼程式碼也容易貼的歪七扭八…

理想的程式社區 Wiki 我覺得要具備幾個要素

  1. 內容可以實現 [[ XXX ]] 站內超連結
  2. Markdown 或 Markdown extra 以上功能
  3. 貼任何程式碼都支援高亮
  4. Version Control ( 基本 )
  5. 可 Diff
  6. 權限控管
  7. 容易「預覽」的介面…

不過這些願望就算是自己用 Rails 實作,也很苦手。因為有一大堆底層苦工需要先作…

我是個很討厭重造輪子的人,馬上就想起以前曾經用過的一套 Wiki : gollum

以前曾經寫過一篇文章介紹過這套 wiki : 用 Pow 架起 Gollum

Github 官方部落格上面的介紹:Making GitHub More Open: Git-backed Wikis

gollum 的基本想法

gollum 的想法是將 Wiki 的每一頁都視為一個檔案,塞進 Git 版本控制。這與傳統的 Wiki 作法不同,多數的 Wiki 實作是將每一頁視為一筆 DB record,而且還要另外拉一個表紀錄版本變遷(有的 Wiki 甚至沒有這樣功能)。

而用 Git 實作的好處,是檔案儲存、變更、版本控制、搜尋,就可以走 Git 本身的機制,不必再重造一套輪子了。所以當初看到 gollum 釋出的時候,受到很大的震撼,沒想過原來可以這樣惡搞閃掉重造輪子。

gollum 的基本功能

gollum 提供了一套 API,可以存取,版本控制,搜尋,自動將特定格式內容轉換。(支援 Markdown、RST, ASCIIDoc…等等)

而且 gollum 還提供一套用 Sinatra 刻的介面,可以讓人輕鬆的寫 Wiki…

還是要重造輪子

寫到這裡,可能會讓人以為我的作法可能是會將 gollum 當作一個 rack app 掛在 http://ruby-taiwan.org 就收工?

gollum 的介面頂多是勉強堪「自用」,所以在我自己的 Mac 上,我的確是用 gollum + Pow 架起來寫私人程式筆記的。但是 gollum 的 wiki 介面,我自己覺得連易用都摸不上邊...(更別提 1.3.x , web preview 介面是爛的 …)

再來是 gollum 並沒有權限與使用者概念,如果是用 web interface 發表,走的都是 local git config 的 user name…

終極 solution

以前自己寫過一個小專案,用 Rails 刻一套介面去接整套 gollum API,自己把 UI 改的 friedly 些。讓寫筆記的速度可以加快,直到 Mou 出來之前,我一直都是這樣寫東西的…

要達到剛剛所說的六個重點,於是最後實作的解法就是…

寫 Rails 去接 Gollum API…

Model

https://github.com/rubytaiwan/ruby-taiwan/blob/production/app/models/wiki.rb

  • 檔案存取 / 版本控制:把整個 db 抽換掉,換成 gollum
  • 與 bootstrap form 結合:因為 http://ruby-taiwan.org 是用 simple_form 實作表單,所以 Wiki 的部分是寫了一個 class 去 include ActiveModel 的一些 API 去接上 form

Controller

https://github.com/rubytaiwan/ruby-taiwan/blob/production/app/controllers/gikis_controller.rb

  • CRU 接了 gollum API 去實作。
  • 寫入 commit log 則取站上認證的 current_user 去塞

權限控制

https://github.com/rubytaiwan/ruby-taiwan/blob/production/app/models/ability.rb

  • CanCan 去管基本的存取。若以後要上黑名單,細緻權限,鎖定,都可以從 CanCan 這端寫 rule block 掉。

Markdown Support & Syntax Highlight

https://github.com/github/gollum/blob/master/lib/gollum/markup.rb

  • gollum 本身就是使用 Redcarpet 去實作 Markdown 的 renderer,所以吐回來的 formatted_data 已經會是支援 Markdown Extra 以上的格式了。更棒的是也自動結合了pygments.rb

Preview

https://github.com/github/gollum/blob/master/lib/gollum/wiki.rb
https://github.com/rubytaiwan/ruby-taiwan/blob/master/app/assets/javascripts/topics.coffee

  • 本來在煩惱 Page Preview 要怎麼實作…真不想自己寫 renderer 硬幹。後來發現 gollum 也提供了 preview_page,提供 in-memory preview。而 @yorkxin 這兩天才為 Topic & Reply 寫一個 ajax preview。就用同樣的一套 interface 也接上去…

Backup

  • 用 Git 實作 Wiki 最大的好處,是可以把內容都塞進一個資料夾內。再定期跑 crobtab 丟上 github …(這是其他 wiki 很難做到的地方)

踩到的一些雷

其實事情也不是大家想像中的順利。主要還有遇到兩個地雷。

https://github.com/xdite/gollum/commit/7924e8c9a90a772d90da5f7c6a3366bfc5010fbb

  • Redcarpet 的介面改變。原先 Redcarpet 1.0.x 是走 Redcarpet.new(data, *flags).to_html 這種介面。而 Redcarpet 2.0.x 是走 markdown = Redcarpet::Markdown.new(html_renderer)data = markdown.render(data)。gollum 沒有鎖 redcarpet 版本,然後就大爆炸了。目前官方沒有 release 新 gem 的打算,只好自己 hot fix 然後拉 pull request 回去。

https://github.com/mojombo/grit/commit/696761d

  • Ruby 1.9.2 在塞中文內容進檔案時會爆炸。主要是 Git 這套拿來存取 Git 的 Ruby Library,會遇上 UTF8 string 長度問題。同樣的,官方目前沒有 release 新 gem 的打算,不過 patch 已經進 master ..

小結

所以為了作這個 Wiki 功能,用了..

非常神經病的列表…

不過出來的架構和最後呈現算是令人滿意...:D

歡迎用看看吧 http://ruby-taiwan.org/wiki

 
over 5 years ago

這篇文章是看完陳鐘誠教授寫的「HTC 最需要的軟體能力」的文章後,自己的感想。

坦白說,我真的覺得這是一篇相當天真的文章。

[在看下去之前,先警告讀者本文是一篇 TL;DR 的文章。]

Thoughts on Flash

上個禮拜網路界的熱門是,Adobe 宣布放棄了繼續在 Mobile Browser 上 Flash 的繼續開發(見 36kr 的 有关Flash移动浏览器插件,Flash平台和Flash未来的几点澄清 )。這篇文章提到幾件重要的事:

  1. mobile 版上的 flash 不可能完成 flash 在 desktop 上的規模偉業
  2. HTML5 可以在 mobile browser 完成類似 flash 在 desktop browser 上能做出來的成果
  3. 使用者在 mobile device 上的閱讀的需求與體驗和 desktop 上有著很大的不同
  4. mobile 版上的 flash plugin scabilty 不好 ( 這與硬體有很大的關係 )

這時候,再回去看去年 Steve Jobs 當初寫的那一篇 Thoughts on Flash,儘管當時很多人罵 Steve Jobs 霸道,為了保護自己的商業利益、不惜故意扭曲放大 Flash 的缺點云云。

你不得不能說 Steve Jobs 其實當年其實是講了真話,這是「他看見的事實」。只是很多人「當時還沒有自己跳下來作,沒有那個深刻體會」,沒有辦法接受這個論點。

mobile flash 致命的缺點

如果你是蘋果的長期使用者的話,應該知道蘋果最 care 的其實不是賺錢,而是「使用者體驗」這件事。(別鞭我)

mobile flash 是個 container(iOS) 中的 container ( flash ) 這件事,其實沒什麼不好。就如同現在普遍的解決方案也是 container (iOS) 中的 container (HTML5 in Browser)。** Flash 平台上就跟 HTML 一樣有著累積已久的解決方案和人才,不是 Native API 短期可以衝出來的。**

但 Flash 的缺陷就如同 Jobs 所說的一樣,無法解決或者是解決速度緩慢:

  1. Flash 無法夠流暢的在智慧型手機上運作。
  2. Flash 在 Mobile 的版本幾乎要被重寫過,這件事情讓「累積方案」看起來沒有那麼值錢了。
  3. 使用者操作的行為不一樣。在 Desktop 上只有一種行為,也就是「滑鼠行為」。但 Mobile 上重要的是「手勢」。
  4. 耗電與效能問題。Flash 想作為 container 中的 container,但是要花費大量額外的人力去對硬體客製,否則無法達成目的。而耗電與效能正是智慧型手機上最被開發者和使用者所在乎的事情。
  5. Native API 的支援。想要做到幫助開發者做到寫跨平台程式,平台支援 Native API 的速度至關重要,但 Adobe 這部分的速度慢的要死。

所以 Jobs 才會在該篇文章的後面,這麼結語:「希望 Adobe 應該將焦點多放在製作 HTML5 的工具上」。我相信這是他的真心話。

Thoughts on HTML5 & Mobile App

除了 Flash 之外,能夠解決「跨平台」這個目的的,就只剩下 HTML 這個方案了。

如果想在智慧型手機上開發軟體不那麼事倍功半的話,只有一個策略,那就是支援標準。

為什麼影音市場會傾向 H264,跨平台媒體方案會傾向 HTML5。因為「硬體」廠商和「OS」廠商會支援「標準」並可能實作「加速」,開發者就不需要那麼累的反而去對「每個平台」作客製。

有人打趣說寫 Mobile Web 真是愉快,因為開發者不需要像在 Desktop 的環境時,面對那麼多不同的 browser (特別是 IE6)。在智慧型手機上只有一種 browser,那就是 webkit-based。

Mobile App 上的實戰

前陣子自己實際寫了一套 Mobile 上的電子出版架構,更能深刻體悟到為何現在多數的工具型 App,其實都是 Native API 與 HTML5 混搭的策略。

  1. Native API 門檻太高。Native API 要練成絕世高手,絕非一朝一夕。但 mobile apps 的市場需求又遠超過開發者市場的供給。
  2. HTML 上累積的解決方夠多。在 mobile apps 上介面上實作一段很絢麗的跳出框或特效,用 Native API 可能要刻上一週甚至更久。但是如果使用 jQuery 在 HTML 上實作就不用。
  3. 使用 HTML 的開發者夠多。撰寫 HTML 的門檻比起 Native API 門檻實在太低了,開發者容易培養訓練。

實質的解決方案:Titanium

Titanium 就是這樣的解決方案,簡介可見 跨平台移動應用程式的解決方案 – Titanium

多數的開發者的策略轉變成,以 Titanium 寫出符合 HIG 或者是 Mobile App Best Pratices 的原生介面(按鈕、流程),但媒體內容卻全以 HTML5 實作。

Titanium 主要的開發語言是 JavaScript,開發者可以透過 JavaScript 撰寫 function,交由 Titanium 轉換編譯成平台上的原生原始碼。

好處是:JavaScript 原本就是 Web Developer 平日使用的工具之一。開發者只要專心與 JavaScript / HTML / CSS 打交道即可,而它們都是「標準」。

這樣就可以將 Mobile App 的開發工作拆得更單純,讓 container 上的開發歸 container ( iOS ),媒體內容歸媒體內容。

真正的決戰場在螢幕尺寸

坦白說我看 「HTC 最需要的軟體能力」此文,最難以理解的是這一段

更棒的是,程式設計師將不再需要為每一個平台撰寫一套程式,一個以 HTML5 為主的程式,可以同時在「iOS, Android, Windows」 等作業系統中執行,也可以跨越「手機、平板、筆電、桌電」等裝置的限制,成為名符其實的「Write Once, Run Anywhere !」的跨平台系統。
但是 HTML5 畢竟只是前端的顯示技術,這個技術並沒有制定出關於後端的標準,因此要能用 HTML5 統一「手機、平板、筆電、桌電」等裝置,仍然有一塊標準技術上的空白之地,而這塊領域也正是台灣廠商可以深耕的領域,這就是以 HTML5 為核心的伺服端技術。
這種伺服端技術不只可以用在傳統的桌上型電腦上,更可以直接用在「手機與平板」電腦當中,舉例而言,我們只要撰寫一個簡單的小型伺服器,放在手機上常駐執行,當 HTML5 網頁需要執行系統功能時,就用 AJAX 或 WebSocket 的方式,呼叫這些小型伺服器,以便執行系統功能,並且傳回系統相關的資訊,如此就能讓 HTML5 程式完成幾乎所有原本只有作業系統才能完成的功能,成為名符其實的「WebOS」。

What The H…

我不確定作者知不知道自己在說什麼?但 Mobile App 與 Desktop App,甚至是 Mobile Web 與 Desktop Web 上開發的挑戰根本不是 OS 的 API 問題。在不同平台上,使用者與平台互動機制與媒體的需求是完全不同的兩回事。

「Write Once, Run Anywhere !」沒什用,因為不能「Use」就沒有用。

Again:問題在於螢幕尺寸造成的 render 效果差異,和 device 不同的輸入互動模式。

Responsive Design 不是終點

對於螢幕尺寸造成的 render 效果差異,有人提出了「Responsive Design」這個概念。

Responsive design 是一個全新的設計概念,開發者可以使用 CSS3 的 media query ,去對不同 device 的寬度去對 HTML 作出不同的 styling。

很理想對吧?

我手上有兩個以上採 Responsive design 的 websites。還有一個採 Responsive design 的 mobile app。

實際開發出來進行維護(可以看 T客邦Digiphoto )才發現這也只是個理想國的概念。

Responsive design 在 iPhone / iPad App 上的確很威,只要寫四套 CSS 就可以解決所有的問題。(iphone 直排/橫排, iPad 直排/橫排)

有些開發者說,Mobile Web 上面沒有惡魔 IE6 了 YA!

錯了,真正的惡魔是 Android。

Mobile 開發上的大惡魔:不同尺寸的 Android 手機與 Android 平板

網頁開發者痛恨 IE 系列的原因是,明明寫的是正確的 CSS。但是 IE 就是會 render 出詭異的結果。

而不同尺寸的 Android 手機 / 平板,給開發者的惡夢就是:「無論你怎麼排版,View 就是會爆炸。」

今天在 Samsung 平版上看可能沒有問題,但是在 HTC 平板上,menu bar 可能就爆掉了。

使用者只會要求在它的 device 上的體驗要是完美的。

當然,PM 也不是沒有好心的告訴我一些他認為「可能」可以解決這樣問題的方案。比如砍字!

在 Flyer 上 menu 可以是「拍攝技法」、「哈燒新品」。但在 Sensation 上 menu 可以變成是「技法」、「新品」!

這是解法嗎?不是。Responsive 是提供 CSS styling 的解法,而砍字這個解法是要求我偵測 Agent 用程式去解決。(我知道可以用 responsive design 去另包元素作 display none; 我不可能配合,這是改動結構。因為使用者提出的需求不僅是這樣而已,還有加字、改 bar、改設計。簡直是瘋了。這樣偵測 Agent 重寫一版網頁版程式還比較快)

簡而言之,Responsive Design 遇到 device 的直排/橫排的情況是很棒的解法,但是它無法解決內容一直在變動,Device 尺寸不一的問題。

Desktop 瀏覽器視窗放大縮小造成破版的問題,使用者不會 Care,完全不是問題。但在 Mobile 瀏覽器上,這就是 Bug!

螢幕的尺寸不一破壞了軟體生態圈

這個世界已經漸漸告訴我們,智慧型手機的戰場不在於硬體規格,也不在於 OS 威不威。因為硬體和 OS 系統商「不可能自己做完任何事」。所以拼的就是軟體生態圈的品質。

消費者買智慧型手機,不是買 featues,而是想買「體驗良好」、「解決需求」的「app solutions」。

好的軟體生態圈才有辦法造出這些 solutions。

如何培育軟體生態圈?

其實開發者心聲多數相當單純:「少給我找麻煩」、「讓我可以賺到錢」,而不是「這技術門檻有多低」。

如果把開發者搞得半死不活,絕大精力都在處理硬體和 OS 製造出的愚蠢 bug。開發者也要吃飯,也要領錢,沒有人會願意餓著肚子陪你辦家家酒的。

而螢幕的尺寸不一就足夠把 Mobile App / Web Developer 搞垮

小結

其實不難規結,開發 Mobile App 的重點是什麼:

  1. 流暢 (UI / Network Latency)、低耗能、高效率
  2. 支援標準
  3. Native 與 HTML 技術混搭
  4. 固定螢幕尺寸
  5. 符合 HIG 標竿的工藝
  6. 可以賺到錢

HTML5 可以解決這當中的一些事情,如 (1) (2) (3)。但要做到「Write Once, Run Anywhere !」,跨越「手機、平板、筆電、桌電」等裝置的限制。看看目前的 Android 機海(4吋,7吋,10吋)情況,只能說算了吧…

而且這四種 Device 的需求原本就不同,HTML5 不是大靈丹。

HTC 最需要的軟體能力 是什麼?

我想絕對不會是往 HTML5 「微型伺服器」 這樣的方向,何況我也聽不太懂這是什麼東西。

 
over 5 years ago

OmniAuth 是在 2011/11/2 正式釋出 v1.0 版的,其實也就是不久之前而已…

這個大改版也讓我吃足苦頭,因為 0.3 與 1.0 的架構差太多,網路上 Google 到的文件反而會打爆我的 application,除錯了很久。

不過也因為這件事情,逼我在幾個小時之內認真看了不少關於認證的想法與文件,才從而理解這些架構,寫出這些文章...。

0.3 與 1.0 的差異

0.3 與 1.0 的差異,在於 1.0 的架構更直覺、更乾淨。主要改變有二:

  • Strategy as Gem
  • 標準的資料介面

Strategy as Gem

在 0.3 版,如果開發者想在 Rails project 內使用 Twitter 的認證。除了必須在 Gemfile 內宣告使用 OmniAuth

Gemfile
gem "omniauth"

還必須使用一個 initializer 去 claim 他要使用 Twitter 認證

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
end

而在 1.0 中

開發者只需要這樣作,在 Gemfile 裡面宣稱他要使用 OmniAuth 和 OmniAuth-Twitter

Gemfile
gem "omniauth"
gem "omniauth-twitter"
注意事項

其實也是這個細微的差異,害我誤入歧途,我按照網路上找到的方案,也在 1.0 版乖乖多寫了一個 initializer 去宣告使用,反而造成 invalid credential。

OAuth 惡夢

當然將 Strategy 抽出來當成 Gem 是一件好事,這樣就不用因為某一個認證方 API 改版,就必須 fork 整個 OmniAuth 出來使用。

不過促使 OmniAuth 改版最痛的問題可能是 OAuth 的問題。

我也是因為實作 Github 的認證才發現到這個令人憤怒的 issue。

[BTW: *講個不好笑的笑話,開發與 Github 結合的產品真的很令人困擾,因為你無法使用 "xxx github" 這樣的關鍵字在搜尋引擎找答案,因為結果一定會出現 xxx 在 Github 上的 repo。翻桌!=_=||| *]

這是 OmniAuth 0.3 版當時的 oa-oauth 目錄夾。你可以各家看到關於 OAuth 的認證實作多麼的混亂,逼得開發者只好寫出超多不同的 solution 去應對!

而 Github 雖然號稱已經支援 OAuth 2.0 (賀!?),但令人崩潰的是,它也不是標準的。Rails in Action 作者 Ryan Bigg 在寫作此本書範例程式的認證部分時,曾經憤怒的發現這一個事實,並把故事始末紀錄在這篇文章「Whodunit: Devise, OmniAuth, OAuth or GitHub?

OAuth 2.0 的規格說,token param 必須命名為 oauth_token,而 Github 的卻叫作 access_token。

而且 Github 不打算修這個問題…

所以如果在 0.3 版的 OmniAuth,你必須要 hack OAuth2 這個 gem 才能支援 Github...。而如果你同時要想支援 Facebook 認證,那就哭哭了 T_T

小結

而既然大家都這麼亂搞,不如把 Strategy 通通抽出來讓開發者在自己的黑箱內惡搞,可能還是比較快的一個方式....

標準的資料介面

在 OmniAuth 拿資料的方式是存取 env["omniauth.auth"] 這個變數,裡面會回傳認證需要的參數與資訊。

這個目錄 含蓋了所有目前 OmniAuth 0.3 支援的 Strategy,可以看到大家的 auth_hash 通通都寫的不一樣,也是各自為政。

所以在 1.0 版,OmniAuth 將強迫大家走同樣的規格回來,這些使用者資訊將會切成四種 DSL methods : info, uid, extra, 和 credentials

這個部分在上一篇談架構時已經寫過,就不再重寫一遍了。

小結

經過這一番折騰,最後我還是成功用 OmniAuth 1.0 實作了 Github 認證。我將在下一節中示範如何整合。

但如果你還是想要用 0.3 實作 OmniAuth + Github 。我推薦你可以參考這篇 Stackoverflow 上的 GitHub OAuth using Devise + OmniAuth 討論。

  • Ryan Bigg 有個 ticketee 的 project 可以參考。(我不保證它能動)

  • Markus Proske 也有個 omniauth_pure 的 project 可以看。

Good Luck!

 
over 5 years ago

OmniAuth 本身並不是一套被限於特定框架、特定認註冊系統上的認證方案,而是一個基於 Rack 的「認證策略提供者」。

主要架構

Provider

OmniAuth 將所有的認證提供方,通通視為不同的 Provider,每一種 Provider 有一個 Strategy。不管你是 Facebook、還是 LDAP,通通擁有各自的 Strategy。

Strategy

每一個 Strategy 分為兩個 Phase:

  • request phase
  • callback phase

而 Omniauth 提供了兩個主要的 url

  • /auth/:provider
  • /auth/:provider/callback

當使用者 visit /auth/github 時,OmniAuth 會將你導到 Github 去作認證。而認證成功之後,會 redirect 到 callback 網址。通常我們會在 callback 網址作 session create 動作(透過拿回來的資料 find_or_create user)

使用 Strategy 的好處

使用 Strategy 的好處很多。最明顯的我覺得有幾點:

1.能夠將 routhing 切得很乾淨。

這點顯而易見。

2.能夠在網路不通下繼續實作認證。

有時候開發中,可能正用本機網址,無法實作 callback。有時候則是網路不通。OmniAuth 可以讓我們使用一套 developer strategy 去 "fake"。

所以在開發過程中,即便遇到網路問題,我們還是可以透過寫 developer strategy 的方式,拿到同格式的假資料,完成假認證、假 callback。

lib/developer_straegy.rb
require 'omniauth/core'
module OmniAuth
  module Straegies
    class Developer
      include OmniAuth::Strategy
      
      def initialize(app, *args)
        supper(app, :developer, *args)
      end
      
      def request_phase
        OmniAuth::Form.build url:callback_url, title: "Hello developer" do
          text_field "Name", "name"
          text_field "Email", "email"
          text_field "Nickname", "nickname"
        end.to_response
      end
      
      def auth_hash
        {
          'provider' => 'twitter'
          'uid' => request['email'],
          'user_info' => 
          {
            'name' => request['name'],
            'email' => request['email'],
            'nickname' => request['nickname']
          }
        }
      end
    end
  end
end

( 這是 0.3 範例,出處為 OmniAuth, 昨天今天明天

而新的 1.0 Strategy Guide 已經 釋出,一個 Strategy 需要完成的部分大致上有這三個:

  1. request phase 如何完成
  2. callback phase 如何完成
  3. 定義回傳需拿到的資料:如 provider name、uid、email、以及 extra info

User Info

在 0.3 版的範例裡面,可以看到回傳的資訊是使用 auth_hash 去包。這也導致了另一個混亂的情形,各種不同的 Strategy 寫了不同的 auth_hash,把 auth_hash 拉回來時,create User 的介面相當混亂與醜陋。

而自 1.0 版起,這些使用者資訊將會切成四種 DSL methos : info, uid, extra, 和 credentials

class OmniAuth::Strategies::MyStrategy < OmniAuth::Strategies::OAuth
  uid { access_token.params['user_id'] }

  info do
    {
      :first_name => raw_info['firstName'],
      :last_name => raw_info['lastName'],
      :email => raw_info['email']
    }
  end

  extra do
    {'raw_info' => raw_info}
  end

  def raw_info
    access_token.get('/info')
  end
end

把基本資訊的存取切分的更清楚。

讓我把各家新版的 Strategy 翻出來介紹給大家吧:

看完這些 example,相信你可以更了解這些資訊架構後面的想法是什麼。

小結

而因為 OmniAuth 是 rack-middleware,且介面單純的緣故( 兩組統一 url),因此可以接在各種任何支援 Rack 的 Ruby Web Framework 上,在這一層之上就完成握手交換資訊的互動。於是整個認證過程就可以與「框架」和「框架上的傳統認證方案」完全切割分離,開發者可以透過這兩組介面完成傳送與接收資訊的動作,而不需像傳統實作,必須大幅客製 controller 與 routing 遷就 provider。

下一節我將繼續介紹,為何 OmniAuth 要自 0.3 大幅改版至 1.0 。

 
over 5 years ago

這幾天在寫一個小玩具,因為要用到 Github 認證,於是採取了 OmniAuth + Devise 這組作法。因為適逢 OmniAuth 在十月底一舉從 v0.3 大改版,直衝到 v1.0。版號大躍進,整個架構與 API 也幾乎全都不一樣了,網路上的教學幾乎等於作廢,加上 Github 原本實作的 OAuth 2.0 本來就不太標準,吃了幾個小時苦頭,終於才把認證搞定。不過也拜這一晚的折騰,讓我把 OmniAuth 架構摸個更加透徹。

多方認證的需求

現在作網站,使用者的要求比以往的高。在過往,幾乎都是站方的姿態較高,使用者要試用一個網站前,無不是必須填一堆資料,勾完一堆囉哩八縮的選項,才能加入這個網站。

但隨著時代的改變,Facebook Connect 的普及,現在網路生態卻跟以前完全相反,如果你的網站不提供傳統帳號密碼以外的方案(諸如 Facebook Connect、Google ID …etc.)使用者二話不說,絕對馬上就閃人。反正網站那麼多,不差哪一個…

於是提供傳統帳號密碼以外的註冊方案,對一個新創網站就顯得格外重要。

實作上的困難

話雖如此,但是實作上是真的有很大的困難的。就拿 Rails 生態圈好了,傳統帳號密碼方案 有非常多套:Devise、Authlogic、Restful-authenication 等等。而實作第三方認證的功能也是相當多元的,你可以拿 Facebook API 的 gem 或者是 Google OAuth 的 gem 直接硬幹整合這些方案,也是做的出來。

理想的境界應該是一個網站最好只要提供一個 3rd Party 的認證,而且認證 Library 與 API 存取機制,不能要有太大的變化。

但這真的只是理想而已,現實上你會遇到三類大挑戰:

1. PM 亂開規格

PM 不會管你死活,硬是要你同時既提供 Facebook / OpenID / Yahoo Auth / Google OpenID。天知道這些網站認證和存取 API 的規格完全都不一樣。

硬是把這些方案一起塞到一個 controller 和同一個 model,瞬間就會無法維護。不…很可能是 code 亂到讓自己狂跌倒,直接作不出來

2. OAuth 版本規格間的問題

理想的境界應該是大家都走 OAuth 就能夠解決問題,但是 OAuth 1.0 推出時,鬧了一個大笑話:被發現有 security issue。於是 OAuth 推出了 1.0a 的修補方案,但這又衍生出另外一個問題:每一家解決 security issue 機制完全不一樣。

因為 service provider 的機制完全不一樣,就造成了已經上路使用 OAuth 的網站大囧。因為 1.0a 那步要變成客製。其實大家做的調整都差不多,但當時有一家是來亂的:Yahoo ….

因為 Yahoo 實在太特例,還造成當時 OAuth 這個 rubygem 的作者,拒絕支援 Yahoo(因為要做的修改不只是「小」修改而已)。

這件事實在是太囧了,於是 OAuth 在不久後,又提供了一個解決方案:直接提出 OAuth 2.0!

鬧劇到這裡就結束了嗎?

沒有。

因為 OAuth 2.0 不相容 1.0a 及 1.0 …

好吧,那算了,大家還是繼續裝死使用 1.0x …

還沒有結束喔!

原本完全不鳥 OAuth 的 Facebook 這時候宣布即將放棄自己的 Facebook Connect 架構,宣布未來直接擁抱 OAuth 2.0。

崩潰。一個專案上跑 n 種 OAuth library 是什麼鬼....

[** 如果你是沒什麼信念的 Web Developer,看到這裡我建議你可以轉行 **]

3. 大網站本身直接的 API 改版以及認證機制的改變

一個網站只要還沒倒,就不可能一直停滯不前。更尤有甚者如 Facebook,它的 API 更是三天一小改,五天一大改。而 FB 的 認證架構 和 API 一改,相對的 library wrapper 就一定會跟著改。

這就苦到那一些直接使用 library 接認證的開發者。

而 FB 改版就已經夠令人苦惱,其他網站不可能也像一攤死水,Google 也改很大....。從之前只是
OpenID + API 存取,改成直接走 OAuth ...

你也許會問,為何要使用 library 直接接認證呢?那是不用獨立 library 接認證有時候也不太行得通,因為每一家提供使用者資訊的「方式」和「資料格式」幾乎不一樣。有時候還要分好幾步才能拿到令人滿意的結果

理想的解決方案

當 Web Developer 實在太苦了,賣雞排真的比較輕鬆 :/

理想中 Developer 們需要的解決方案應該是這樣的:

  1. 開發者不需管最底層的傳統認證方案是哪一套 solution,甚至是不只局限於 Rails 這個框架
  2. 開發者不需管提供認證方使用的是哪一套協定
  3. 開發者拿到的使用者資料格式應該是接近一致的

這套方案存在嗎?

存在,它就是 OmniAuth

小結

前言歷史寫太長了,決定拆成幾篇寫完。下集待續。

 
over 5 years ago

今天下班回家,看到 Mr. Jamie 寫了這一篇 魔球 (Moneyball):一個 Hack Everything 的棒球故事

電影《魔球 (Moneyball)》終於在台灣上映了,一樣的我也還沒去看。但我同樣的也要說,就算你沒有要進電影院看故事,我也推薦你一定要去買魔球這本書。

但我要推薦的理由跟 inside 的 fOxJamie 都不一樣。我推薦的原因是,它是一本告訴你「要成為產業贏家就必須搞清楚競賽重點」的書。

魔球是一本 2002 年出版的書,現在這個版本是因應電影上映重新出過的書。中文書第一版出的時候,我正念大二。

而閱讀這本書,從此改變了我對經營事業的看法。

不是 Hack ,而是抓住遊戲的重點

Billy Beane 的故事相信大家都耳熟能詳。Mr. Jamie 整理了五段摘要:

  • 一個老產業 + 一支窮球隊
  • 勝場數 = 營收
  • 最珍貴的資源: 27 Outs
  • 數字 > 天賦
  • 第二年

我就不重新寫了。

而 Billy Beane 的故事,讓我所領悟到很清楚的一件事是: Billy Beane 並不是個 Game Hacker,而他的成功,是因找到了這場金錢遊戲的本質。

(也許是我念數學系的關係,教授平常要求我們的不是去練各種技巧,而是去挖出問題本質,以自身累積出的理論與實力見題解題、見招拆招)

在大聯盟賺到很多錢的方法?

  • 如何賺大錢?勝場數 = 營收
  • 如何贏球?在 27 個出局數中得到最多分
  • 如何得到最多分?提高上壘率
  • 如何提高上壘率?找出厲害的球員
  • 沒那麼多錢買明星球員怎麼辦?找出上壘率高,但有其他缺陷導致薪水被嚴重低估的球員。

而 Billy 所作的也很單純,就是:「用一分錢實實在在的買下一分錢的勝利」。想辦法讓他的錢花的有價值。

而其他人的手段卻是「買明星」,而說穿了也是用「用十塊錢去賭賺一塊錢的機會。」

所有人都能靠賭博賺錢,但是真正賺到錢的機率很低,所以絕大多數的球隊也幾乎都是花了大錢,但票房勝場慘兮兮。

要從網路上賺錢,第一天就要作能有實質收入的生意

本來我也覺得大聯盟的砸錢遊戲,荒謬可笑。

直到我踏入了網路界才發現,原來這樣的劇本在各個領域內無時無刻都在上演。只是賭博遊戲變成了:

  • 如何在網路上賺錢?做出 PV 幾百萬的網站。
  • 網站如何長成 PV 幾百萬? => 做出很酷的網站 => 花大錢 Hire 明星團隊
  • 網站如何長成 PV 幾百萬? => 生出很棒的內容 => 花很多錢找人產生內容、經營社群
  • PV 沒有成長怎麼辦 => 改版增加更多更屌的實驗新 feature => RD 不夠 => Hire 更多的 RD
  • PV 沒有成長怎麼辦 => 砸錢養更多的編輯生更多的 content => 編輯不夠 => Hire 更多的編輯

一年半載下來。準備的幾千萬資本額不夠燒,就算有幾百萬 PV。等到你做到幾百萬 PV 的那一天,你才發現因為 PV 不能吃,轉換率太低,回收的利潤根本不夠龐大的人事費用,然後因為你的帳面太難看,又沒有 VC 想投你,最後就只能黯然收攤。

這不是在影射任何一家網路公司,而是幾乎所有倒掉的網路公司都是類似的劇本。只是誰的口袋比較淺,誰先死。

從網路上賺到錢的秘密:「charge for your products.」

37 Signals 的 DHH 曾經在 2008 的 Startup School 給過一場 Talk。這場 Talk 曾經震撼當時的矽谷,這個影片那時還在網路上傳來傳去好一陣子,蔚為風潮。

因為在這場 Talk 中,他講出 37signals 賺錢真正的秘方,這個秘方真的非常非常簡單:簡單到這個言論當時造成矽谷一陣騷動,那就是:「charge for your products.

想從網路上「賺錢」?很簡單,那就是先「收錢」啊!

很好笑對吧?沒想到大家都在作「慈善」,做到忘記其實公司成立的目的,其實就是要「賺錢」。

小結

更有趣的是,這分別是 2002 以及 2008 的兩個故事。至今你還可以在各行各業中看到,大家還是拼了命的在下大注賭博:拿自己的老底賭,去借大錢賭。

很多程式設計師總打趣,若寫程式賺不到錢,那就應該回家賣雞排。

若你打算開雞排店,就算你炸的雞排很難吃,你會有可能從開業第一天,就每天雞排都免費,送到有人開始覺得你炸的雞排很好吃,然後你才決定開始賣 40 塊嗎?

你不會,因為如果你這樣作,店開一個月就會倒了。

為什麼你覺得你在網路上這樣送免費雞排不會倒?

從雞排銷售數字分析廚師手藝?Hack 雞排香料祕方?

我想我從這些故事裡,領悟到的是:「你要靠賣雞排賺錢就不要免費;你要靠賣雞排賺大錢就應該努力去把雞排炸的很好吃,讓更多人主動衝來買; 而租個豪華大攤位,挖個五星級廚師、開發榴槤雞排口味,客人不一定會更多,但更有可能的是會讓你虧到死而已...」。

我不知道你會在魔球這本書這場 Talk 裡看到什麼。

希望有機會看到你想追求的答案。

 
over 5 years ago

今天在 RGBA 跟 DK 嘴砲聊 Twitter 釋出的 BootStrap 這套 CSS Framework,才雄雄想起來我前幾週寫了一個小玩具,忘記拿出來講。

我曾經在 Asset Pipeline 的重大意義:Version Control Your Asset Package 提過 bootstrap-rails 這個 gem。

Rails 社群這群狂徒當然不會放棄把 Bootstrap 扔進 Gem 做 Asset Pipeline 的機會。事實上,Bootstrap 還是大家練習包 Asset Gem 的絕佳練習對象。你可以在網路上 Google 到一堆 bootstrap-rails 的 gem,幾乎都是在做同樣的事 XD

不過 Asset Gem 包好了,剩下來的問題就是… Helper。Bootstrap 是提供了樣式,但是這些被指定了 class 的 HTML呢?

有麵包屑、列表、警告訊息、表單,別跟我說你還是想要手寫啊?

Rails Developer 要有骨氣一點,要繼續秉持著懶的寫 HTML 的精神繼續寫 Ruby XD

所以我的練習作業跟人家不一樣,我是跑去寫 Rails Helper,我把原先的 Asset Gem fork 出來 一份,然後寫了幾乎所有可以拿來練的 HTML Helper …XD

寫一般 HTML Helper 還算簡單,基本上也是拿 前輩寫過的 Helper 改一改 DOM 結構扔進去包成 Gem。

但是寫 Form Helper 就很麻煩了。因為表單元件除了 text_field 與 text_area 這兩個選項外的,實在千奇百怪,什麼鬼 case 都有。本來之前也想秉著硬幹魂硬幹到底,折騰兩個小時以後就決定砍掉放棄了。

本來想說跑去看看其他 Form Gem 如何設計,自己也來寫一套 Form Helper。結果看完第一套 Gem simple_form 我就投降了。simple_form 考慮得很周全,幾乎什麼 case 都考慮到了

要重造一台精密坦克,即便是只有 clone 履帶也可能超過我能力。

我馬上放棄自己寫 Form Helper 的念頭。方向換成鑽研如何使用 simple_form 自動產生出來的表單,改成可以直接 bootstrap 樣式的 HTML。

本來最初的想法也是直接 override helper method,這樣最快。因為 simple form 自己產生出來的表單欄位,外面還是會產生出來一堆包覆用的 styling div。

後來看完 source 繞了一大圈才發現,README 上面就有說明,simple_form 提供直接「抽換」template 的選項。

也就是可以透過 override def input 這個 method,可以達到改變 styling div 的目的。

所以我做的事就變成重寫 form builder

module Bootstrap
  
  class CustomFormBuilder < SimpleForm::FormBuilder
    def input(attribute_name, options = {}, &block)
       "<div class='clearfix'>#{super}</div>".html_safe
    end

    def button(type, *args, &block)
      options = args.extract_options!
      options[:class] = "btn primary"
      args << options
      "<div class='actions'>
        #{super}
      </div>".html_safe
    end
  end
end

override input helper 再包進自己的 Helper Gem 內。

class CollectionInput < SimpleForm::Inputs::CollectionInput
  def input
    "<div class='input'>#{super}</div>".html_safe
  end
end

寫表單再也不需要辛苦的手包 div 調整 style。

可以維持用原先 simple_form 的這種標準寫法,卻產生出符合 bootstrap style 的 form。

<%= bootstrap_form_for :post, :url => posts_path do |f| %>
  <%= f.input :username, :disabled => true, :hint => "You cannot change your username." %>
  <%= f.button :submit %>
<% end %>

如此一來,以後我 bootstrap 任何 project 就可以直接引入自己寫的 xdite-bootstrap-rails 這個 gem,一次搞定 Form , HTML , CSS , JavaScript 的問題。

繼續懶惰下去。甚至可能乾脆寫成 rails template,因為也許 template 可能才 3 行而已?

寫完這個 gem,我突然間理解為什麼 simple_form 打遍天下無敵手,因為它本身就是一套 Form Framework。其他人實在沒有再造一套輪子的必要,透過 override template,你幾乎可以造出各式各樣的表單欄位....

 
over 5 years ago

Github 內部自用的機器人 Hubot 一直是個神秘的產品,常常看到內部員工在投影片內炫耀 Hubot 多麼的屌:

Hubot1

Hubot2

但往往只是聞其聲,不見其蹤。

不過要是按照 Github 內部慣例,常常在員工投影片出現的東西,往往就是下一個 release 的標的。因此大家也都是迫不及待在等 Hubot 的釋出。按照大家的假想,Hubot 應該是某種 Perl 或 Ruby 的 client,而且 Ruby 的機率可能還大的多。

但 Github 官方 blog 貼的這篇公告文章讓大家下巴掉下來:HubotCoffeeScript 寫的.......XDDDDD

(註:以防你不知道什麼是 CoffeeScript,或者是它能帶來的好處,我曾經寫過這麼一篇文章 解釋原理以及用途 )

CoffeeScript meets NodeJS

更精確的來說,Hubot 是用 NodeJS 架構作出來的一套機器人框架,而 Github 並非直接撰寫 JavaScript,他們是直接使用 CoffeeScript。整個專案的原始碼都只有 .coffee 而已。

Support Campfire 與 IRC

目前 Hubot 支援兩種 chatroom,預設是 37signals 的產品 Campfire,也有提供 IRC 的 adapter。

Provide tons of hubot-scripts example

為了避免大家不知道怎樣擴展 Hubot 的功能,Github 在專案內提供了大概十支左右的 example,也另開了一個專案 hubot-scripts,讓大家交流和 contribute。

Hosted on Heroku

你會覺得,要跑起這樣一隻 bot,也許又要找一台機器把 bot 跑起來?

Well,這次你錯了…

Hubot 的架構被設計為可以透過 HerokuProcfile 架構掛起來,也就是可以把這一隻 Bot 養在雲端 XD ( Hubot 的文件有教你怎麼作 )

小結

昨天晚上在 Twitter 上看到幾個 Rails core team member 在講 hubot-scripts 時,才突然發現 Hubot 竟然已經釋出了。早上認真想玩時,發現整個架構竟然是 CoffeeScript + NodeJS 時,內心其實有小震撼…

目前看到的幾個是 feature project 的 network program 都是以這樣的方式誕生,如 Pow、Hubot。看來 network program 應該會越來越往 NodeJS 方向傾斜過去。

BTW,今天在玩 Hubot 時,有發現幾個比較值得注意的地方。

  1. 請使用 trunk 版。雖然官方請你直接下載有版本號的打包檔,不過大概是剛釋出,bug 有一點點多,但社群都積極的在修補,這些修補檔目前都還在 master branch 上,還沒被 tag 成 release 版本。所以跑 master 通常會比較沒問題。

  2. 扔上 Heroku 時,要記得寫 Procfile,不然是不會動的。Procfile 的 sample 在 src/templates 下。記得一定要寫,然後再開 heroku ps:scale app=1。完整 Heroku 教學放在 src/templates/README.md 下。

Good Luck!

 
over 5 years ago

我不知道設計這個 feature 的是哪位。只是簡單講一下我覺得這個 feature 畫虎不成反類犬了。

在為網站實作 SEO 改善工程時,我們通常會使用一個技巧:在 permalink 裡面塞關鍵字

這樣的設計你可以在 T 客邦 看到,也可以在 PIXNET 看到。

  • T 客邦的設計是 /1234-seo-in-url
  • PIXNET 的設計是 /5678-SEO藏在網址裡

中文是否適合塞在網址裡面,還是個見仁見智的問題,畢竟每一家處理 URL ENCODE 的方法不一樣。但

Yahoo 的網址是這樣的: /SEO藏在網址裡-7890.html。

看的出設計者應該覺得這是個 獨樹一格 別出心裁 的設計。因為全世界幾乎沒有人這樣幹啊 =_=|||

螢幕快照 2011-10-25 下午5.25.23

螢幕快照 2011-10-25 下午5.26.29

這樣你應該看出問題了吧。

各家縮網址和 auto-link 的 library 不認得中文字。因此

  • /1234-seo-in-url => 不會有問題
  • /5678-SEO藏在網址裡 => 會被 parse 成 /5678 ,但因為是動態網頁,所以還是沒問題

  • /SEO藏在網址裡-7890.html => 被 parse 成 / ,就算輸入 /7890.html 還是連不到。

「將中文放在 URL 裡且是在數字之前」的設計,會造成在某些網站社群穿透力為 0,因為貼的連結會被自動 parse 成為根本不能動的連結。

不知道為什麼這個問題沒被測到,這應該在 beta 測試就要被檢驗出來了...

===

後續 update

網友 @fauzty 說:

Android 手機的內建瀏覽器和海豚瀏覽器,就因此看不到新版奇摩新聞。Opera Mini 倒是可以,應該是 url encode 問題
 
over 5 years ago

先聲明,這一篇純粹是個人讀後感而已。不是什麼嚴肅的 Book Review。

今天 賈伯斯傳 上市了。身為一個蘋果愛用者,自然是蠻高興這件事的。因此就花了「一點時間」把這本書讀完了。

但是讀完之後,坦白說我真的算蠻失望的。若要我形容這本書的讀後感,我會說 -- 去把 2 L 悅氏礦泉水 一飲而盡,你就知道讀這本書的滋味了。

這麼好的題材,被這樣烹調實在很可惜。

沒有人說傳記「必須」要有趣,但我覺得「Jobs 傳」這本書平淡到過於異常。

怎麼說呢?也許你是 Jobs 的反對者,那麼你可能會覺得這本書 --- 很有趣。因為裡面不少八卦小故事,關於他詭異作風、負面人格描述,關於不屑別人的行為的大量描述。

但有營養的呢?我覺得很少。如果這是採訪一百多個親友、同事、仇敵得出來的樣貌,我真的非常失望。

人們最敬佩也想了解的是 Jobs 的熱情、Vision、和推動他持續創造出偉大產品背後的秘密。而 Steve Jobs 會希望作者幫他作傳,也是基於他想讓「他的孩子」們知道他「所做的一切」、還有「這一切背後的想法與意義」。

但是 Walter Isaacson,他真的不理解 Jobs,這也就是我認為可惜的地方。我在書中只見作者一直不斷的描述周遭人覺得他多詭異、多暴君,而雖然看得出來作者試圖為 Jobs 說話解套,但是一直失敗。He just doesn't get it.。

我想這也是 Jobs 這輩子始終為什麼認為別人為什麼愚蠢的原因:「He just doesn't get it.」

如果你曾新創過某個事業,並 fell in love with it。你就能從書中就會看見閃閃發光的 Jobs。他能夠夠持續偉大的原因很簡單 --- 那就是他熱愛自己的公司,熱愛持續打造偉大的成品。Willing to die for it,而他最後也作到了…

我想這也許是因為傳記作者始終是個作家。而不是個 some business founders。只要你是個 bussiness owner,曾經熱愛過某個事業,用盡了全力打造你深信的偉大的產品。你就能夠了解 Steve Jobs 的行為和心理模式。這是領人薪水,覺得別人家資金花不完的上班族很難領悟到的事:

It's simple,只要你覺得這是別人的事業。你就不會想要 -- 「做到最好」。反之亦然。

Steve 會有那麼大的動力打造產品,力求瘋狂的完美,講話時不時散發著令人攝服的現實扭曲場。這些都不是因為它是一個詭異瘋子,而是因為他真心相信且瘋狂熱愛它的事業。

用盡一切的氣力去熱愛作某一件事,去把產品打造到最好是很累的。有過這樣經驗的人,就會更敬佩 Steve Jobs,他真的夠厲害,遠遠不是熱情覺得讓人可敬而已。這也不只是熱情而已,當中還要有不動趨鞭自己進步的動力。

因為他走在世界最前端,這更加格外的辛苦。

We do want to see things behind these. 我們不是要看瘋子傳。

He just doesn't get it.

===

Reed Jobs 都可能比 Water Issacson 懂 Steve Jobs 在想什麼( according to some piece of this book )。真是可惜了,這麼好的材料,這麼平淡的一本八卦書….