Homebrew 是一套在 Mac OS X 下使用的套件管理工具,以往大家會使用 Mac Ports ,但是 port 的套件相依性太深,常常會為了裝個小套件而跟著裝上一堆用不到的相依套件,甚至造成套件版本衝突…… 因此 Homwbrew 甫一推出立刻受到大家的歡迎。

今天早上我剛好重裝 readline 套件,安裝完成後訊息提示我 readline 套件是 「keg-only」。我為了查出「keg-only」到底是指什麼意思,結果解開一連串的謎題,真相終於大白! Homebrew 的所有命名真的非常有邏輯~~

首先, brew 本身是釀造、釀酒的意思,會用這個字的原因是 homebrew 的安裝方式為下載 source code 回來做編譯,由於是在自己電腦做 local complie 編譯套件,所以這個工具叫做 homebrew 自家釀酒。

釀酒需要有配方 formula ,當你需要安裝套件時,流程就是下 brew 命令去根據配方 formula ,釀造出一桶(keg)酒來。所以 keg 指的是整個編譯完成的套件資料夾。

再來,放置套件的位置在 /usr/local/Cellar , Cellar 就是地窖,一桶一桶釀好的酒當然要存放在地窖裡囉!所以編譯完成的套件資料夾 keg 預設目錄在 /usr/local/Cellar 。

最後回到「keg-only」這個詞,字面上意思現在就很清楚,表示這個套件只會存放在桶子裡,不會跑出桶子外;實際上的行為是 brew 不會幫你做 symlink 到 /usr/local ,避免你的原生系統內還有一套 readline 而打架,所以訊息提示說 readline 套件是 keg-only 。

至此謎題全部解開啦! Homebrew 的命名邏輯真是超有趣的~

 

相關連結:

Homebrew: 新一代 OSX 套件管理工具
Homebrew: OS X’s Missing Package Manager

Homebrew 專案頁面: http://mxcl.github.com/homebrew/
Homebrew 原始碼: https://github.com/mxcl/homebrew

文章標籤

笨笨小蟹 發表在 痞客邦 留言(7) 人氣()

這個功能其實從 Rails 2 就有了(也許更早,我不太確定),不過我最近才有機會研究了一番,看的也是 Rails 3 的文件。

要在 model 裡面使用 serialize 有一個限制,就是該欄位必須是 text 格式,所以在玩這個功能之前請先 migrate 一個 text format 的 column 出來。

寫法如下:

class User < ActiveRecord::Base
  serialize :preferences
end

serialize 第一個參數是欄位名稱,第二個是 class 。可以有變化的地方在 class 這個參數,如果不給預設就是使用 YAML ,可以給 Array, Hash 甚至是自定的 class ,看你存什麼格式就會照著寫入該欄位內。

比方說接續上面的範例:

user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }

 

如果 class 不對,比方說格式指定 Hash ,你卻塞給他一個 Array ,那麼就會跳出 SerializationTypeMismatch 錯誤。

class User < ActiveRecord::Base
  serialize :preferences, Hash
end

user = User.create(:preferences => %w( one two three ))
User.find(user.id).preferences    # raises SerializationTypeMismatch

特別注意到,如果你使用一個 class ,那麼它會自動變成你這個 model 的 instance method ,非常的酷!

class User < ActiveRecord::Base
  serialize :preferences, OpenStruct
end

user = User.new
user.preferences.theme_color = "red"

 

最後,由於欄位需要做初始化的動作,可以在 migrate 時就指定預設值 "--- {}" ,但是 MySQL 5.x 會對 text 格式的欄位忽略預設值,因此建議作法是用 after_initialize callback ,去呼叫同名 method ,雖然會對 performance 會造成些微的影響,但可以避免一些錯誤出現。

def preferences 
  read_attribute(:preferences) || write_attribute(:preferences, {}) 
end

 

參考資料:

http://api.rubyonrails.org/classes/ActiveRecord/Base.html
THE RAILS 3 WAY

文章標籤

笨笨小蟹 發表在 痞客邦 留言(1) 人氣()

有些時候需要撈資料,找出重複的記錄,但 Ruby Array 本身沒有這種功能的 method ,所以就自己打開 class Array 加進去吧!

class Array

  def find_dups
    uniq.map {|v| (self - [v]).size < (self.size - 1) ? v : nil}.compact
  end

end

參考資料:http://snippets.dzone.com/posts/show/4148

