[Rails] ステータスを表す値はEnumを使おう【初心者向け】

システムを構築してると、幾つかのステータスを持つことがあります。例えば、商品についてのモデルProductstatusとして、「発売予定」「発売中」「絶版」「再販未定」の4つのステータスを持っているとします。
これらを扱うModelの良い書き方を紹介します。

ステータスを数値を使って表現した時の問題点

それぞれのステータスを10, 20, 30, 40という数値で管理しようと思います。
そのとき単にinteger型のフィールドにすると、例えば、発売中にステータスをフォームは

<%= form_for @product do |f| %>
  <%= f.label :status %>
  <%= f.radio_button :status, 10 %> 発売予定
  <%= f.radio_button :status, 20 %> 発売中
  <%= f.radio_button :status, 30 %> 絶版
  <%= f.radio_button :status, 40 %> 再販未定
<% end %>

となったり、

def in_sale?
  self.status == 20
end

となるなど、ここだけでは意味が分からない数値が入って可読性が悪くなってしまいます。
それを防ぐための機能としてRailsのModelにはEnumという機能が用意されています。

Enumの使い方

Modelに以下のように書きます。

class Product < ActiveRecord::Base
  enum status: {will_be_released: 10 ,in_sale: 20, sold_out: 30, resale_undecided: 40}
end

このように定義してやると、DBの値は10,20,30,40のままで、コード内では:will_be_releasedなどで参照できます。
たとえば、先ほどのViewのコードは以下のように書くことができます。

<%= form_for @product do |f| %>
  <%= f.label :status %>
  <%= f.radio_button :status, :will_be_released %> 発売予定
  <%= f.radio_button :status, :in_sale %> 発売中
  <%= f.radio_button :status, :sold_out %> 絶版
  <%= f.radio_button :status, :resale_undecided %> 再販未定
<% end %>

このようにしてやれば、無意味な数値がコードに入らないのでよみやすくなります。
また、ふたつ目のin_sale?メソッドについてですが、実はeunmを定義した時点で自動的に作成されます。同様にstatus:in_saleに更新するメソッドとしてin_sale!も作成されています。
また、:will_be_released, :in_sale, :sold_out, :resale_undecided以外の値を代入すると、エラーが発生するため、DB自体にConstraintを設定することなく、データの整合性も保つことができます。

補遺

今回、それぞれの判別用の数字を10, 20, 30, 40としましたが、このように10飛びに、あるいは100飛びにすることによって、間に新しいステータスができたとき、15のように間に値を数値入れることができます。

ただのIDとしての役割しか果たしていないので、もちろん、後ろに50とおいてもいいのですが、なんとなく気持ち悪さがあるので、自分はいつもこのように置いています。

まとめ

ModelのEnumを使うことで、得られる主なメリットは可読性の工場ですが、様々な便利なメソッドが用意されたり、DBの設定を変更しなくても保存可能な値を増やせたりします。
また、enum_helpというGemを代入することで多言語対応も可能です。
このような保存する値が決まっているものに関しては積極的にEnumを作っていきましょう。