Hãy tưởng tượng một câu hỏi có thể là "pending", "approved", hoặc "flagged". Hoặc một số điện thoại là "home", "office", "mobile", hoặc "fax" (nếu là năm 1982).
Một số model đòi hỏi loại dữ liệu này. Một thuộc tính chỉ có thể có một trong vài giá trị khác nhau. Và tập hợp giá trị đó hầu như không bao giờ thay đổi.
Đây là tình huống mà, nếu là Ruby thuần túy, bạn sẽ chỉ dùng symbol.
Bạn có thể tạo model PhoneNumberType hoặc QuestionStatus và quan hệ belongs_to để giữ các giá trị này, nhưng đó không có vẻ đáng giá. Bạn có thể nhét chúng vào file yaml, nhưng bây giờ bạn phải nhìn ở nơi hoàn toàn khác để tìm ra đối tượng của bạn có thể làm gì.
Trong 4.1, Rails đã thử giải quyết vấn đề này với ActiveRecord enums.
Một vài giá trị, trong model
ActiveRecord enums khá dễ dàng. Bạn cho model một cột integer:
bin/rails g model phone number:string phone_number_type:integer
Liệt kê các giá trị thuộc tính đó có thể nhận:
class Phone < ActiveRecord::Base enum phone_number_type: [:home, :office, :mobile, :fax] end
Và bây giờ bạn có thể xử lý chuỗi thay vì số.
Thay vì cái này:
irb(main):001:0> Phone.first.phone_number_type => 3
Bạn sẽ thấy cái này:
irb(main):002:0> Phone.first.phone_number_type => "fax"
Bạn có thể thay đổi thuộc tính đó dùng chuỗi hoặc int:
irb(main):003:0> phone.phone_number_type = 1; phone.phone_number_type => "office" irb(main):004:0> phone.phone_number_type = "mobile"; phone.phone_number_type => "mobile"
Hoặc thậm chí dùng phương thức bang:
irb(main):005:0> phone.office! => true irb(main):006:0> phone.phone_number_type => "office"
Bạn nhận được phương thức để hỏi xem thuộc tính có giá trị cụ thể không:
irb(main):007:0> phone.office? => true
Và bạn có thể tìm tất cả đối tượng với giá trị bạn tìm:
irb(main):008:0> Phone.office Phone Load (0.3ms) SELECT "phones".* FROM "phones" WHERE "phones"."phone_number_type" = ? [["phone_number_type", 1]]
Nếu bạn muốn thấy tất cả các giá trị khác nhau bạn có thể dùng, cùng với số chúng liên kết, dùng phương thức lớp phone_number_types:
irb(main):009:0> Phone.phone_number_types => {"home"=>0, "office"=>1, "mobile"=>2, "fax"=>3}
Điều này làm cho chúng dễ dàng đưa vào form HTML:
<div class="field"> <%= f.label :phone_number_type %><br> <%= f.select :phone_number_type, Phone.phone_number_types.keys %> </div>

Một vài điều cần lưu ý
Enums không phải không có vấn đề, tuy nhiên. Bạn phải giữ vài điều trong tâm trí nếu bạn không muốn gặp rắc rối sau này.
Khi bạn định nghĩa enum, thứ tự quan trọng. Vì vậy nếu bạn quay lại mã và quyết định những giá trị đó thực sự nên theo thứ tự bảng chữ cái:
class Phone < ActiveRecord::Base enum phone_number_type: [:fax, :home, :mobile, :office] end
Điện thoại của bạn sẽ không còn đúng loại nữa. Bạn có thể khắc phục bằng cách nói cho enum số nào đi với giá trị nào:
class Phone < ActiveRecord::Base enum phone_number_type: {fax: 3, home: 0, mobile: 2, office: 1} end
Nhưng thực sự, lựa chọn tốt nhất của bạn là giữ thứ tự nhất quán.
Một vấn đề lớn hơn là làm gì bên ngoài thế giới Rails. Mặc dù Rails thấy các giá trị enum này như chuỗi, chúng chỉ là số bên trong cơ sở dữ liệu của bạn. Vì vậy ai đó nhìn vào dữ liệu thô của bạn sẽ không có ý tưởng những số đó nghĩa là gì. Điều này cũng nghĩa là mọi ứng dụng đọc cơ sở dữ liệu đó sẽ phải biết mapping enum đó.
Bạn có thể dump mapping enum của bạn vào cơ sở dữ liệu hoặc file yaml nếu bạn thực sự cần người khác thấy chúng. Nhưng đó không DRY, vì bây giờ bạn đang định nghĩa enum ở hai nơi. Và nếu bạn đi xa như vậy, có thể tốt hơn làm điều chúng ta tránh ở đầu: tạo một model hoàn toàn riêng và association, để Phone sẽ belong_to PhoneNumberType.
Nhưng nếu bạn giữ nó đơn giản, enums là cách tuyệt vời để bắt đầu.
P.S. Trừ phi bạn bỏ lỡ, Practicing Rails sẽ được bao gồm trong Ruby Book Bundle, ra mắt thứ Hai, 6 tháng 7. Hãy lấy nó và 5 cuốn sách Ruby tuyệt vời khác với giảm giá lớn!