笨笨小蟹 發表在 痞客邦 留言(0) 人氣()

會使用到 Jammit gem 的原因是,我發現網站在 6 月改版後,就常常會發生網站脫衣服(吃不到 css)的狀況,而且都是在上班時間。

翻 log 查到原因後發現,原來是因為原本網站的伺服器只有一台,改版時調整成兩台並且做 HA ,問題就出在多台伺服器上。

Rails 的 layout 中, stylesheet_link_tag 我有設定 :cache => :merge_index ,因此看原始碼時會發現,網站只有讀取 merge_index.css ,這段設定就是請 Rails 幫忙把數隻 css 壓成一隻,減少 http request 的數量。

 

問題來了,這隻 merge_index 會在瀏覽器訪問頁面時檢查是否存在 /public/stylesheets 資料夾,如果不存在就先產生它,因此在 production 環境的目錄內你會找到 merge_index.css 這個 development 環境沒有、也不在 repository 內的檔案。

以上的狀況,當你只有一台伺服器時,一切都正常;當有多台伺服器時,就會發生瀏覽器問到還沒產生 merge_index.css  的伺服器,因而吃不到 css 造成脫衣服的問題。

這個問題同樣也出現在T客邦網站,因此 xdite 寫了一篇文章介紹 Jammit 來解決這個問題,解決就是 preheat CSS/JS。但實際套用後發現,並不能照著 xdite 的文章實作,那要怎麼寫呢?

 

首先要編輯 config/assets.yml ,這個 yml 檔案的最前面是關於 Jammit 的設定,詳細的設定清單比方說壓縮方式、gzip、路徑等等可以去看參考文件

注意,設定中記得要有加入這行,否則在 Jammit package 時,會告訴你無法產生 css (IE7 以下 MHTML issue)。

embed_assets: datauri

 

接著是設定 stylesheets 和 javascripts ,範例如下

stylesheets:
  commons:
    - public/stylesheets/reset.css
    - public/stylesheets/common-use.css
    - public/stylesheets/hf.css
    - public/stylesheets/right-column.css
  merge_diary:
    - public/stylesheets/diary.css
    - public/stylesheets/openid-comment.css

javascripts:
  commons:
    - public/javascripts/pop.js
    - public/javascripts/facebox.js
    - public/javascripts/jquery.timers-1.2.js
  merge_index:
    - public/javascripts/jquery.scrollTo-1.4.2-min.js
    - public/javascripts/jquery.easing.1.3.js
    - public/javascripts/jquery.serialScroll-1.2.2-min.js
    - public/javascripts/slideshow.js

以範例來說,到時候產生出來的就會有 commons.css, merge_diary.css, commons.js, merge_index.js ,每隻檔案要包含哪些 css/js 就是在 yml 內設定,相當直觀。

ps. 實際上,因為有設定 datauri ,所以檔案名稱還會後綴 -data-uri ,此處為了直觀故省略了後綴字。

 

接著,要去修改 layout 中使用 stylesheets 和 javascrpits 的方法,寫法是

<%= include_stylesheets :commons, :merge_diary %>
<%= include_javascripts :merge_index, :commons %>

 

最後,因為網站需要 preheat stylesheets 和 javascripts ,在 capistrano recrips 內需要加上

before("deploy:symlink") do
  run "cd #{release_path} && bundle exec jammit"
end

 

做完 preheat CSS/JS 之後,就不會在出現網站脫衣服的狀況囉!

笨笨小蟹 發表在 痞客邦 留言(0) 人氣()

update: 2011/08/31

Rails 3.1.0 釋出後,迅速的在 Guide 網站補上 Asset Pipeline 教學!

http://guides.rubyonrails.org/asset_pipeline.html

 

文章內教學的非常清楚,但我還沒空嘗試,因此僅留下 link 記錄。

http://blog.envylabs.com/2011/08/using-the-asset-pipeline-outside-of-rails-serving-and-running-coffeescript-2/

笨笨小蟹 發表在 痞客邦 留言(1) 人氣()

css3buttons_rails_helpers gem 是幫助你將網站套上精美的 github style button 。

https://github.com/thetron/css3buttons_rails_helpers

 

注意 3.0.x 只能使用 0.9.5 版, 3.1 之後就沒有問題可以使用最新版本,因此要記得在 3.0.x 時使用

gem 'css3buttons', '0.9.5'

