Official Blog including Rails / Web Development / Startup topics

We will speak at RubyChinaConf 2013

Hi, we will attend RubyChinaConf 2013 this year. ( 5 of our developers will fly to Beijing)

Also, our dev lead "xdite" will also give a speech about how to avoid write maintainable rails view

Talk proposal:

Maintainable Rails View

寫出容易維護的純 Ruby / Rails code 門檻並不高。但開發網站過程中,最令人頭疼的是,當程式碼牽扯到 UI 層面,許多事情就亂了套。這個 talk 會分享許多實務上如何設計維護性高的 View 的最佳實踐。Helper, Partial, Cells, Decorate Object, View Object, Form , Policy Object, 这个主题将向你一一讲解。

Using Github Organization & Team For Application Authentication

Yesteday, I found out an interesting gem warden-github-rails reported by Ruby5. It allows developers using Github organization and team to manage routing permissions.

It is very useful. So I immediately implemented in our internaal applications. Here is Howto

Installation

https://github.com/fphilipe/warden-github-rails/

put in Gemfile

gem "warden-github-rails"

Register a application on Github

fill setting to config/config.yml ( using SettingsLogic )

  github_client_id: ""
  github_client_secret: ""
  github_organization: "rocodev"
  github_team: 
     name: "your_team_name"
     id: "team_interger_id" # github using interger id as team 

Register an OAuth Application

get client_id / client_secret from Github Applicatio Registration

Config warden-github-rails

create a new file config/initializers/warden_github_rails.rb

Warden::GitHub::Rails.setup do |config|

  config.add_scope :admin, :client_id     => Setting.github_client_id,
                           :client_secret => Setting.github_client_secret ,                          
                           :scope         => 'user'

  config.default_scope = :admin

  config.add_team Setting.github_team.name, Setting.github_team.id
end

Set Rails routing

config/routes.rb

  github_authenticate(:org => Setting.github_organization, :team => Setting.github_team.name ) do
    namespace :admin do 
      root :to => "base#index"
      match '/logout' , :to => "base#logout", :as => :logout
    end
  end

Admin::BaseController

app/controllers/admin/base_controller.rb

class Admin::BaseController < ApplicationController
  def index
    @is_admin = github_authenticated?(:admin)

    sign_user_from_github(github_user)
  end

  def logout
     github_logout
     sign_out current_user

     redirect_to root_path
  end


  protected


  def sign_user_from_github(github_user)
    user = User.find_or_create_from_github(github_user)
   
    sign_in user

   end
end

add find_or_create_from_github in User model

class User < ActiveRecord::Base
  def self.find_or_create_from_github(github_user)

    user = User.where(:email => github_user.email).first
    
    if !user
      user = User.new
      user.email = github_user.email
      user.name = github_user.name
  
      user.password = Devise.friendly_token[0,20]
      user.save!
    end

    return user
  end
end

Enjoy!

You can find more API in

Rails 3 中生 MySQL subquery 的秘技

Rails 3 中生 MySQL 生 subquery 的方式

有時候設計 Rails Application 時,某些 JOIN 的語法並不是那麼好寫。(不管是手刻,或者是透過 ORM 下出來。)

所以在 Application 層( PHP or Rails) 下兩次 SELECT 或者是用 subquery 往往會變成直覺下的取代方案。

舉個例子,有個功能叫做:我上架的書有被人 facvorite 的總數量。

一般開發者在不用 JOIN 的情況下,會寫出這樣的 code

  book_ids = Book.where(:user_id => user.id)
  favorite_count = Favorite.where(:book_id => book_ids).select("book_id").map(&:book_id).uniq.size

因為你無法知道 book_ids 怎麼拉出來丟 query,只好拉到 Application 層再塞回去用 IN 撈。數量小這樣的作法是還 okay….但…萬一 book_ids 多達萬,顯然這招就效了。除了慢之外,還製造一堆無用物件浪費記憶體空間…

而 Rails 3 背後強大的 Arel,其實提供了自動算出 subquery 的語法:

其實上面那一段可以改為這樣寫

  favorite_count = Favorite.where(:book_id => Book.where(:user_id => user.id)).select("distinct book_id").count

是的,在查詢語句裡面再下條件就會自動變成 subquery 了....

Favorite.where(:book_id => Book.where(:user_id => user.id))

http://stackoverflow.com/questions/5483407/subqueries-in-activerecord

