ニュース
Rails7 がリリースされたことで enum 構文の記載方法が少し拡張されました。(得られる結果は現状同じ)
- Before(Rails5, Rails6)
enum status: { active: 10, archived: 20 }, _prefix: true
enum comments_status: { active: 10, inactive: 20 }, _suffix: true
- After(Rails7)
enum :status, { active: 0, archived: 10 }, prefix: true
enum :comments_status, { active: 10, inactive: 20 }, suffix: true
余計なアンダースコアなども抜け落ちてスッキリし、Afterの方が印象は良いですね。
この記事で伝えたいこと
Rails5 からActiveRecord::Enum
を定義する際は_prefix
、_suffix
という武器がある事を知っていただきたい。
核心部分だけ見たい方は、ショートカット
2019.09.12
文章の 「です・ます」調を統一。
2019.06.01
例として使用するテーブルの説明を追記
Rails のバージョン
./bin/rails --version
Rails 5.2.0
_prefix、_suffix の定義
You can use the :_prefix or :_suffix options when you need to define multiple enums with same values. If the passed value is true, the methods are prefixed/suffixed with the name of the enum. It is also possible to supply a custom value:
同じ値を持つ複数のenumを定義する必要がある場合は、_prefixまたは:_suffixオプションを使用できます。渡された値がtrueの場合、メソッドにはenumの名前の接頭辞/接尾辞が付けられます。カスタム値を指定することもできます。
が今回のテーマ。さっそく同じ値を持つ複数の enum
を定義していこう。
とその前に、よくある enum
定義とそれが良くない理由を深掘りしていき理解度を深めよう。
定義したい内容
Conversation
(カンバセーション:会話、対話) というテーブルを例として、2つの enum
を定義したい。
- status (ステータス。0=有効、10=アーカイブ)
- comments_status (コメントのステータス。0=有効、10=無効)
まちがった定義その1
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 10 }
enum comments_status: { active: 0 inactive: 10 }
end
結果
irb(main):004:0> c = Conversation.find(1)
ArgumentError (You tried to define an enum named "comments_status" on the model "Conversation", but this will generate a instance method "active?", which is already defined by another enum.)
エラー。後から追加したcomments_status
のactive
が重複しているため。
まちがった定義その2
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 10 }
enum comments_status: { comments_status_active: 0, comments_status_inactive: 10 }
end
間違いその1のエラーを回避するために後から追加したcomment_status
の値を書き換えている。確かにエラーにはならないが、結果的にわかり辛くなった。以下のように比較する際に気持ち悪くなりそう。
conversation.comments_status_active? # 分かりやすい
conversation.active? # 「status.active か comments_status.active」 のどちらか分かり辛い。
まちがった定義その3
class Conversation < ActiveRecord::Base
enum status: { status_active: 0, status_archived: 10 }
enum comments_status: { comments_status_active: 0, comments_status_inactive: 10 }
end
今度は統一性を持たすためにそれぞれ接頭辞をつけるようにした。
これで重複エラーも回避、統一性があり気持ち悪さも回避と問題解消。
conversation.comments_status_active? # 分かりやすい
conversation.status_active? # 分かりやすい
確かにこれで「重複もしない」、「混乱もしにくい」と問題なさそうだが、_prefix(接頭辞)、_suffix(接尾辞)機能を活用できていないという点で良い設定とは言えないだろう。
_prefix(接頭辞)_suffix(接尾辞)を使う
ここからが本テーマの中枢。
_prefix(接頭辞)
class Conversation < ActiveRecord::Base
enum status: [:active, :archived], _prefix: true
enum comments_status: [:active, :inactive], _prefix: true
end
各 enum 行の後ろに _prefix: true
を付与。
:active
は、 enum status
と enum comments_status
のどちらにも存在する値だが、 _prefix
を付与することで重複エラーにはならない。
_prefix
は接頭辞となるため以下のような操作が可能になる。
# 頭に status_ を付与
> conversation.status_archived! # 更新(UPDATE)
> conversation.status_archived? # true
> conversation.status_active? # false
# 頭に comments_status_ を付与
> conversation.comments_status_active! # 更新(UPDATE)
> conversation.comments_status_active? # true
> conversation.comments_status_inactive? # false
# 接頭辞なしはエラーになる
> conversation.active?
Traceback (most recent call last):
1: from (irb):11
NoMethodError (undefined method `active?' for #<Conversation:0x00007ff78097c910>)
_suffix(接尾辞)
class Conversation < ActiveRecord::Base
enum status: [:active, :archived], _suffix: true
enum comments_status: [:active, :inactive], _suffix: true
end
続いて _suffix
(接尾辞)。以下のような操作が可能となる。
# お尻に _status を付与
> conversation.archived_status! # 更新(UPDATE)
> conversation.archived_status? # true
> conversation.active_status? # false
# お尻に _comments_status を付与
> conversation.active_comments_status! # 更新(UPDATE)
> conversation.active_comments_status? # true
> conversation.inactive_comments_status? # false
# 接尾辞なしはエラーになる
> conversation.active?
Traceback (most recent call last):
1: from (irb):11
NoMethodError (undefined method `active?' for #<Conversation:0x00007ff78097c910>)
カスタム値
class Conversation < ActiveRecord::Base
enum status: [:active, :archived], _prefix: true
enum comments_status: [:active, :inactive], _prefix: :comments
end
comments_status
側だけ_prefix: :comments
とカスタム値を設定。
こうすることで、 接頭辞comments_status_
をつけるよりcomments_
だけでいいので文字数が減り+意味も理解しやすいという利点がある。
# 頭に comments_ を付与
> conversation.comments_active! # 更新(UPDATE)
> conversation.comments_active? # true
> conversation.comments_inactive? # false
まとめ
上記の間違い1,2,3は実際に私が遭遇した内容であり、もしどこかで同じようなプロジェクトに遭遇している方はぜひ _prefix
, _suffix
を利用しより良いコードを体験してみてほしい。
良い controller は良い model が作る。 良い model は良い enum が作る
参考リンク
Ruby on Rails 5.1.6 > ActiveRecord::Enum
Change the World!> ActiveRecord::Enum の使い方と重複エラーの避け方
Qiita > Rails5でenum定義したカラムの元の値を取得