裝好之後需要跑 generator ,所以請下

$rails g css3buttons

然後將 layouts 裡面的 stylesheet_link_tag 換成

<%= css3buttons_stylesheets %>

 

這個 helper 是將 CSS3 GitHub Buttons 這個網站提供的 css and html 將他包裝成 rails 的 helper 。

http://nicolasgallagher.com/lab/css3-github-buttons/

 

其實用法很簡單,如果沒有用 rails helper gem ,那就是自己將需要變成 button 的 html tag 加上 class。

加上 class="button" 就會變成按鈕,加上 class="button pill" 就會變成圓角按鈕,加上 class="button primary" 按鈕內的文字就會粗體顯示。

有 button 的 class 配合各種 icon 圖案,例如 class="button icon add" ,就可以完成精美的按鈕。

 

如果使用 rails helper gem ,那麼就可以很直覺的使用定義好的 helper 來套上漂亮的按鈕。

規則很簡單,就是將原本的 link_to method 改成 button_link_to ,然後把你要的效果(大小、重要、顏色、圖案)向前疊加,來看範例吧。

原本是

<%= link_to("Search", search_path) %>

改成 helper 寫法是

<%= big_search_button_link_to("Search", search_path) %>

大按鈕+搜尋圖案,看起來就非常的直覺!

 

如果需要變色也可以,例如原本是

<%= link_to("Delete", delete_path) %>

改成 helper 寫法是

<%= danger_remove_button_link_to("Delete", delete_path) %>

危險紅色+移除按鈕,非常完美!

 

如果你想用 Button Group 也沒問題,寫法是

<%= button_group do %>
  <%= button_link_to "Show", @post %>
  <%= button_link_to "Edit", edit_post_path(@post) %>
  <%= negative_trash_button_link_to "Delete", @post, :confirm => "Are you sure? %>
<% end %>

這樣就會出現連在一起的按鈕群組啦!

笨笨小蟹 發表在 痞客邦 留言(0) 人氣()

記錄幾個好用的 tmbundle ,可以幫助整理 coding style 。

首先是小技巧,要設定 TextMate 使用 tab 時的對應字元,就在視窗的最下方,有個顯示 Soft Tabs: 2 或是 Tab Size: 4

我的團隊是使用 space 2 做為 indent ,所以是設定 Soft Tabs: 2 。

 

其次是,常常會發現有些檔案拉下來排版亂掉,可能是來自美術,或是手誤沒排好等等,可以裝 code-beautifier.tmbundle

操作方法是按 alt+cmd+b 你就會看到亂掉的程式碼,被重新整理好 indent 啦!

 

最後,你常常會不小心在行尾多了一些空白,可以裝 whitespace-tmbundle

用法完全無痛,因為他取代了存檔熱鍵 cmd+s ,只要存檔就會自動清潔所有的 trail whitespace ,讚!

笨笨小蟹 發表在 痞客邦 留言(0) 人氣()

在 rails 的專案內設定 .gitignore ,忽略掉 config/database.yml ,但是卻一直出現在 changed status 上。

這問題以前遇過一次,但是忘了記錄下怎麼解決,這次來寫解法。

reference:
http://stackoverflow.com/questions/3296739/git-not-ignoring-certain-xcode-files-in-gitignore

這種狀況是,之前有不小心把該檔案 commit 進 repo ,所以無法忽略,要下

git rm --cached your_filename

然後 commit 去更新 repo ,這樣 gitignore 就會生效啦!

文章標籤

笨笨小蟹 發表在 痞客邦 留言(1) 人氣()

上班一年多了,最近一次的大型專案開發在 6/1 上線,來利用這篇文章反省加檢討我參與過的專案管理與開發時程問題。

先講很失敗的第一次,因為對當時的實力掌握太差,所以最後大受打擊無疾而終。

處理的專案是廣告系統的後台,需要改寫並新增功能,但我想完 model ,寫完 CRUD 之後就停滯了。

  • 原因1:當時的我,對於基本網站架構還不夠熟悉,在規劃 CMS 上也不夠清楚,因此上稿的部份寫完,與前台接軌就卡住了。
  • 原因2:寫完那些 CRUD 之後,因為我同時還要顧及美妝的專案,每個月固定都要寫 event ,雙管齊下當時的我無法吃下來。
  • 原因3:時間管理太差,我當時沒有把整個負責部分切開成一張張小票的能力,整個混在一起沒頭沒腦地做,難以掌控進度,也無法知道什麼時候要完成哪些區塊。

