今回は状態管理を容易に実装することができる機能「enum」の使い方についてまとめました。enumは手軽に扱うことができるだけでなく、様々な便利メソッドが使えるため、Railsの状態管理でよく使われる機能の1つになります。
enumとは
enum(= イーナム)とはRails4.1から追加されたモジュールで、1つのカラムに複数の定数を保存することができる機能です。また文字に整数を割り当てることもできます。
# 例(draftを1、publishを2、quitを3として割り当てる)
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
定義方法
enumはハッシュや配列(要素は文字列またはシンボル)の形でモデルに定義します。
以下は投稿を管理するPostモデルにdraft/publish/quitの3つのステータスをハッシュと配列(文字列とシンボル)、それぞれの定義方法で記述した例になります。
# ハッシュでenumを定義した場合
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
# 配列(文字列)でenumを定義した場合
class Post < ApplicationRecord
enum status: [ "draft", "publish", "quit" ]
end
# 配列(シンボル)でenumを定義した場合
class Post < ApplicationRecord
enum status: [ :draft, :publish, :quit ]
end
ハッシュと配列で定義した場合の違い
ハッシュの場合
ハッシュの場合は定義した数字がDBに保存されます。
# ハッシュでenumを定義した場合
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
% Post.create(status: 'draft')
=> statusカラムには1が保存される
% Post.create(status: 'quit')
=> statusカラムには3が保存される
配列の場合
配列の場合は0から始まるインデックス番号がDBに保存されます。
class Post < ApplicationRecord
enum status: [ "draft", "publish", "quit" ]
end
% Post.create(status: 'draft')
=> statusカラムには0が保存される
% Post.create(status: 'quit')
=> statusカラムには2が保存される
enumのメリット
enumを使用する主なメリットとしては以下の3点が挙げられます。
- 改修が容易
- 便利なメソッドが使用できる
- 不正な値がDBに保存されることを防ぐことができる
改修が容易
enumの1つ目のメリットとしては改修の容易さが挙げられます。
先程のステータスを管理するenumで見ていきます。
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
ここに新たなステータス「レビュー待ち(review)」というステータス追加が必要になった場合は、statusにreview: 4
の記述を追加するだけです。
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3, review: 4 }
end
このようにenumで状態を管理することで、状態追加や削除を簡単に行うことができるというメリットがあります。
便利なメソッドが使用できる
2つ目のメリットは便利なメソッドが使用できることです。
enumには以下のような便利なメソッドが用意されているため、実装コストを減らすことができるというメリットもあります。
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
# draftステータスのレコードを作成
% Post.create(status: "draft")
# draftステータスか確認
% Post.last.draft?
=> true
# publishステータスか確認
% Post.last.publish?
=> false
# quitステータスか確認
% Post.last.quit?
=> false
# draftステータスのレコードを作成
% Post.create(status: "draft")
# publishステータスに更新
% Post.last.publish!
# draftステータスか確認
% Post.last.draft?
=> true
# publishステータスか確認
% Post.last.publish?
=> true
# quitステータスか確認
% Post.last.quit?
=> false
不正な値がDBに保存されることを防ぐことができる
3つ目のメリットは不正な値の保存を防ぐことができることです。
enumに定義した値以外を保存しようとするとエラーになるため、予期せぬ値の保存を防ぐことができます。
class Post < ApplicationRecord
enum status: { draft: 1, publish: 2, quit: 3 }
end
% Post.create(status: "review")
=> 保存されない
enumを使ったミニアプリの作成
実際にenumを使ったミニアプリを作成しながら、enumの使い方について見ていきます。
今回はユーザーの権限を管理するミニアプリを想定します。
新規アプリケーションの作成
まずは以下のコマンドで新規アプリケーションを作成します。
% rails new enum_sample -d mysql
% cd enum_sample
% rails db:create
- アプリケーション名: enum_sample
- Ruby: 3.1.2
- Rails: 7.0.4.1
- データベース: MySQL
Userの雛形を作成
次にScaffoldの機能を用いて、string型のnameカラム(名前)とinteger型のgenderカラム(性別)、integer型のroleカラム(権限)を持つUserモデルを作成します。
今回はgender(性別)とrole(権限)をenumで管理します。
% rails g scaffold User name:string gender:integer role:integer
% rails db:migrate

