RailsのEnumと相性が悪い人向けのInumをバージョンアップさせた話。
はじめに
Railsの列挙型事情
Rails4.1以前では公式に列挙型が実装されておらず、列挙型を実現するために様々なgemが生み出されてきましたが、Rails4.1でついにActiveRecord::Enumという列挙型のクラスがやっと実装され、長い列挙型宗教戦争が終わろうとしています。
どうせI18n対応とかじゃないの?
もちろんI18n対応も理由の一つですが、I18n対応だけで良ければ brainspec/enumerize · GitHub あたりを使うのがベストだと思います。それだけでは足りないほしがりさん向けです。
Inumができること
モデルにロジックを食わせてコレ以上肥満体型にしたくない!
そんなあなたにInumダイエット。Inumでは列挙型を独立したクラスとして定義するので、モデルに列挙型に関するロジックを書いて太らせるなんてことはありません。(メンテナンス継続理由の大半を占めています。)
class AnimeType < Inum::Base define :NYARUKOSAN, 0 define :NONNONBIYORI, 1 define :KILLMEBABY, 2 # セリフを表示する def serif case self when NYARUKOSAN "いつもニコニコあなたの隣に這いよる混沌、ニャルラトホテプ、です!" when NONNONBIYORI "にゃんぱすー" when KILLMEBABY "キルミーベイベーは復活する、絶対にだ!!" end end end p AnimeType::KILLMEBABY.serif # => "キルミーベイベーは復活する、絶対にだ!!"
各位@ActiveRecordとなかよく
バージョン3.0.0には ActiveRecord support. by alfa-jpn · Pull Request #4 · alfa-jpn/inum · GitHub super friendly ActiveRecord. という内容のプルリクがマージされています。超仲いいです。
class Anime < ActiveRecord::Base bind_inum :column=> :anime_type, :class => AnimeType end
この1行書くだけで、Animeモデルのanime_typeカラム(integer)に列挙型がバインドされ便利なメソッドが定義されます。
anime = Anime.new # getter/setterで列挙型にラップされます。 anime.anime_type = AnimeType::KILLMEBABY p anime.anime_type.value # => 2 # 判定用メソッドが追加されます。プレフィックスにカラム名が入りますが、bind_inumのオプションで消せます。 p anime.anime_type_nonnonbiyori? # => false
仲がいいものの依存はしておらず、ActiveRecordは必須ではありません。
I18nで表示名を付けたい
I18nのyamlでinum下に記述することでtranslateできます。
ja: inum: anime_type: nyarukosan: '這いよれ!ニャル子さん' nonnonbiyori: 'のんのんびより' killmebaby: 'キルミーベイベー'
translate メソッドもしくは t メソッドで翻訳できます。
p AnimeType::NONNONBIYORI.t # => "のんのんびより"
パワフルにパーズできる
わりと何でもパーズしてくれるメソッドが内部にいるので、代入や比較ができます。
# パーサー p AnimeType.parse(0).t # => "這いよれ!ニャル子さん" p AnimeType.parse('NONNONBIYORI').t # => "のんのんびより" p AnimeType.parse(:KILLMEBABY).t # => "キルミーベイベー" p AnimeType.parse(999) # => nil # ActiveRecordのsetterでもパーサーが働くのでこんなことや anime = Anime.new anime.anime_type = "KILLMEBABY" # わりと色々な箇所に仕組んでいるのでこんな風にも使えます。 p (AnimeType::NONNONBIYORI + 1 ).t # => "キルミーベイベー"
EnamerableとComparebleをinclude
わりと無双な感じ
p AnimeType::KILLMEBABY == 2 # => true p AnimeType::NONNONBIYORI < AnimeType::KILLMEBABY # => true AnimeType.each do |enum| p enum.t end