波打際のブログさん

主に、プログラミング備忘録など。

RailsのEnumと相性が悪い人向けのInumをバージョンアップさせた話。

はじめに

Railsの列挙型事情

 Rails4.1以前では公式に列挙型が実装されておらず、列挙型を実現するために様々なgemが生み出されてきましたが、Rails4.1でついにActiveRecord::Enumという列挙型のクラスがやっと実装され、長い列挙型宗教戦争が終わろうとしています。

ならオワコンサードパーティの列挙型なんていらないのでは?

 残念ながら妄想していた列挙型ではなかったのです。なのでActiveReocrd::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で表示名を付けたい

 I18nyamlで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

参考サイト