enumの定義
先程作成したUserモデルにenumを定義します。
権限の強さはmaster(マスター権限)>admin(管理者権限)>general(一般権限)としています。
# app/models/user.rb
class User < ApplicationRecord
enum gender: { man: 0, woman: 1, other: 2 }
enum role: { master: 0, admin: 1, general: 2 }
end
ビューの編集
以下のようにビューファイルを編集します。
# app/views/users/index.html.erb
<p style="color: green"><%= notice %></p>
<h1>ユーザー一覧</h1>
<div id="users">
<% @users.each do |user| %>
<%= render user %>
<p>
<%= link_to "詳細", user %>
</p>
<% end %>
</div>
<%= link_to "新規作成", new_user_path %>
# app/views/users/new.html.erb
<h1>新規ユーザー作成</h1>
<%= render "form", user: @user %>
<br>
<div>
<%= link_to "一覧に戻る", users_path %>
</div>
# app/views/users/edit.html.erb
<h1>編集</h1>
<%= render "form", user: @user %>
<br>
<div>
<%= link_to "詳細", @user %> |
<%= link_to "一覧に戻る", users_path %>
</div>
# app/views/users/show.html.erb
<p style="color: green"><%= notice %></p>
<%= render @user %>
<div>
<%= link_to "編集", edit_user_path(@user) %> |
<%= link_to "一覧に戻る", users_path %>
<%= button_to "削除", @user, method: :delete %>
</div>
# app/views/users/_form.html.erb
<%= form_with(model: user) do |form| %>
<% if user.errors.any? %>
<div style="color: red">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.label :name, style: "display: block" %>
<%= form.text_field :name %>
</div>
<div>
<p>性別</p>
<%= form.label :man %>
<%= form.radio_button :gender, :man %>
<%= form.label :woman %>
<%= form.radio_button :gender, :woman %>
<%= form.label :other %>
<%= form.radio_button :gender, :other %>
</div>
<div>
<p>権限</p>
<%= form.label :master %>
<%= form.radio_button :role, :master %>
<%= form.label :admin %>
<%= form.radio_button :role, :admin %>
<%= form.label :general %>
<%= form.radio_button :role, :general %>
</div>
<br>
<div>
<%= form.submit %>
</div>
<% end %>
# app/views/users/_user.html.erb
<div id="<%= dom_id user %>">
<p>
<strong>名前:</strong>
<%= user.name %>
</p>
<p>
<strong>性別:</strong>
<%= user.gender %>
</p>
<p>
<strong>権限:</strong>
<%= user.role %>
</p>
</div>
コントローラの編集
コントローラを以下のように編集します。
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: %i[ show edit update destroy ]
def index
@users = User.all
end
def show
end
def new
@user = User.new
end
def edit
end
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to user_url(@user), notice: "ユーザーが作成されました" }
format.json { render :show, status: :created, location: @user }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to user_url(@user), notice: "ユーザーが更新されました" }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: "ユーザーが削除されました" }
format.json { head :no_content }
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :gender, :role)
end
end
翻訳設定
翻訳設定を追加します。
module EnumSample
class Application < Rails::Application
config.load_defaults 7.0
# 以下の記述を追記
config.i18n.default_locale = :ja
end
end
% touch config/locales/ja.yml
# config/locales/ja.yml
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
name: 名前
helpers:
submit:
create: 登録
update: 更新
挙動確認
enumの翻訳設定
ここまでで一通りの設定はできましたが、よく見るとenum部分のみ英語表記のまま表示されてしまっています。

次はこの部分の翻訳を行います。
enumの翻訳には「enum_help」というGemが用意されているのでそれを使用します。
# Gemfile
gem 'enum_help'
% bundle install
ja.yml
を以下のように編集します。
# config/locales/ja.yml
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
name: 名前
enums:
user:
gender:
man: 男性
woman: 女性
other: その他
role:
master: マスター
admin: 管理者
general: 一般
helpers:
submit:
create: 登録
update: 更新
app/views/users/_user.html.erb
を以下のように編集します。
<div id="<%= dom_id user %>">
<p>
<strong>名前:</strong>
<%= user.name %>
</p>
<p>
<strong>性別:</strong>
<%= user.gender_i18n %>
</p>
<p>
<strong>権限:</strong>
<%= user.role_i18n %>
</p>
</div>
ここまででenumの翻訳が反映されるはずです。

まとめ
- enum(= イーナム)とはRails4.1から追加されたモジュールで、1つのカラムに複数の定数を保存することができる機能
- enumはハッシュや配列(要素は文字列またはシンボル)の形でモデルに定義する
- ハッシュで定義した場合は指定した数字がDBに保存される
- 配列で定義した場合は0から始まるインデックス番号がDBに保存される
- enumのメリットとしては「改修が容易」「便利なメソッドが使用できる」「不正な値がDBに保存されることを防ぐことができる」が挙げられる
参考
今回は状態管理を容易に実装することができる機能「enum」の使い方についてまとめました。状態管理といったenumが使える所は積極的に使い、読みやすいコードになるよう心がけていきましょう。