幾個原則

  • 不是 JOIN 就是慢
  • 兩個 SELECT 不一定比 JOIN 快。(在以上 book_id 數量很大的 case 中)
  • 兩個 SELECT 有時候會比 JOIN 快。(在以上 book_id 數量很小的 case 中)
  • 有時候應該用 SUBQUERY 取代兩個 SELECT
  • SUBQUERY 不是萬靈丹,因為大多時候 JOIN 蠻多時候比 SUBQUERY 有效率,但 SUBQUERY 好寫蠻多的,可偷吃步…
  • SUBQUERY 適可而止,太多層 SUBQUERY 效能掉很兇(兩層其實就慢了...)。

Rails 4: New Feature, Better Syntax

TL;DR : Rails 4 是一個溫和加強版的 Rails3,而且贈送了很多酷炫 feature,適合進場

上個月花了一點時間直接衝了 Rails 4.0beta1,利用 upgrade project 去熟悉整個 Rails4 新的架構。

若要我形容對於 Rails4 這次升級的感想的話,我會總結為兩句話 New Feature, Better Syntax

相較於 Rails 2 -> Rails 3 幾乎是個毀天滅地的重新大改寫(i.e. 不管是 Rails 本身,還是使用 Rails 開發的 project),Rails 3 -> Rails 4 的升級及變更內容顯得溫和許多。

Better Syntax

Rails 4 這次的改進,許多都是吵了多年以來的折衷方案、或者是許多開發者對如何設計,始終各自有 strong opinion 的主題,都找到 best practices 而被一槌定音了。或者是一些一直以來大家覺得老是被逼著這樣寫,非常智障的設計,也都被改掉了…

Routing

  • 讓 Routing 更安全:新增了以 http verb 為 syntax 的寫法,如 get/post
  • 支援 Rouring concern: 如果要幫不同的 resources 加上如同 :comments 這樣的 nested_resources 就不用一直再重複貼上

ActiveRecord

  • 強迫 scope 的寫法要全面改成 proc / lambda:避免 eager-evaluated 出現的問題。
  • Relation#not:以往要寫出 not 的查詢條件,寫法讓人哭笑不得。
  • Relation#none:以往撈不出集合,是 nil,要回傳 [] 空集合要自己作..
  • Relation#pluck:可以輕鬆只摘出某些欄位,以及要自己手下 select…
  • Relation#unscope:避免 default_scope + order 產生的排序問題。(因為下 except 無法閃過去)
  • update & update_columns:update 會觸發 callbacks, update_column 不會,但是 update_column 無法送多個 params,於是必須只好用 sneaky-save 這個 solution 繞過。現在 4 直接支援 update_columns

ActionController

  • before_filter 更名為 before_action:就是正名...
  • respond_to do |format| 拿掉 xml 以 json 取代:2013 年了,沒人再拿 xml 當 default API...

Security

  • 拿掉 attr_accessible,改用 strong_paraments:去年 Github 被打下的事情鬧很大,Rails 的安全策略重新被檢討,於是最後社群討論出採用 strong_parameters 得這個 best practices。

Other

  • 砍掉 public/index.html : 砍掉愚蠢的 publc/index.html,以往教 Rails 初學者第一課就是記得砍掉這個預設檔案,不然寫的東西都會看不到
  • add_flash_type :以往警告訊息只有 [:notice , :alert, :error ] 三種類型,但是自從有 bootstrap 之後。大家習慣使用的是 [:notice , :warning, :error]。要套版時要一直手加 , :flash => { :warning => "Oh no!" } 是很智障的事,Rails4 開放自定義 flash types。(P.S. 這是我提的...)
  • mem_cache_store 換成 dalli:自從 1.9 出了之後,原先的 :mem_cache_store (memcache-client)會撞到 utf8 問題,於是大家都改用 dalli 作為 backend cache,Rails 4 的 :mem_cache_store 預設將改為 dalli。

New Feature

  • Model Concern / Controller Cern: 重複用到的 method使用 Concern 複用
  • Turboklinks : 無痛自動 pjax。pjax 不難,只要你用 Rails4 …
  • Cache Digest: 採用 Russian Doll Cache Strategy,智能 cache 設計,以前多層 partial cache 的問題讓大家實在很頭大。
  • HTML5 input form helpers: 現在是 HTML5 的時代,開發時自然會使用很多 js plugin,如calendar plugin,但用傳統的 form object 去產生這些 field 實在很痛苦。Rails4 內建了 HTML5 input form helpers。
  • 採用 jbuilder 產生 json : 生 json 可以採用類似生 rss 的方式寫 builder,真是驚訝這個 feature 現在才出現 ….

結論