最後因為開始寫美妝的 event ,負責的部份就也撥給同事處理,那時候也沒有再拆票過來請我幫忙,從此這個專案就成了我心中的痛,處理的一團糟。

回頭檢視,我真是太傻太天真,做一塊還不夠熟悉的東西(web),也不曉得章法(製作流程),重點是也不知道怎麼開口請教別人給予一些基本指導。

 

再來講最近的一次美妝改版,情況比剛剛說的廣告後台專案好,但有很多值得檢討記錄的地方,這也是寫本篇的目的。

改版的方式走瀑布式開發,雖然我一直很想嘗試敏捷開發,但是我對網站的經驗還是太少,無法充分的決斷與規劃。

在初期,我先挑了一個最麻煩的新品快遞服務來處理,開始將 database 先 porting 成新版的結構,同時做出對應呈現的前台。這次我學乖了,開始試著把這個服務切成一張張小票處理,但是我沒有處理好時程,對於處理事項的權重也有問題。票開好了應該要先排序、找核心票避免解票時發生卡票、相依性太高的票要盡量切小/切開,這些問題我都踩到了,因此開發時雖然一直在做事,但卻像多頭馬車什麼都有做卻什麼都沒有完成,所以在時程上顯示一直 delay。

同時,雖然這段時間美妝的 event 改用其他方式製作,不會花到我的時間,但我決定趁這次機會,將陳舊的 Rails 2.3.4 直接改用 Rails 3.0 改寫,導致初期花了不少時間,將原本的程式搬過來後調整成新版的寫法。另外,由於有做 SSO ,原本的使用者認證 gem 就變得太過肥大,當時我決定將原本的 user authentication library 改寫一份給專案使用的精簡版本,這種種繁雜的問題,使得初期進度極為緩慢。

初期的問題在於,我對 Rails 3 還不熟,所以在估算改寫的時間時,必須要將解決 Rails 3 的寫法這塊也考慮進去。雖然我習慣性地的預留一些開發時間,但是留的太少了。再來,當已經拆出小票之後,優先權最高的應該是 dependency 最高的票,因為這種票不做完會 block 到其他票的進度;其次是要把只有自己特別熟悉的票先做完,因為這種票如果要請其他 RD 協助,是快不起來的。這幾點之中,我只有最後一項處理的比較好,前兩項準則沒抓到,所以做起票來東卡卡西卡卡,成效不彰。

 

中期的時候,雖然花了不少時間,但我有把初期新品快遞的服務功能寫完 90% ,因此當美術把版都切完送過來時,我開始處理第二塊 porting 流行日誌,這時也正式進入五週的技術開發時程。由於先前寫 Rails 3 把部分的地雷都排除過,因此技術上卡住的情形降低很多,不過我在這個階段做錯兩件事,迫使我時程大 delay 。

第一,新品快遞的服務功能完成 90% ,這個數字事實上有 pending 一個在後台接軌痞客邦相簿的功能,決定 pending 的原因很簡單,因為已完成的功能是可以正常上稿,也可以使用痞客邦相簿,只是會多幾道手續,但是如果要完成接軌的功能我得再花 2~3 天實作,以時程來說最高優先毫無疑問是上線,因此我應該先去完成其他的服務,最後有時間再來解這張功能票,但我陷在裡面好幾天,後來主管提醒之下才醒悟。

第二,優先權錯誤,在初期和中期開發上,我都把 database migration 放在最高優先,因此花了很大的力氣去做對應的 convert ,這個決定錯在我應該先生頁面,讓頁面功能正常,再來做 database migration 把舊資料轉移過來才對,畢竟上線會先看的是功能正常,其次才是看資料內容。

由於這兩個原因,讓我做了兩週之後,雖然大部分資料庫已轉好,但頁面依舊有許多是未套版或功能未完成。第一週結束時我還很天真的認為,進度雖然有延誤,但是週末加班趕工可以追上進度,第二週結束後才發現我錯了,檢視之下發現,原因是我切出的票具有太高的 dependency ,可是我花在做 database migration 的時間卻不少,因此頁面功能的進度 delay ,導致時程被越拉越遠...

