講者:小蟹
女人迷網站在年前經歷了 Rails 三個版號的大躍進,本次將由工程師小蟹跟大家做升級的經驗分享,包含:
- 如何查找升級問題
- gem 不相容時的 Monkey patch
- 拔除 Asset pipeline
- 開發期間,維持網站的功能開發
- deadline 與開發時程控管
主要是當初在學 rails 的時候也有受到小蟹幫忙,還記得第一次知道 binding.pry
是小蟹教我的,看到這個主題就馬上報名來聽了。
以下是當天的筆記分享。
提升自我
不斷提升自己,學習及瞭解 Ruby / Rails / Web 新技術或是趨勢,然後透過分享可以學到很多
但這些聽過的東西要從知道變成知識,一定要自己做過,如果沒做過,那還是等於不會
如果要嘗試去做,一定要做好萬全準備,沒有一家公司專案會期許你把「穩定」變成「不穩定」
因為嘗試新技術或是新版本,一定會對原本的 code 降低瞭解度,所以必須先想好備案,這是不是可以 revertable 的?
女人迷的 Practices
gem 雖然方便,但盡可能的少使用 gem
- 如果要用到 gem 的一小部分,不妨看是哪一部份,可以自己寫或是搬出來
- 如果客製化需求比較高,可以寫 library
如果整個套件只用到一部分 那麼可以自己寫 或是搬出來一部分(後續 Q&A 有問到,缺點可能是不好維護,例如有些是你從 gem 搬出來的結果遇到漏洞問題升級時如何解決?)
開始有經驗之後要能去找問題根本的原因,要會爬 code issues PRs
例如,舉例之前要做簡繁轉換時,想法是可以在 html 渲染後透過 Rack middleware 插入一層把文字作處理,畢竟 html 就是純文字,所以去研究如何寫這層 middleware
為什麼要做升級呢?
因為太多依賴,而且沒辦法用新的功能,有想用的功能可能要去搬 code 來用,就會變成東搬一塊西搬一塊,不如直接升級,之後有問題也不用自己處理。
如果想用舊版 rails 不升級又想處理 security issue 可以用 Rails LTS 幫你處理,但要付錢
- 女人迷的 library
- 把好用的 code 搬過來用
想用 Hash#transform_keys
只好打 Monkey patch 在 config/initializers
,可以參考範例
想用 Decrypt session cookie to get user_id,可以參考範例
為什麼要維持 Rails 3.2 長達六年
- 夠穩定
- 夠熟悉
為什麼要做這次升級
- 有些 gem 可能有鎖版本,或是更新了 security issue 但卻沒辦法跟著更新
- 更好擴充
- 想用更新的前端工具,例如 webpacker
可以參考 The Past, Present and Future of Rails at GitHub
為什麼敢做如此大的升級
- 本身對 code base 夠熟,並且僅是內容網站 + 金流,不算太複雜
- 架構乾淨
講者在升級前去把 rails 5.2 到 6.0 的升級內容都一個一個看過,發現沒有太大幅動的改動,同時也去看了所有 rails 6.0 的 milestone
先提案,所以每天的工作內容變成
- 主要任務就是升級到 6.0 beta
- 帶實習生
- 修復緊急需求
升級前後對照
升級前 | 升級後 |
---|---|
Ruby 2.3.5 | Ruby 2.6+ |
Rails 3-2-stable | Rails 6.0 beta3 |
sprocket | webpacker |
unicorn | puma |
升級過程
ruby 2.3.5 => 2.6+
向下兼容做得很好,也幸運的沒有發生相關的錯誤
這裡有個滿好玩的點,如果不想跟著 gem 的 rails 版本,想要跟著 github 上 rails master branch 最新進度,可以用以下的方式去建立 rails project
驗證舊版的 model 在 rails 6 上的狀況
- 做一個新的實驗用的 project,用目標版本 rails 6.0beta
- 把原本舊版本專案下的 model 複製過去
- 每一個 model 都跑
Model.count
去試試看會不會爆炸,炸哪裡修哪裡,例如用User.count
噴錯就照著錯誤訊息開始慢慢 migrate - 修復錯誤直到所有的 model 都可以執行
Model.count
比較常看到爆炸的部分是 scopes, relationships, validates, callbacks
檢查點: 跑
Model.count
直到沒有錯誤訊息
準備升級
建立新的 branch
逐行檢查 config 下的檔案,如果有新的看要增加或跳過 已經存在的,做調整或刪除
升級的細節
ApplicationMailer ApplicationRecord
- config
- boot.rb and enviroments
- config/routes.rb
移除之前為了舊版 rails 所打的 monkey patches
檢查點:
bin/rails c
直到沒有噴錯可以進入
webpacker
- 用 yarn 做 javascript 管理
- 修正 css require 以及 image-url 替換
- 修正 js require, 用 import and export
- use resolve path
- source map config
- 把 sprocket 移除,並且都換成 webpacker methods 類似
javascript_pack_tag
檢查點:
bin/rails s
直到可以打開瀏覽器瀏覽網站
可以只 require 你要的東西
config/application.rb
原本如果是 require "rails/all"
表示把所有 rails 底下的工具都全部拉進去,但其實可以依照情況選擇,比方如下
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
# require "active_job/railtie"
require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
# require "action_mailbox/engine"
require "action_view/railtie"
# require "action_cable/engine"
# require "sprockets/railtie"
# require "rails/test_unit/railtie"
strong parameters 處理
用 binding.pry
去檢查 form 送出的東西有 unpermit 並且看是不是正常運作
研究了一下似乎沒有更優雅的方式處理 nested fields 的 params 而且 nested fields 處理很醜,並且第一層不能是 Array
ckeditor gem
內部後台使用,修了很多細節
因為用了一些插件所以必須要壓在 4.5.x 版本
要修改成 webpacker 把 sprocket 換掉
gem 的版本 4.3 裡面帶的 ckeditor 可能是 4.7,但其實可以指定 config.cdn_url
到 4.5.11
檢查點:
bin/rails s
在 admin 後台
deploy 處理
雖然有 capistrano 3, 但先維持在 2.15.x,反正只是像是 shell script 幫你做完事情,如果你有仔細看這些指令,其實可以自己寫或自己補上去
要修改能夠 deploy puma / webpacker
檢查點:
cap deploy
在正式上線之前的準備
趕著上線,所以沒有把 testing 也一併修好,先捨棄
用了 error handling service Sentry to fix ASAP,上線後盯著看,有噴錯立即修
分兩步驟上線
- 主站 和 API
- Admin
第一次上線
發現 session 問題,所以 API 也要一同上線,要解密 session for user_id
結果發現 admin 那邊不能登,因為 session 跟舊版的方式不一樣了
所以緊急寫個危險的解法先解掉,只要能稱過這段日子就行
第二次上線
升級 ckeditor gem
處理 strong parameters in Admin
維護及優化直到現在
介紹 Rails 6.0 以及要注意的地方
Rails 6.0 改動比較大或有趣的地方
- Security
- Multi-DB
- Zeitwerk
- Parallel testing
- Action Text and Action Mailbox
(這邊不多寫細節了,有興趣可以查一下XD)
要注意的地方
- cache_key 變成只有
model/id
不是原本的model/id-timestamp
,這在 5.2 版本以上要使用的話,要用cache_key_with_version
- 如果要 redirect 外部連結,必須要
allow_other_host: true
否則在 5.2+ 會 fail 並且返回 500 - params 在 5.0+ 版本後已經不是 hash 類別,而是
ActionController::Parameters
,這問題前陣子剛好被binding.pry
雷到,可以參考我另一篇文章,如果要用 hash 必須要用params#to_unsafe_h
- class name 在
5.0+
之後因為 zeitwerk 的緣故,命名需注意,例如:API 要改成 Api
Pro tips
puma-dev 可以在本地跑 https
建立 port 號的 domain file
Run bin/rails s
- use param
-p PORT
- use param
-P PID
可以開多個 server 在同一個專案
這樣一來就可以用 puma-dev 又能 binding.pry
debug 了
Q&A
為什麼要從 unicorn 換 puma
因為 puma 更多人使用,而且 unicorn 作者都不太維護了,而且
- unicorn: Single-threaded multi-process
- puma: Purely multi-threaded
如果搬 gem 的 code 出來用,遇到問題要怎麼管理?
講者回答,通常搬出來都會放上出處等等