Rails4.1のActiveRecord::Enumを旧バージョンのRailsやRails以外でも使いたい


本日既に12/11ですが、1日目が空席でしたので参加します！よろしくお願いします！

# 概要
Rails 4.1 で導入された`ActiveRecord::Enum`が気に入っており使いたかったのですが、悲しい事情により使えなかったため、旧バージョンの Rails や Rails 以外でも動くミニクローンを作ってみました。

# 昔話
昔話と書きましたが、今年の初めの話です。

Ruby を学び始めたばかりの頃、書きたいものが何も思い浮かばなかったので、とりあえず自分で定義した凄く簡単な BNF を元に、レキシカルアナライザやパーサを実装してみていました。

その際、トークン型を列挙型で定義しようとして「Ruby って列挙型ないんだ！」と知りました。

# ActiveRecord::Enum
Ruby には列挙型はないんですが、Rails には4.1から`ActiveRecord::Enum`という列挙型が導入されました。

こいつがなかなか便利なやつでして、例えば、User オブジェクトにステータスを持たせるとします。

まずは`ActiveRecord::Enum`を使わず、愚直に書いてみます。

```ruby
class User
  STATUSES = {
    active: 0,
    inactive: 1
  }

  attr_accessor :status

  def active?
    @status == STATUSES[:active]
  end

  def inactive?
    @status == STATUSES[:inactive]
  end
end
```

User オブジェクトのステータスを更新しようと思ったら、

```ruby
user.status = User::STATUSES[:active]
```

と書けば更新できますし、User のステータスがアクティブか確認したかったら、

```ruby
user.active?
```

と、書くことができます。

さて、ここで`ActiveRecord::Enum`を使ってみます。

すると、

```ruby
class User < ActiveRecord::Base
  enum status: %i(active inactive)
end
```

と、書くことができます！短い！めっちゃ短い！

どんな風に動くか Rails Console で確認してみます。

```ruby
# ステータスがアクティブかを確認
pry(main)> user.status
=> "active"
pry(main)> user.active?
=> true
pry(main)> user.inactive?
=> false

# シンボルを代入してステータスを更新
pry(main)> user.status = :inactive
=> :inactive
pry(main)> user.status
=> "inactive"
pry(main)> user.active?
=> false
pry(main)> user.inactive?
=> true

# 破壊的メソッドでステータスを更新
pry(main)> user.active!
=> true
pry(main)> user.active?
=> true

# ステータス一覧を取得
pry(main)> User.statuses
=> {"active"=>0, "inactive"=>1}
```

めっちゃ便利！凄い！

より詳しく挙動を知りたい方は、

- [ActiveRecord::Enum](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html)

をご覧ください。

# 悲しい世界
これを使ってリファクタリングできそうなソースを見つけたのですが、Rails が古かったため、使えませんでした。悲しかったです。

また、`ActiveRecord::Enum`の機能なので、当然 ActiveRecord でしか使えません。
また、冒頭の昔話で述べたような、ただの Ruby プログラムに ActiveRecord 入れるのは……どうなんでしょう。

# 作ってみた
ないなら作ればいいじゃないかということで、試しに自分で作ってみました。

- [超シンプルな列挙型 (enum) を実現する gem 作ってみた - 雀巽の日記帳](http://necojackarc.hatenablog.com/entry/2015/11/11/090000)

SimplerEnum という名前で公開中です。

簡素で質素な enum ですよという謙虚な気持ちで名前をつけたんですが、同僚に「他のあらゆる enum よりもシンプルだぞとアピールしてる感じが良い」と言われました……まじかよ。

((((；ﾟДﾟ))))ｶﾞｸｶﾞｸﾌﾞﾙﾌﾞﾙ

使い方を先ほどの User の例でご紹介します。

```ruby
require 'simpler_enum'

class User
  include SimplerEnum

  simpler_enum status: %i(active inactive)
end
```

宣言は Rails の enum を丸パクリです！わはは！

さて、それでは動かしてみます。

```ruby
# ステータスがアクティブかを確認
pry(main)> user.status
=> :active
pry(main)> user.active?
=> true
pry(main)> user.inactive?
=> false

# シンボルを代入してステータスを更新
pry(main)> user.status = :inactive
=> :inactive
pry(main)> user.status
=> :inactive
pry(main)> user.active?
=> false
pry(main)> user.inactive?
=> true


# 破壊的メソッドでステータスを更新
pry(main)> user.active!
=> :active
pry(main)> user.active?
=> true

# ステータス一覧を取得
pry(main)> User.statuses
=> {:active=>0, :inactive=>1}
```

ほとんど一緒！便利便利！

Github の README でもう少し詳しく書いていますので、よければそちらをご覧ください。

- [necojackarc/simpler_enum - Github](https://github.com/necojackarc/simpler_enum)

サポートバージョンは Ruby 1.9 以上としています。

# Rails の enum との違い
ほとんど挙動が一緒ですが、Simpler と言ってるだけあって、かなり機能削減しています。

Rails は文字列を列挙型のキーとして使っており、更にシンボルでの操作に対応していますが、SimplerEnum ではシンボルをキーとして使っており、シンボルでの操作しか行うことができません。

また、SimplerEnum にはプレフィックスやサフィックスを付ける機能もありません。

シンプルに、

- 値一覧取得メソッド
- 状態問い合わせメソッド
- 状態変更メソッド
- シンボルでの入出力対応

のみを提供しています。

# 稼働実績
会社のプロダクションコードに、SimplerEnum をぶち込んでリファクタリングする PR をぶん投げてみたら、なんとびっくりレビュー通過して本番投入されました。

というわけで、とある Web service で絶賛稼働中です:laughing:

Rails バージョンは秘密です:innocent:

# 課題
全体的に作りが甘いです。テストも含めて。

まず、例外チェックが雑です。

他には、カバレッジは99%となっていますが、AcitveRecord で SimplerEnum を使用するテストケースは作成していません。手作業で確認してしまいました。

こう動けばいいなという、一番外側の振る舞いだけテストファーストで書いて一気に実装したので、E2E テストしかないような状態になっています。

# まとめ
Ruby で列挙型使いたいなと思った時にどうぞ！