你可能聽過(甚至自己說過)Rails / Ruby 有太多 magic……
- 這個想法從何而來?
- Rails magic 到底是什麼?
- 你能做什麼來消除這種 magic?
這是我的看法:
當某事感覺像 magic 時,是因為有你不知道的事,有些資訊缺失。
這就像魔術表演,如果你知道把戲就沒有 magic 了。

在軟體開發中,知道把戲等同於擁有 對事物真實運作方式的理解。
來看個例子!
這個例子關於 BCrypt,一種用於安全儲存密碼的雜湊演算法。它在 Ruby 中透過 bcrypt gem 實現。
這裡展示如何用 BCrypt 雜湊 "testing":
require 'bcrypt' BCrypt::Password.create("testing") # "$2a$10$3o.xrISG8fqKfzKqDpgKn.3cwjFV//9C9cZ7MuK5S9sNKFBivB7YG" BCrypt::Password.create("testing") # "$2a$10$BFb/e3cWuAIyelcmkfGtE.GTifXHxig4IzVbdZs9C1zSpF/Jr6c3W" BCrypt::Password.create("testing") # "$2a$10$Q952BVgM783Y4kPvwvxwC.CS2DWUX9jQuDoE6q2R041kQY0ZWPnSe" 注意每次都得到不同的雜湊,但如果用 MD5(別用!)或 SHA1,給定字串總會得到相同輸出。
為什麼 BCrypt 行為不同?
我馬上解釋,但先看看如何比較兩個 bcrypt 雜湊,一個來自資料庫,一個來自使用者輸入(如表單)。
BCrypt::Password.new(@user.hash) == params[:password] # true
左邊(BCrypt::Password.new)是 BCrypt 物件,接收資料庫儲存的雜湊作為參數。
右邊(params[:password])是使用者嘗試登入的明文密碼。
這裡假設使用正確的使用者/密碼組合。
為什麼這評估為 true?
要理解這點,你需要知道兩件事:
- BCrypt 使用所謂的「salt」(鹽),這是 用於增加安全性 對抗預計算雜湊的隨機值。Salt 儲存在雜湊本身裡。
- 在 Ruby 中 許多看起來像語法的東西其實只是方法。雙等號運算子(
==)就是這種情況。
知道這點,我可以告訴你 BCrypt 定義了自己的 == 方法,知道如何提取那個「salt」值,以便在比較密碼時考慮它。
換句話說:
BCrypt#== 從儲存的雜湊取出「salt」值。
然後用這個 salt 雜湊明文密碼(使用者輸入),這樣如果密碼有效,兩個雜湊就會相同。
如果你 看原始碼 它看起來會像這樣:
def ==(secret) super( BCrypt::Engine.hash_secret(secret, @salt) ) end
記住 super 會在父類別上呼叫同樣的方法(這裡是 ==)。
BCrypt::Password 的父類別是 String。
一切關乎方法
一件重要的事是理解除了少數關鍵字和幾個語法元素(如括號),一切都是關於類別和方法。
如果你知道你在用什麼類別(用 class 方法檢查),你總能知道有哪些操作(方法)可用。
但有時我們只有沒有物件的方法呼叫。
這通常意味著方法定義在當前類別,但不總是如此。
例子:
puts 123
puts 定義在哪?來找出來:
method(:puts).owner # Kernel
另一件可能令人困惑的事是 metaprogramming,因為有了 metaprogramming 你可以在程式執行時建立、修改甚至移除方法。這些讓代碼更不透明、更晦澀。
你可以透過了解有哪些 metaprogramming 方法可用及其運作方式來減少困惑。
總結
你學到了深入理解事物運作方式如何幫助你消除任何「magic」、寫出更好的代碼、成為更好的開發者。
如果你想提升 Ruby 技能,看看我的書《Ruby Deep Dive》,專為填補基礎與進階概念之間的鴻溝而設計 🙂