學 Rails4 最快的方式不是看書,因為總體而言,這次的升級並不是什麼大破壞。改良的 syntax 和好用的新 feature。反而應該會減少不少平常開發上的負擔。而學習 Rails4 最好的方式就是用勇敢在現有的 project 上,大方的開一個 rails4 branch 下去練習升。

不用花很多時間你就能感受到 Rails4 新 feature 帶來給你的好處…

// 警告:以上建議只針對 Senior Rails Developer。目前還有一些 gem 沒有 Rails4 版本。所以 project 升級有時候會遇到必須要自己 fork gem「手動升級/Hack」的狀況,不熟包 gem 者勿輕易嘗試。

這次內建的一些 feature,其實都還蠻有意思的,也許將來還會挑幾篇特別再寫幾篇文章...

資源

SCSS 開發原則:禁用 @import 'compass';

TL;DR :禁止使用 @import "compass";,最少最少都要從第二層如 @import "compass/css3"; 呼叫起。

===

上禮拜幫一個專案上了從 Wrapbootstrap 上買來的 Core Admin CSS 當後台 Admin 之後。開發的同事偷偷問我,是否有什麼設定可以讓開發時不重新 compile CSS,因為現在第一次進後台,compile CSS 都要超過五秒。但我們自己寫的前台 CSS 倒沒有這個問題....

五秒是個很驚人的數字,根據以往的經驗,我猜測可能又是 CSS 架構設計不當的問題,所以編譯才要花這麼久時間。

果不其然,鑽進去看了一下整體架構之後,我只送了一個 commit,改了九行。

before : Compiled core-admin/application.css (44080ms) (pid 74648)
after : Compiled core-admin/application.css (116ms) (pid 74648)

compile 時間就從 44 秒降到 0.1 秒.......XDDDD

Okay 這其中的原因是:

core-admin/application.css 的架構是這樣的,大概掛了 20 支以上的 CSS。

*= require core-admin/uniform_default
*= require core-admin/base
*= require core-admin/box
*= require core-admin/message_box
*= require core-admin/dropdowns
*= require core-admin/font-awesome
*= require core-admin/responsive-tables
*= require core-admin/fullcalendar
*= require core-admin/gritter
...

其中絕大部分的 CSS 又有這一行 @import "mixin_helpers";,而 mixin_helpers.css.scss 裡面的第一行就是 @import "compass";

Compass 與 AssetPipeline 編譯原則

Compass 的運作原理就是用層狀結構掛下底下數十支 mixin。假設 compass 底下超過 30 道 import 好了。乘以呼叫 compass 的 20 次,那會是相當驚人的數字。而 AssetPipeline 的問題是只要遇到 @import 速度就會慢下來。

所以很慢是理所當然的。把 @import "compass"; 換成直接呼叫 compass 下負責的 mixin,如 @import "compass/css3/box-shadow"; 就沒有這些問題了。

禁止直接使用 @import "compass";

這也是我們前台 CSS 並不慢的原因,因為公司的前端開發內規有一條就是禁止使用 @import "compass";,最少最少都要從第二層如 @import "compass/css3"; 呼叫起。這樣可以避免之後很多維護上的問題…

SMACSS 愛好者特別注意

特別如果你是 SMACSS:Scalable and Modular Architecture for CSS 的愛好者的話,更不可以不注意這一點…

Tuning 原則

另外附上我 tune CSS compile 速度的原則,提供各位參考:

  • 了解 asset pipeline 的 compile 原理
  • compile 速度永遠是卡在 @import (這也是 compass 與 sprocket 作者意見不合的地方 )
  • compass 的原理建立在層狀 import,所以也就是掛到越底層的css,在 compile 時速度會越快
  • 如果懶得每次都掛一堆 mixin,希望寫 helper 進去給所有 css 一次掛,記得檢查你掛了什麼....
  • 如果需要掛 mixin helper 的地方太多,那麼就應該改用 import 而非 require,這樣就不會 compile mixin helper 太多次...

Upgrade 到 Rails4 的一些感想

Rails4 在前天的 RailsConf 2013 釋出 Rails 4.0 RC1 了,這也表示大家應該可以進場了。

上個月在 Rails 4.0 beta1 時為了練手感,把手上的一個中小 production 專案,也上了 rails4 branch。

