👨‍💻

《Rails》enum型の使い方を少し深堀る

2024/04/28に公開

こんにちは、ミヤビです(^o^)

テックキャンプの卒業まであとわずかです。

オリジナルアプリは毎日触っていますが一生続きそうな感じですね。

そんな今日はオリジナルアプリ作成で使っているenum型についてまた考えてみます。
そもそも前提が間違っていたな、とかの気づきもあったので共有していきます。

ちなみにずっとenumを「えなむ」と読んでいましたが、「いーなむ」でした^^;
日本人の初学者っぽい感じと僕の天然さも出しつつ書いていきます。

間違っていた前提

ちょっと前に一回ActiveHashとの比較でも書いていました。
https://zenn.dev/d_miyabi/articles/e1ebdc5b1ceeaa
アプリを作成していき、機能を増やしていくにあたって色々気づいたりエラーが出たりしたわけですが、そうなった原因の一つにそもそもの前提が違ったっぽいな、ということがありました。

enumを使う際は、モデルにenumを定義します。

user.rb
  enum funeral_wishes:  { conduct: 0, do_not: 1 }
  enum funeral_form:    { general_funeral: 0, family_funeral: 1, mourner: 2 }

これは現在の僕のオリアプに定義しているenumです。
モデルであるuser.rbに定義しています。
(葬式アプリということで専門用語を使っているのでわかりにくくてすいません)。
以前に定義していたのは下記の様な感じです。

user.rb
  enum funeral_wishes:  { する: 0, しない: 1 }
  enum funeral_form:    { 一般葬: 0, 家族葬: 1, 喪主に任せる: 2 }

何が違うかと言うと、シンプルに英語で定義していたか日本語で定義していたか?の違いです。
これは要件とか設計によってどっちでもいいのかもしれないんですが、お作法的にはおそらく英語で定義する方が良さそうです。
これに気づいたきっかけは、同じ言葉を日本語で使っていたらエラーが発生したからなんですね。
ちょっとそのエラー画面は残していないんですが、たしかArgumentErrorが表示されていました。
例えば下記のような場合にエラーが起こります。

user.rb
  enum funeral_wishes:  { する: 0, しない: 1, その他: 2 }
  enum funeral_form:    { 一般葬: 0, 家族葬: 1, 喪主に任せる: 2, その他: 3 }

この場合は「その他」という言葉を2回定義していた為に発生したと考えています。
同じモデルの中に同じ名前の定義が複数あるとエラーが起こる、という感じです。
機能が少ない内は問題になりませんが増えてくると気づくことってありますね。

英語で定義して翻訳する

さて、お作法が何となく分かったので実際の使い方を少し書いておきます。
これも当然後から気づいてやったので、本来最初にやるべきこともあります。
まずはgemをインストールしておく必要があります。

gemfile
gem 'enum_help'

こちらを記述して「bundle install」しておきます。
enum_helpはenumで定義した値をi18n化させることができるgemですね。

僕なりの今の解釈は「翻訳してる機能」です。ざっくり。
余談ですがbundle installのあとは再起動をしましょう。
これ忘れがち(僕だけ?)。
gemをインストールしたらモデルにenumを定義しておきます。

user.rb
  enum funeral_wishes:  { conduct: 0, do_not: 1 }
  enum funeral_form:    { general_funeral: 0, family_funeral: 1, mourner: 2 }

enumを定義したあとは、その翻訳を記述します。
configのlocalesにja.ymlというファイルを作成して、そこに記述します。

ja.yml
ja:
  enum:
    user:
      funeral_wishes:
        conduct: "する"
        do_not: "しない"
      funeral_form:
        general_funeral: "一般葬"
        family_funeral: "家族葬"
        mourner: "喪主に任せる"

僕はたまたま遭遇していないんですが、このymlに記述する時はインデントに要注意みたいです。
空白がずれていたりするとエラーが起こるみたいです。
あとはセレクトボックスを表示するための記述をビューに書きます。

edit.html.erb
<h3>葬式のこと(希望)</h3>
  <%= form.select :funeral_wishes, User.funeral_wishes.keys.map { |k| [t("enum.user.funeral_wishes.#{k}"), k] }, { include_blank: "指定なし" } %>
<h3>葬式のこと(形態)</h3>
  <%= form.select :funeral_form, User.funeral_forms.keys.map { |k| [t("enum.user.funeral_form.#{k}"), k] }, { include_blank: "指定なし" } %>

これも今でもスラスラと説明はできないんですが。

User.funeral_wishes.keys.map { |k| [t("enum.user.funeral_wishes.#{k}"), k] }

この部分で、userモデルに定義されたfuneral_wishesのenumの各keyを取得して、それを対象の言語に翻訳しています。
この部分は複数形にするのがお作法のようです。
ここまでやったらこの様な画面が出来上がります。
Image from Gyazo
こんな感じでドロップダウンリストが出来上がりました。
Image from Gyazo
ちなみにデータベースに保存されるデータは数字が保存されます。
Image from Gyazo
なのでそもそもテーブル作成時には整数型で設計する必要があります。
この部分がenumのデメリット部分でもあるかもしれませんね。
定義を確認しないと数字が何を示しているのかは分からない感じです。
という感じでenumについてオリアプ作成の中での気付きを言語化してみました。
自分が実装するにあたって下記の記事も参考にしました。
https://blog.to-ko-s.com/enum-select-box/#i-4
初学者だと分からないことが分からない、って状態が多くて毎日悩みますよね。
その試行錯誤はきっと無駄にならないからとにかく止めないで継続していくことだと個人的には思っています。

何かの本で読んだんですが、沸騰の話が印象的です。

こんな感じのニュアンスでした。
水は急には沸騰しないですし、勉強もきっと同じです。
苦しいし辛いけど、でも何か楽しい。
僕にとってのプログラミングの勉強はそんな感じですね。

また気づきやエラーを言語化して共有しますね。

より良い人生にしていきましょう♪

Discussion