前陣子因著自己的需要,做了這個『寶咖咖搜尋器』:
- 專案github repository
- 資料後端WordPress.com blog: 你今天寶咖咖了嗎
- 使用入口:github page
如果你跟我一樣看房看了數年仍是無殼蝸牛,大概一眼就看懂這在做什麼了;如果看不懂,恭喜!你的人生還粉粉嫩嫩、閃閃動人。不過請放心,本篇跟這些黑漆抹烏的完全無關,只著墨在tech stack上。有興趣了解背後悶到出汗的故事者,可以參閱這篇ptt發布文。
這個專案不大,但考量到平日龐大的工作量加上顧小孩,我能用的時間既零碎又少,能承受的維護成本非常低,所以最好不用自己host,也不需要我花太多力氣在null exception之類的蠢bug。最後的結論就是:
- Backend: WordPress.com
- Frontend host: Github page
- Frontend development: elm
用WordPress.com當作資料後端?
自從REST API整入core之後,把WordPress當作泛用CMS並不是什麼新鮮事。在這次的小專案裡,我沒有用core REST API,而是使用WordPress.com API,主要是因為個人比較熟悉,沒什麼理由不能使用core REST API。主要用到的是GET /sites/$site/posts
這個API,詳細說明請見官方文件。這個API讓我們可以指定一些條件取出想要的文章列表,例如:
- search: 關鍵字搜尋
- tag / category: 針對某個標籤或某個分類進行搜尋
- exclude: 排除某些文章
其實這樣就很像一個關聯式資料庫了吧?想像把所有資料點都用一篇WordPress文章( post )來描述,資料點間的關聯性則用tag或category來描述;加上文章本身就有內文可以放更複雜的東西,還可以加上meta、自訂類別,表述資料的能力其實非常強。
以這個專案來說,用來查詢資料的程式碼相當簡單:
-- exclude the post ID 92 since it is the announcement post. postApi : String -> String postApi query = "https://public-api.wordpress.com/rest/v1.1/sites/isthisbaokaka.wordpress.com/posts?fields=title&exclude=92&search=" ++ query decodePostQueryResponse : Json.Decoder ( List String ) decodePostQueryResponse = Json.at [ "posts" ] ( Json.list ( Json.field "title" Json.string ) ) requestQuery : String -> ( Result Http.Error ( List String ) -> msg -> Cmd msg requestQuery queryString callback = Http.send callback ( Http.get ( postApi queryString ) decodePostQueryResponse )
除了不用自己寫API外,WordPress.com非常穩定,又不用我自己host,還可以用習慣的calypso或wp-admin介面來直接編輯資料,省掉我不少功夫。
咦?這只有GET,如果需要POST / PUT / PATCH / DELETE呢?WordPress.com API是有authentication API啦 … 但要怎麼放token是個問題,端看app的使用情境而定。
Github page + CircleCI
前端host的選擇很多,個人因為習慣的原因選用Github page;除了不想自己host外,另一個重要理由是有免費的CircleCI可以用。有CircleCI我就可以在每次更新master branch時,讓CircleCI幫我用elm-make編譯並更新Github page,也就是持續發佈( continuous deployment )。
這部分可以google到很多教學,例如這篇Using CircleCI with Github Page就寫得不錯;但像這種只有一個人在弄不用考慮多人同時push的小專案不用這麼複雜。大致上是:
- 設定一個branch為Github page所用,此例為
gh-pages
- 決定一個build artifact資料夾,此例為
./build
。 - 在master branch將
./build
加入.gitignore
,在gh-pages中則不要加入.gitignore
。這麼一來,開發的時候可避免build artifacts的騷擾,Github page branch又仍可從該資料夾發佈。不過這也可以用git add -f
來達到一樣的效果,就看自己覺得怎樣比較潮。 - 設定CircleCI追蹤master branch。每當有更新,就切到gh-pages branch去編譯、上傳,省去自己發佈的麻煩。
詳細設定請看circle.yml,相當簡單。這個做法的缺點是不適合多人同時開發的情境,如果一群人同時push到master,gh-pages究竟最後是發佈哪個版本很難保證。一個折衷方法是用master / staging兩個branch,以staging為主branch,在需要發佈新版時再發一個pull request將staging merge至master;但這樣就沒有自動的持續發布了,潮感銳減。
錦上添花:Elm
elm是一套為前端開發所設計的static-typed functional language,最終會編譯成JavaScript來發佈。會選用它是因為它有類似Haskell的型別系統,透過型別安全達到極高的程式運行正確性;這麼一來,『It compiles. Ship it.』這個笑話是真的成立的。
我自己以前半途毀棄過無數side projects,回想起來通常都是在碰到很花時間的bug時宣告入土。但透過elm,不但很難寫錯,就算我過個幾個星期才回來寫也沒辦法把程式弄壞,大幅降低維護成本。有興趣可以參考這個talk: Making Impossible States Impossible
另外還有一個好處是不用花時間搞boilerplate,只需要elm package就行了。這年頭要寫個ES6的專案,光是把所有套件帶齊設定好就要花好一陣子了,npm、babel、webpack等等;這也是為什麼github上有好幾卡車的boilerplate專案,而且還在持續增加中。
有趣但小有缺憾
這個小專案有趣,而且也實際用過很多次;透過WordPress.com + Github page + Elm的組合,讓我後續維護成本極低。唯一缺憾是想做的功能沒有做全,為了避免一拖再拖造成精神上的負擔,我設定了一個死線,一旦到了這個日期就要收尾,也就是上面ptt發布文。
沒做的部分是建案反查建商。這功能主要有兩個部分:
- 資料來源
- 如何在WordPress中描述上述資料?
資料來源的部分,國內比較完整又方便取得的大概是住展房屋網;它本來就有從建商搜尋建案的功能,我又已經有建商列表了,只要寫一支爬蟲去自動巡覽列表,把查到的建案一一爬下來即可。WordPress中描述資料的部分,可以用tag來對每一個建案標註建商。這麼一來查找到建案後,帶出其tag就可以知道是哪個建商,而且還可以進一步用此tag帶出所有該建商的建案。
雖然做法已經想好,但短時間內恐怕都無暇實作吧。反正都open source了,真希望哪天起床就發現有小精靈幫我做好了啊啊 👻👻👻