Không có gì đơn giản và chán nản hơn câu lệnh case. Nó là di sản từ C. Bạn dùng nó để thay thế một đống if. Case closed. Hay là chưa?
Thực tế, câu lệnh case trong Ruby phong phú và phức tạp hơn bạn tưởng tượng. Hãy xem chỉ một ví dụ:
case "Hi there" when String puts "case statements match class" end # outputs: "case statements match class"
Ví dụ này cho thấy câu lệnh case không chỉ khớp giá trị của item mà còn khớp class của nó. Điều này có thể vì bên dưới, Ruby dùng toán tử ===, hay còn gọi là three equals operator.
Tour nhanh về toán tử ===
Khi bạn viết x === y trong Ruby, bạn đang hỏi "có phải y thuộc nhóm được đại diện bởi x không?". Đây là phát biểu rất chung chung. Cụ thể tùy thuộc vào loại nhóm bạn đang làm việc.
# Ở đây, phương thức Class.===(item) được gọi, trả về true nếu item là instance của class String === "hello" # true String === 1 # false
String, regular expression và range đều định nghĩa phương thức ===(item) riêng, hoạt động như bạn mong đợi. Bạn thậm chí có thể thêm phương thức triple equals vào class của mình.
Bây giờ chúng ta biết điều này, có thể làm mọi trò hay ho với case.
Khớp range trong câu lệnh case
Bạn có thể dùng range trong case nhờ việc range === n đơn giản trả về giá trị của range.include?(n). Làm sao tôi chắc chắn? Nó có trong docs.
case 5 when (1..10) puts "case statements match inclusion in a range" end # outputs "case statements match inclusion in a range"
Khớp regular expression với câu lệnh case
Dùng regex trong case cũng được, vì /regexp/ === "string" chỉ trả về true nếu string khớp regex. Docs của Regexp giải thích điều này.
case "FOOBAR" when /BAR$/ puts "they can match regular expressions!" end # outputs "they can match regular expressions!"
Khớp proc và lambda
Đây hơi lạ. Khi bạn dùng Proc#===(item), nó giống hệt Proc#call(item). Xem docs tại đây. Điều này nghĩa là bạn có thể dùng lambda và proc trong case statement như dynamic matchers.
case 40 when -> (n) { n.to_s == "40" } puts "lambdas!" end # outputs "lambdas"
Viết matcher class của riêng bạn
Như đã nói, thêm hành vi case tùy chỉnh vào class của bạn đơn giản như định nghĩa phương thức === riêng. Một trường hợp dùng có thể là tách logic điều kiện phức tạp thành nhiều class nhỏ. Tôi phác thảo cách hoạt động trong ví dụ dưới:
class Success def self.===(item) item.status >= 200 && item.status < 300 end end class Empty def self.===(item) item.response_size == 0 end end case http_response when Empty puts "response was empty" when Success puts "response was a success" end