あゆの塩焼きブログ

【Rails】enumは素晴らしい。もっと使いやすくしよう!

Agenda目次
  • ・🚀 Railsのenum、もっと使いやすくしたい
  • ・🛠️ まずは復習:Rails標準のenumの素晴らしさ
  • ・🌟 解決策:EnumBaseパターンを使おう!
  • ・✍️ EnumBaseパターン 実装例
  • ・🚀 さらに発展:Enumに色やソート順も持たせよう
  • ・🎯 まとめ
  • ・📝 最後に

🚀 Railsのenum、もっと使いやすくしたい

プログラミングをしていると、必ず出会う「状態管理」。

Railsではenumを使えば簡単に状態管理できますが、標準機能だけでは物足りないと感じたことはありませんか?

  • 状態に日本語ラベルを持たせたい
  • 状態ごとに説明文も持ちたい
  • 将来的に状態の追加・変更に強い設計にしたい

上記を一気に解決するのが、EnumBaseパターンです。

この記事では、普通のenumから一歩踏み込んで、実践で本当に使える状態管理を一緒に目指しましょう!

🛠️ まずは復習:Rails標準のenumの素晴らしさ

Railsのenumは、手軽にマジックナンバーを排除できる仕組みです。

class User < ApplicationRecord
  enum status: { active: 0, inactive: 1, archived: 2 }
end

user.active!
user.inactive?
User.active

これだけで
- メソッドが生える
- スコープが作れる
- 可読性がグッと上がる(マジックナンバー解消)

最高ですよね。

でも、Rails標準のenumには一つだけ弱点があります。

状態に付加情報(表示名・説明など)を持たせられない。

これ、地味に辛いんです。

🤔 どんな問題が起こるのか?

例えば、ユーザー状態を日本語で表示したい場合。

標準のenumではビュー側でI18nを使うパターンが多いですが、

管理が煩雑になったり、状態追加時にコードと表示の対応がズレる危険もあります。

<%= t("enums.user.status.#{user.status}") %>

さらに「アクティブユーザーって何?」みたいな説明文も欲しくなると、もうEnumでは対応しきれなくなってしまいます...

🌟 解決策:EnumBaseパターンを使おう!

EnumBaseパターンでは、

「状態」を単なる数字ではなく、情報を持ったオブジェクトとして扱います。

これによって、

ラベル・説明・順番・色指定など

なんでも管理できる、リッチな状態管理が実現できます。

✍️ EnumBaseパターン 実装例

1. 共通親クラスを作成する

class EnumBase
  attr_reader :key, :value, :label, :description

  def initialize(key, value, label:, description: nil)
    @key = key
    @value = value
    @label = label
    @description = description
  end

  def to_s
    label
  end

  def self.all
    constants.map { |c| const_get(c) }
  end

  def self.find_by_value(value)
    all.find { |e| e.value == value }
  end

  def self.options_for_select
    all.map { |e| [e.label, e.value] }
  end
end

ここでは
- key:プログラム内での識別子
- value:DBに保存される数値
- label:人間向けの表示名
- description:オプションの説明文

を持たせています。

2. 実際のEnumクラスを作る

class UserStatus < EnumBase
  ACTIVE   = new(:active, 0, label: 'アクティブ', description: '通常利用中のユーザー')
  INACTIVE = new(:inactive, 1, label: '非アクティブ', description: '一時停止中のユーザー')
  ARCHIVED = new(:archived, 2, label: 'アーカイブ済み', description: '退会したユーザー')
end

3. モデルとつなぐ

class User < ApplicationRecord
  enum status: {
    active: UserStatus::ACTIVE.value,
    inactive: UserStatus::INACTIVE.value,
    archived: UserStatus::ARCHIVED.value
  }

  def status_label
    UserStatus.find_by_value(status_before_type_cast)&.label
  end

  def status_description
    UserStatus.find_by_value(status_before_type_cast)&.description
  end
end

✨ リファクタリング事例

もともと、ビューにこんなコードが乱立していたとします。

<% if user.status == 'active' %>
  アクティブ
<% elsif user.status == 'inactive' %>
  非アクティブ
<% elsif user.status == 'archived' %>
  退会済み
<% end %>

これを、EnumBaseパターンを使うと…

<%= user.status_label %>

一行で完結。

しかも状態追加・変更があっても、Enumクラス側だけ直せばOK!

ビューやコントローラーには一切影響を与えません。

🚀 さらに発展:Enumに色やソート順も持たせよう

たとえばEnumBaseをこう拡張すれば、

class EnumBase
  attr_reader :key, :value, :label, :description, :color, :sort_order

  def initialize(key, value, label:, description: nil, color: nil, sort_order: nil)
    @key = key
    @value = value
    @label = label
    @description = description
    @color = color
    @sort_order = sort_order
  end

  def self.all_sorted
    all.sort_by(&:sort_order)
  end
end

色付きバッジ表示や、状態リストの並び替えも自由自在にできちゃいます!

🎯 まとめ

項目 Rails標準enum EnumBaseパターン
管理できる情報 数字とシンボルのみ ラベル・説明・色・順序・何でも
拡張性 低い 高い
ビューコードのシンプルさ 普通 激シンプル
メンテナンス性 普通 高い

📝 最後に

Rails標準のenumはすばらしい。

でも、それを自分たちの設計思想に合わせて進化させることもまた、Railsらしさだと思います。

状態管理の未来を、あなたの手でリッチにしていきましょう。

Loading...