โจทย์ที่อาจเป็น "pending", "approved", หรือ "flagged" หรือเบอร์โทรที่เป็น "home", "office", "mobile", หรือ "fax" (ถ้าเป็นปี 1982)
บาง models ต้องการข้อมูลแบบนี้ Attribute ที่มีค่าได้เพียงค่าหนึ่งจากชุดค่าที่จำกัด และชุดค่านั้นเกือบไม่เปลี่ยน
สถานการณ์ที่ถ้าเป็น plain Ruby คุณจะ用 symbol
คุณอาจสร้าง model PhoneNumberType หรือ QuestionStatus และ belongs_to relationship เพื่อเก็บค่าเหล่านี้ แตไม่คุ้มค่า อาจ stuff ลง yaml file แตตอนนี้ต้องไปดูที่ต่างหากเพื่อรู้ว่า object ทำอะไรได้บ้าง
ใน Rails 4.1 Rails พยายามแก้ปัญหานี้ด้วย ActiveRecord enums
ค่าจำกัดใน Model
ActiveRecord enums ง่ายมาก ให้ model มี column integer:
bin/rails g model phone number:string phone_number_type:integer
ระบุค่าที่ attribute รับได้:
class Phone < ActiveRecord::Base enum phone_number_type: [:home, :office, :mobile, :fax] end
ตอนนี้จัดการกับ strings แทน numbers
แทนที่จะเป็น:
irb(main):001:0> Phone.first.phone_number_type => 3
จะได้:
irb(main):002:0> Phone.first.phone_number_type => "fax"
เปลี่ยน attribute ได้ด้วย strings หรือ ints:
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"
หรือ用 bang method:
irb(main):005:0> phone.office! => true irb(main):006:0> phone.phone_number_type => "office"
ได้ methods สำหรับถามว่า attribute มีค่าเฉพาะไหม:
irb(main):007:0> phone.office? => true
และหา ทั้งหมด objects ที่มีค่าที่ต้องการ:
irb(main):008:0> Phone.office Phone Load (0.3ms) SELECT "phones".* FROM "phones" WHERE "phones"."phone_number_type" = ? [["phone_number_type", 1]]
ถ้าอยากดูค่าทั้งหมดพร้อม numbers ที่ map ไป ใช้ class method phone_number_types:
irb(main):009:0> Phone.phone_number_types => {"home"=>0, "office"=>1, "mobile"=>2, "fax"=>3}
ทำให้ใส่ใน HTML form ง่าย:
<div class="field"> <%= f.label :phone_number_type %><br> <%= f.select :phone_number_type, Phone.phone_number_types.keys %> </div>

สิ่งที่ต้องระวัง
Enums ไม่ได้ไม่มีปัญหา ต้องจดจำบางอย่างไว้ถ้าไม่อยากเจอปัญหาภายหลัง
ตอน define enum ลำดับสำคัญ ถ้ากลับมาเปลี่ยนลำดับเป็น alphabetical:
class Phone < ActiveRecord::Base enum phone_number_type: [:fax, :home, :mobile, :office] end
Phones จะมี types ผิด แก้ได้โดยบอก enum ว่าเลขไหนไปกับค่าไหน:
class Phone < ActiveRecord::Base enum phone_number_type: {fax: 3, home: 0, mobile: 2, office: 1} end
แตตัวเลือกดีที่สุดคือเก็บลำดับให้ consistent
ปัญหาใหญ่กว่าคือทำยังไงนอกโลก Rails แม้ Rails เห็น enum values เป็น strings พวกมันแค่ numbers ใน database คนดู raw data จะ ไม่มี ว่าตัวเลขเหล่านี้หมายถึงอะไร ทุก app ที่อ่าน database นั้นจะต้องรู้ enum mapping
อาจ dump enum mapping ลง database หรือ yaml ถ้าจำเป็น แตไม่ DRY เพราะ define enum ในสองที่ ถ้าไปไกลขนาดนั้น อาจดีกว่าสร้าง model แยกและ association ให้ Phone belong_to PhoneNumberType
แตถ้าเก็บง่ายๆ enums เป็นวิธีเริ่มต้นที่ดี
P.S. กรณีพลาด Practicing Rails จะอยู่ใน Ruby Book Bundle launching วันจันทร์ 6 กรกฎาคม รับมันและหนังสือ Ruby อื่นๆ อีก 5 เล่ม ราคาพิเศษ!