在第一週發現時程延誤時,我有先告知主管希望把我不擅長的票拆出去,請其他 RD 幫忙,但只有一小部分;而到第二週結束時,我不得不再次告知主管,我很抱歉拖到時程只剩三週才講,可是必須要再請一位 RD 一起寫,否則真的會來不及。幸運地是,雖然我當初花了太多的力氣去做 database migration ,但這部份也是我較為熟悉的部份,因此一起合作 coding 的 RD 在跳進來後沒有被這塊 block 。

 

後期開發上,首要決斷是我完全放棄管理專案,改請主管接手,我不再開會而是專心寫程式,而且很明顯我的排票、時程估算和決策等等都還有待磨練。主管接手之後,他很迅速地根據時程決定 pending 一些有替代方案、以及影響較小的功能,讓所有目標都專注在完成整個網站並上線,同時 RD 的壓力也跟著降低;此外,主管根據我已經切出的票再延伸,讓團隊開發上能夠更加地容易。以往我開票的方法不夠有條理,也沒有切到很乾淨,在做票時也不曉得要設定狀態讓其他 RD 知道,這次讓我都學到了。另一位 RD 加入之後,由於 api 接軌和 javascript 的部分他很熟,功能做起來也相當迅速,而我就可以專心做我熟悉的部份,進度因此明顯提昇,最後得以在 6/1 順利上線。

總結來說,

  • 我對於時程估算非常有待加強,主要是得看出實作上會花時間的地方,有時候魔鬼藏在細節裡,沒做過真的不會知道小地方其實需要花很多時間實作。基本 CRUD 最少要半小時,如果有特殊功能繼續往上加; database migration 非常花時間,一定要儘早處理;遇到有加密的部份,因為涉及到 system library ,最少要撥三天處理(像是 SSO );自己不擅長的技術,要估算兩倍或三倍的時間;有替代方案的功能,可以往後壓晚點做。
  • 我的切票技巧有比以前好,但是切的還不夠乾淨,最終目標應該是切出的票要有正交性。
  • 做票的優先權, CRUD 很枯燥,但是要乖乖地做;所有的 link 都應該先做,不然會缺東少西;拆不開,具有高 dependency 的核心功能要先做,不然會 block 後面的票;只有自己能做的票要先做,以免影響到其他 RD 。
  • 排定時程時,從容易的功能開始,排在時程末端,接著依序將越難的功能排到越前面的時程。訂好的時程,要每天或是每兩天檢查,延誤一週就要趕快求救。

以上這些,是我親身的專案經驗檢討,雖然兩次專案都沒有處理好,但記錄下失敗的經驗,避免以後重蹈覆轍。這次趁著記憶猶新,將這兩個月的苦日子回想了一遍,盡量客觀地把自己的問題找出來。沒有人喜歡失敗,我也不例外。

笨笨小蟹 發表在 痞客邦 留言(0) 人氣()

本篇的作法是我參考了文末的 reference 後融合而成。

在 app/controllers/application_controller.rb ,首先在最前面加上

unless Rails.env == 'development'
  rescue_from Exception, :with => :render_500 
  rescue_from RuntimeError, :with => :render_500
  rescue_from ActiveRecord::RecordNotFound, :with => :render_404
end

然後定義 render_404 、 render_500 、 render_optional_error_file 這三個 method

def render_404
  render_optional_error_file(404)
end

def render_500
  render_optional_error_file(500)
end

def render_optional_error_file(status_code)
  status = status_code.to_s
  if ["404", "422", "500"].include?(status)
    render :template => "/errors/#{status}.html.erb", :status => status, :layout => "errors"
  else
    render :template => "/errors/unknown.html.erb", :status => status, :layout => "errors"
  end
end

打開 config/routes.rb ,寫上所有找不到頁面要處理 404 的 route

match "*other", :to => "welcome#handle404"

在 app/controllers/welcome_controller.rb 加上 handle404 action

def handle404
  render_404
end

最後把 /public/404.html, 422.html, 500.html 刪除,這樣就完成了!

 

Reference:
render_optional_error_file method
rails3定制404和500错误

Extra:
另外我後來還有找到 gem 可以處理 errors 頁面,但因為那時候 code 都寫完了所以沒有測,僅列出文章和連結提供參考
Helder Ribeiro — Dynamic Error Pages in Rails 3 with Goalie
wrangler gem
dynamic_errors gem

文章標籤

笨笨小蟹 發表在 痞客邦 留言(1) 人氣()