錦囊妙計-後端篇
When you choose a language, you’re choosing more than a set of technical trade-offs—you’re choosing a community. - Joshua Bloch
這兩章介紹一些常見的Rails疑難雜症問題,以及常用的RubyGem套件。更多熱門套件可以參考 The Ruby Toolbox、awesome-ruby 和 awesome-rails-gem 等介紹。
如何除錯 Debug?
就三個步驟:
- Reproduce: 有特定的步驟可以重現這個 bug
- Isolate: 找出是哪一段 code 造成這個 bug
- Fix: 修好他
詳細可以參考 The Art of Debugging 一文
Debug 方法
- 在 template 中使用
<%= debug your_variable %>
- 使用 Rails.logger
- 使用 web console 發生例外的時候會出現,或是自己放 console 到程式裡面
- 使用中斷點 byebug (使用指令 continue 讓中斷繼續)
byebug 詳細用法請參考 https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md
- 進一步找 Rails 或其他 gem 的原始碼了解
- 執行
bundle open the_gem_name
會用預設的編輯器打開,可在~/.bash_profile
裡面設定export EDITOR="subl"
- 執行
-
https://github.com/brentd/xray-rails 打開 template 和 partial 的好工具
- https://github.com/rails/spring 有時候重開還是沒用沒有載入到修改的設定,可以試試 bin/spring stop
其他參考文章
- https://speakerdeck.com/igaiga/rubyconftaiwan2015
- http://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html 一些不用 debugger 只用 puts 的除錯技巧
- http://rails.ruby.tw/debugging_rails_applications.html
- http://blog.motion-express.com/debugging-rails-5-skills/
- http://www.rubyonrails365.com/7-must-have-gems-to-install-on-any-project
- http://www.jackkinsella.ie/2014/06/06/a-comprehensive-guide-to-debugging-rails.html
如何查找 Gem 的原始碼?
除了 Gem 的文件,直接查找 Gem 的原始碼有時候也是除錯或看用法的最好方式:
- 執行
bundle open GEM_NAME
就會用預設的編輯器打開 gem 的原始碼了。 - 如果你想看 Rails 的原始碼,由於 Rails 拆分成不同的 gems,所以
bundle open rails
打開來不會看到所有的 Rails 原始碼。建議讀者可以直接git clone [email protected]:rails/rails.git
一份官方的原始法回去即可。
Rake
Rake 用來編寫任務腳本,讓我們在CLI中可以執行。它的好處在於提供良好的任務編寫結構,並且很方便設定各個任務的相依性,例如執行任務C前,需要先執行任務A、B。在 Rails 之中就內建了許多 rake 指令,除了你已經使用過的 rake db:migrate 之外,你可以輸入 rake -T 看到所有的 rake 指令。
而要在 Rails 環境中撰寫 Rake,請將附檔名為 .rake 的檔案放在 lib/tasks 目錄下即可,例如:
# /lib/tasks/dev.rake
namespace :dev do
desc "Rebuild system"
task :rebuild => ["db:drop", "db:setup", :fake]
task :fake => :environment do
puts "Create fake data for development"
u = User.new( :login => "root", :password => "password", :email => "[email protected]", :name => "管理員")
u.save!
end
end
透過執行 rake dev:rebuild,就會砍掉重建資料庫,最後執行 rake dev:setup 建立一些假資料作為開發之用。
推薦 https://github.com/stympy/faker 這個 gem 可以產生各種假資料
其他常見的使用情境包括:1. 修正上線的資料,這樣部署到Production後,可以用來執行 2. 建立開發用的假資料 3. 搭配排成工具使用,例如每天凌晨三點寄出通知信、每週一產生報表等等
更多介紹可以參考 http://jasonseifer.com/2010/04/06/rake-tutorial 這篇文章。
分頁
- kaminari 目前比較流行用這個
- will_paginate 比較老的專案會用這個
檔案上傳
- Paperclip 是目前使用上最為方便的檔案上傳 plugin。
- CarrierWave 寫起來比較繁瑣但設計比較嚴謹
範例 source code
- https://github.com/ihower/rails-exercise-ac4/commit/3fca6237babd4f462924d9c12696a8f17f0ef32c
- https://github.com/ihower/rails-exercise-ac6/commit/7de1031f64807de69bf83f32e46ae0ddab4f9414
paperclip 如何上傳任意格式的檔案?
假設欄位叫做 attachment 的話,改成以下宣告:
has_attached_file :attachment
do_not_validate_attachment_file_type :attachment
設定檔處理
在整合第三方應用時,第三方的 API Key 和 token 等等,我們不希望 hard-code 在程式碼裡面,一來是因為我們可能不想把這些敏感的 keys commit 進去版本控制系統。二來是因為將來佈署的時候,在 staging 和 production 不同環境下,這些 key 都會不一樣,我們希望容易抽換。
因此,有幾種作法和考量:
使用 YAML 格式作為設定檔放到 config 目錄下
fb_config = YAML.load(File.read("#{Rails.root}/config/facebook.yml"))[Rails.env]
或 fb_config = Rails.application.config_for(:facebook)
要注意
- YAML 會區分數字和字串,例如
01234
會看成1234
,如果要確保被看成字串,請加上引號,例如"01234"
- 讀出來的 Hash 是用字串 key,不是 symbol key。例如是
fb_config["app_id"]
而不是fb_config[:app_id]
- 如果要通吃,可以用
symbolize_keys
這個方法,例如fb_config = fb_config = Rails.application.config_for(:facebook).symbolize_keys
。這樣用字串或 symbol 都可以。
如果不要 commit,請加進 .gitignore
因為設定檔的東西不同開發者可能不一樣、而且可能包括敏感資料,因此如果不要 commit,就列在 .gitignore 裡面。
並且依照開發者慣例,我們會產生一個 xxx.yml.example 檔案當作設定範例給同事參考。例如: 放 AWS key 的 s3.yml.example
、放 email 設定的 email.yml.example
一些 commit 也無妨的情況
但是如果該設定檔的內容機密層級低、專案是放在 private repository 的話(各位的練習作業和期末專案是放在 public repository),而且每個開發者的設定檔都長得一樣,那麼 commit 出去也無妨,大家開發會比較方便。例如config/database.yml
地的資料庫帳號密碼,大家可以講好都設成一樣帳號root
、密碼留空即可。
除了寫在 YAML 檔案中,這些 token 也可以寫進config/secret.yml
,例如以下的設定,用Rails.application.secrets.batchbook_api_key
就可以拿到值了。
development:
secret_key_base: XXXXXX
batchbook_api_key: YYYYYYYY
環境變數作法
也有一些人不喜歡用 yaml 載入,而是用環境變數。請參考 The Rubyist’s Guide to Environment Variables 這篇文章的作法。
通常是對 Linux 環境比較熟悉的同學,比較喜歡用這種作法。Heroku 也有專文 The twelve-factor app 推崇這種作法。
ActiveReord: 加強搜尋
- Ransack可以很快的針對ActiveRecord做出排序和複雜的條件搜尋。不過 ransack 並不太考慮效能問題,特別是文字模糊搜尋,這部份網站長大後會改用全文搜尋引擎,例如 ElasticSearch
ActiveReord: 列表結構(自訂排列順序)
搭配 jQuery UI Sortable 就可以做出拖拉排序,可以參考 Sortable Lists這篇文章。
Model 設計:
UI 設計可用 jQuery UI Sortable:
- https://github.com/joliss/jquery-ui-rails
- http://api.jqueryui.com/sortable/#event-update
- http://railscasts.com/episodes/147-sortable-lists-revised
Example Code: https://github.com/ihower/rails-exercise-ac5/blob/master/app/views/events/index.html.erb
ActiveReord: Self-referential Relationships
1-to-many tree 樹狀結構 (parent and children)
- 樹狀結構 比較 awesome_nested_set、ancestry 和 closure_tree
- 應用範例: http://railscasts.com/episodes/162-tree-based-navigation?view=asciicast
many-to-many
- http://railscasts.com/episodes/163-self-referential-association
- Friendship 朋友關係範例: https://github.com/ihower/social-exercise-ac3/blob/master/app/models/user.rb
ActiveReord: Tagging 標籤
可以自己寫,也是多對多模型,搭配虛擬屬性(Virtual Attribute)的設計來達到使用者可以輸入任意的 tag name Tag model: name, taggings_count Tagging: tag_id, {target_model}_id
- Tagging 教學文 http://www.sitepoint.com/tagging-scratch-rails/
- acts-as-taggable-on
- http://easyactiverecord.com/blog/2014/10/21/tagging-your-content-using-the-acts-as-taggable-on-gem/
ActiveReord: Soft Deletion 和版本控制,編輯和刪除後還可以留下紀錄和還原,
- paper_trail 另開一個 versions table 完整紀錄
- paranoia 加一個欄位標記被刪除。不建議用這種方式,因為會讓資料庫的 unique key 檢查出問題。
- audited
ActiveReord: 有限狀態機
適合用來設計比較複雜的 model 流程狀態
ActiveReord: 資料表註解
會幫你在model code上面註解加上所有資料表的欄位
根據ActiveRecord的關聯自動產生漂亮的Entity-Relationship Diagrams
API 串接:處理 HTTP
Ruby 內建的 Net::HTTP 設計的不是很好用,因此大多數人改用其他 API 比較好用的函式庫,例如:
範例 Source Code
安裝 gem 'rest-client'
- 抓 Ubike 資料 https://github.com/ihower/rails-exercise-ac8/commit/2eed28e94fc60aa7b7cc11cce531321e86254b0d#diff-d36dd0926c5b89774b950af54144b8b2R3
- 抓 Facebook 個人資料(需透過 token) https://github.com/ihower/rails-exercise-ac8/commit/1c1746c04b3eb08864de1512e7f7abf542aae381
一些 API 資料來源
- Github API: https://api.github.com/users/ihower
- 政府開放資料 http://data.gov.tw/
- g0v 開放資料 http://data.g0v.tw/
- SheetHub http://sheethub.com
- 資策會 Social Event Radar http://api.ser.ideas.iii.org.tw/docs/#!/fb_fanpage_search
- 台北市政府開放資料 http://data.taipei/
- YouBike臺北市公共自行車即時資訊: API 端點 URL 和 欄位說明文件
- 臺北市政府 食材登錄平台 https://taipeicity.github.io/foodtracer/
- 臺北市政府 交通即時資料 開放資料專區 https://taipeicity.github.io/traffic_realtime/
CSV
Ruby就有內建這個函式庫了,只需要require "csv"
即可使用。
YAML
Rails 的資料庫設定檔 database.yml 是用一種叫 : YAML Ain’t Markup Language 的格式所撰寫,檔案打開來,看起來就像一般的 plain 設定檔,非常容易修改。
YAML 的設計首要目標就是要讓使用者容易看懂,可以和 script 語言搭配良好。用途有 資料序列化 data serialization、設定檔 configuration settings、log files、Internet messaging、filtering 等。網站上已知有支援的 script 語言有 Python,Ruby,Java,PHP,Perl,Javascript 等。
require ‘yaml’
ps2 = YAML.load_file(‘example.yaml’)
ps2.each do |it|
puts it.inspect
end
JSON
Rails 內建就有 ActiveSupport JSON,用法如下:
ActiveSupport::JSON.encode( [ {:a => 1 , :b => 2 } , "c", "d" ] )
=> "[{\"a\":1,\"b\":2},\"c\",\"d\"]"
ActiveSupport::JSON.decode( "[{\"a\":1,\"b\":2},\"c\",\"d\"]" )
=> [{"a"=>1, "b"=>2}, "c", "d"]
yajl-ruby 則是一套底層用C,比較快很多的 JSON parser,建議可以讓Rails底層改用這套函式庫,請在Gemfile
檔案中加入
gem 'yajl-ruby', :require => 'yajl'
XML
Rails 內建使用 Ruby 的 XML 函式庫 Builder
Nokogiri 是一套基於 libxml2 的函式庫,效能較佳。可參考 Getting Started with Nokogiri 一文介紹用法。
如果要替換 Rails 內建的 XML 函式庫,請在Gemfile
檔案中加入
gem 'nokogiri'
有些函式庫為了執行效率,底層會改用 C 的函式庫,適合於正式上線環境,缺點是需要編譯,在一些特殊環境可能無法運作,例如最新版的 Nokogiri 就不支援 Windows 了。而純 Ruby 實作的版本就沒有這個問題。
排程工具
如果您有週期性的任務需要執行,除了可以透過Linux的crontab設定去執行rake腳本。例如輸入crontab -e加入:
0 2 * * * cd /home/your_project/current/ && RAILS_ENV=production /usr/local/bin/rake cron:daily
就是每天凌晨兩點執行rake cron:daily這個任務。
或是你可以安裝whenever這個 gem,就可以用Ruby的語法來定義週期性的任務,可以很方便的設定伺服器上的cron排程。
自動備份
可以搭配 whenever 就可以定期備份了
升級Rails
小版號的升級,通常透過以下步驟即可完成:
- 修改
Gemfile
的Rails版本:gem 'rails', '6.0.0'
- 執行
bundle update
- 執行
rake rails:update
會嘗試更新Rails自己產生的檔案,例如config/boot.rb,請一一手動檢查。
升級前,也請參閱官方公告的升級注意事項。
其他
- http://asciicasts.com/episodes/255-undo-with-papertrail
- http://intridea.com/2011/5/13/rails3-gems
- http://erik.debill.org/2011/12/04/rake-for-rails-developers