大概有幾個感想:

  • Upgrading to Rails4 這本書強烈建議要買,才 $15 USD,可以節省你不少 debug 時間。

  • 升 Rails4 建議不只開 branch,也用 rvm 開一個 gemset 出來作,因為 gem dependency 變更蠻多的。

  • rails4_upgrade 要裝。這個 gem 蠻好用的..可以幫你掃 dependency 問題。事實上 Rails3 升 Rails4 最討厭的是 gem dependency tree,因為 Rails 3 已經出太久了(幾乎快兩年了吧),很多 Gemfile 都強綁定 3 ,所以升 Gemfile 時會出現很多問題...

  • major gem,如 simple_form, devise, 幾乎都有 beta1 版,裝了就保證可以動。小的 gem 也幾乎都有 rails4 branch 可以 hotfix。(起碼我在 beta1 進場時遇到的問題就幾乎都有解,所以在 rc1 的狀況應該會更好)

  • 這次 Rails4 的改動,我個人的感想會是 Rails3 的 New Feature, Better syntax Version。如果平常 code 都寫的蠻漂亮(接口和封裝乾淨)的話,升級應該是沒有太痛才對。唯一讓人很煩的就是 gem dependency 解不完,還有牽扯到 scope 與 query 的部份幾乎都要重寫..:/ (目前是都還跳 warning 而已,但真要清 warning,如果 model 裡面 condition 很多,真的會清到手快斷...)

  • 有關於 New Feature 與 Better syntax 這個議題,我應該週末會寫一篇出來..

  • Rails project 的本體內容物是沒有改動太大,但大家拿來 build gem 的 internal API 改不少,這也難怪 Jose Valim 這一兩天也同步釋出了 Crafting Rails Applications (2nd edition): Expert Practices for Everyday Rails Development 第二版的 beta。我這幾天改 gem 要升 Rails4 也是中了一堆 api 變更的地雷…

  • 會逼大家都改 gem 的原因是因為是,連 migration api 都改了,所以只要提供產生 migration 的 gem 通通會逼要升 Rails4,真是個好招 -_- (連我只有兩個 commit 的 AutoFacebook gem 也不能倖免。解法在這裡

  • Obie Fernandez 前天也宣布了 Rails 聖經 The Rails 4 Way開始 beta。值得注意的是他這次是使用 Leanpub 釋出書籍的 beta,而非走 Informit 的 RoughCut 版本。

  • 為什麼我有時間測這些東西?好問題,我也不知道…明明最近就忙到快死了...orz

Bootstrap Helper 與 Bootstrappers 開始支援 Rails 4

我寫的兩隻 gem bootstrap-helperboostrappers 目前都釋出 Rails4 版本了。

  • gem install bootstrap_helper -v 4.2.2.1
  • gem install bootstrappers -v 4.0.rc1

有任何問題,請回報到 Github 上的 issues 上。

Boostrappers 是針對我在 2013/03 月底測試 Rails 4.0.beta1 測出來的 solution 更換掉 gemset 的。目前應該是沒什麼大問題...

不過這次值得注意的是,Rails4 底層又換了不少 API,包括 generator 的 action 和 migration,所以為了 bnootstrappers 的升級,我被迫 release 了三隻 gem。

包括我之前寫的 AutoFacebook,也被迫出了一個 Rails4 版本。

  • gem install auto-facebook -v 0.1.rails4

Sublime Text 2 Packages for Rails

最近玩到一些在開發實務和日常工作上還滿實用的 Package,記錄下來也分享給可能有同樣需要的朋友。

PlainTasks


相當易用的 TODO List,配合適當的快捷鍵及設定,可以在 ST2 裡快速開啟並編輯.

簡單說明一下,他其實是開啟指定的文字檔,然後利用插件的快捷鍵和縮排來達到 New / Done / Cancel / Archive 的功能。

首先我在家目錄下建了一個叫 TODO 的空檔案,然後在 ST2 的 Key Bindings - User 裡增加這行

{ "keys": ["super+."], "command": "open_file", "args": {"file": "/Users/Unayung/TODO"} }

  1. ⌘ + . 可以呼叫 TODO List
  2. ":"結尾的字串會被視為標題
  3. ⌘ + Enter 增加TODO項目
  4. ⌘ + d 把項目設定為已完成 (打綠勾勾)
  5. ⌘ + m 把項目設定為已取消 (打紅叉叉)
  6. ⌘ + Shift + a 把已完成及已取消的所有項目都移到 Archive

如此一來就可以很方便的在日常寫 code 的環境下記錄各種 TODO 事項

ERB-Sublime-Snippets

有在寫 ERB 的話這個東西就很方便了,例如輸入 er 再按 tab 就會自動補成 <% %>,游標還幫你放在中間,多貼心
常用轉換對照

Rails Latest Migration

這東西是省去翻找最新一個 migration 檔案麻煩的 package,安裝完成之後只要叫出 Command Palette (⌘ + Shift + p) 然後輸入 last 他就會幫你開好最後一份 migration 檔。

Rails Related Files


這也是同樣幫助你在檔案之間移動的 package,他的作用是例如你在對 book.rb 動作,點滑鼠右鍵或是 ⌘ + Shift + o 會列出和目前這個檔有關連的檔案 ex. books_controller.rb, book相關的 views 等等

Simple Rails Navigator


MVC開發常常會在 model / view / controller 切來切去。有了這個 package 就可以很優雅(?)的在各種檔案間切換自如

  1. ⌘ + Ctrl + m 列出所有 model folder 裡的檔案
  2. ⌘ + Ctrl + v 列出所有 view folder 裡的檔案
  3. ⌘ + Ctrl + c 列出所有 controller folder 裡的檔案
  4. ⌘ + Ctrl + j 列出 assets/javascripts
  5. ⌘ + Ctrl + s 列出 assets/stylesheets

記得要加入以下的 Key Bindings 才會動

{ "keys": ["super+ctrl+m"], "command": "list_rails_models" },
{ "keys": ["super+ctrl+c"], "command": "list_rails_controllers" },
{ "keys": ["super+ctrl+v"], "command": "list_rails_views" },
{ "keys": ["super+ctrl+j"], "command": "list_rails_javascripts" },
{ "keys": ["super+ctrl+s"], "command": "list_rails_stylesheets" }

Emmet

強烈建議這個 Package 一定要裝,太好用了 !!

不管是開發什麼類型的 Website / Web Application 你都可能會需要 tr 裡有十個 td

這時你只要輸入 tr>td*10 按一下 tab

Done !! 還有更多 tricky 的使用方式請參考官網的介紹。

把公司 Log 搬到 Hipchat...

一直以來( 5-6 年前開始..),我都是用 IRC 在管團隊的 Log 和通知。

這個習慣最早以來是跟前輩學習來的。這在比較強悍的技術團隊內部,幾乎是行之有年的標準 Convention。

( 可見 Flickr 著名的 10+ Deploys Per Day: Dev and Ops Cooperation at Flickr
投影片 (P.52),不過他們大概 2006 年就開始這樣做了,這篇只是後來比較漂亮的整理...)。

當年 在 T 客邦,也是用 redmine + IRC bot 自己搞了一套。

把 Log 都打到 IRC 有很多好處。團隊成員去開會、或者暫時離開。回到電腦前,還是可以很快速的掌握剛剛發生了什麼事。再加上 issue tracking 或者是 system alert 其實是很洗信箱讓人容易分神的東西,所以我們把這些幾乎都搬到 IRC 上,建立出一個可以非同步但又高效率的合作開發模式。

不過這個模式還是有一些極限,所以最近在 survey 過後,最近我決定把 公司 整套 solution 搬到 Hipchat 上。

主要搬家原因

  1. 發現每個同事一進來都要教怎麼用 irssi + 工作站掛 irc,學習成本很高
  2. 公司聊天室是 skype, log 在 irc 上,開兩窗有點麻煩。加上 skype-bot 不是不能作,只是我覺得 skype-bot 很吵…
  3. 人員離職很麻煩,因為要把 irc room 的 key 和 info 整套換掉,無法作權限控管
  4. demo 給別人看 irc solution 時也很麻煩,因為對方一定看得到我們的 key ....
  5. 對 irc 訊息上色要試很久,對一般的 developer 門檻有點高
  6. irc log 多半要切到桌機才能看,沒有 mobile solution。

所以最後就整套就搬到 Hipchat 了。看起來大家現在是用的蠻習慣的。

Hipchat 的好處

  1. 主要是 Web Based,但有 iOS, Android, Mac, Windows, Linux client
  2. 有 group 和 permission control,踢人加人很方便
  3. API 整合,寫 bot 很容易
  4. 聊天行為與一般 IRC chat 蠻相近的
  5. 貼圖貼檔案貼 Link 很方便
  6. 一樣會存歷史紀錄,就算離線了,重新上線還是可以找 Log
  7. 很多主流系統整合支援( github, redmine, capistrano, airbrake….)
  8. 5 人以下現在是免費的方案…

Hipchat integration

我們目前是把目前的幾種 Log 都打到 hipchat 上

  1. Github (github 的 hook 支援 hipchat, pull request, push , merge 都會通知...)
  2. Capistrano Deploy http://blog.hipchat.com/tag/capistrano/
  3. Airbrake ( server error 通知系統, airbrake 支援 hipchat )
  4. Redmine (官方的 hipchat/redmine_hipchat 不好用,所以我自己改了一隻 rocodev/redmine_hipchat 出來)

之後還會掛更多東西上去…