Help us understand the problem. What is going on with this article?

Ruby (on Rails) で使える enumeration 実装を比較してみた

More than 3 years have passed since last update.

去年の社のアドベントカレンダーで書いた記事をこちらでも共有。


Ruby, とくに Rails で使える enumeration 実装

  • Enumerize
  • ActiveRecord::Enum

の機能を比較してみました。

以下のコード例では require 'enumerize' しているものとします。また、userUser のインスタンスとします。

Enumerize 概要

The Ruby Toolbox の "ActiveRecord Enumeration" カテゴリでは 1 位になっている gem です。

O/R マッパとは独立した gem なので、Active Record オブジェクトはもちろんのこと、プレーンな Ruby オブジェクトに対してもふつうに使えます。

次のようなコードで、クラスに enumeration を組み込めます。

class User
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium]
end

ActiveRecord::Enum 概要

※名前が長いので以下 enum とします

Rails 4.1 から入った機能です。名前空間が示すとおり、Active Record オブジェクトで使うのが前提となっています。

Active Record を継承したクラスへ次のように enumeration を組み込めます。

class User < ActiveRecord::Base
  enum membership: [:free, :regular, :premium]
end

比較

上で例示した User のインスタンス user を使い、両者の主な機能について表にまとめてみました。

機能 Enumerize ActiveRecord::Enum
値の更新 user.membership = :free user.free!
文字列化 user.membership.text user.membership
デフォルト値の設定 default: :free(lambda も渡せる) DB のデフォルト値設定が別途必要
i18n 対応 デフォルトで対応 enum_help が別途必要
predicate メソッド生成 predicate: true で生成 デフォルトで生成
scope 生成 scope: true で生成し、名前変更も可能 デフォルトで生成
prefix, suffix 生成 predicate だけ生成可能 predicate, 更新メソッドに生成可能(ただし Rails 5.0 から)
序数の取得 user.membership.find_value(:free).value user.membership[:free]
複数値の保持 可能 不可能
テスト用のマッチャ あり なし

以下、かいつまんで見ていきます。

値の更新

enum は bang メソッドで更新できるのでちょっと短く書けて Ruby ぽいです。

user.membership = :regular  # Enumerize
user.regular!               # enum

デフォルト値の設定

Enumerize は enumeration の宣言部分でデフォルト値をオプションとして渡すことができます。

class User
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium], default: :free
end

一方、enum ではマイグレーションの中でデフォルト値を設定する必要があります。

class AddMembershipToUsers < ActiveRecord::Migration
  def change
    add_column :users, :memebership, :integer, default: 0
  end
end

predicate, scope メソッドの生成

Enumerize は enumerize の宣言部分で predicate と Active Record の scope メソッドを生成するオプションを渡せます。scope は名前も変えられます。

class User < ActiveRecord::Base
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium], predicates: true, scope: true
end
user.regular?  # predicate
User.with_membership(:premium)  # premium user だけ取得

# 自分で scope 名を指定できる
class User < ActiveRecord::Base
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium], scope: :having_membership
end
User.having_membership(:free)  # free user だけ取得

一方、enum は自動で predicate, scope の両メソッドを生成してくれています。ただし scope の名前は変えられません。そのぶんシンプルともいえます。

class User < ActiveRecord::Base
  enum membership: [:free, :regular, :premium]
end
user.regular?  # predicate
User.premium  # premium user 取得

プレフィックス/サフィックス生成

Enumerize は enumeration の宣言部分で predicate メソッド生成時に prefix, suffix オプションを渡すと、predicate メソッドのプレフィックス、サフィックスを生成できます。

class User
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium], predicates: { prefix: true }
end
user.membership_free?

# 自分でプレフィックス名を指定できる
class User
  extend Enumerize
  enumerize :membership, in: [:free, :regular, :premium], predicates: { prefix: 'member' }
end
user.member_free?

enum にも Rails 5.0 からプレフィックス/サフィックス生成機能が入りました。_prefix, _suffix というアンダースコア付きのオプションを渡すと predicate, 更新メソッドの両方にプレフィックス/サフィックスを生成します。

class User < ActiveRecord::Base
  enum membership: [:free, :regular, :premium], _prefix: true
end
user.membership_free!  # :free に更新

# 自分でプレフィックスを指定できる
class User < ActiveRecord::Base
  enum membership: [:free, :regular, :premium], _prefix: :member
end
user.member_premium?

複数値の保持

Enumerize は複数の値を保持できます。つまり、次のように、multiple オプションを渡すと、ある enumeration について複数状態を持てます。

require 'active_support/core_ext/object/blank'  # << の中で blank? 使っているので必要

class User
  extend Enumerize
  enumerize :subscriptions, in: [:newspaper, :magazine, :podcast], multiple: true
end
user.subscriptions << :newspaper
user.subscriptions << :podcast
user.subscriptions  #=> #<Enumerize::Set {newspaper, podcast}>

テスト用のマッチャ

Enumerize では RSpec などのマッチャが使えます

describe User do
  it { is_expected.to enumerized(:membership) }
end

まとめ

Enumerize のほうが ActiveRecord::Enum よりまだ機能は多いということがわかりました。とくに、Enumerize にはデフォルトの i18n 対応、デフォルト値設定や scope 名設定の柔軟さがあります。独立した gem であることから、プレーン Ruby オブジェクトや Mongoid オブジェクトで使えるようにもなっています。

一方、Rails 4.1 以降では、ActiveRecord::Enum で Rails の機能の一部として enumeraion の基本機能や predicate や scope の生成と言った機能が使えるようになっていることがわかりました。プロジェクトによっては、これぐらいのシンプルさで十分ということもあると思います。Rails 5.0 へのアップデートが必要になるものの、_prefix, _suffix も使えるようになっていました。

興味を持った方は下記の公式ドキュメントをご参照ください。

pepabo
「いるだけで成長できる環境」を標榜し、エンジニアが楽しく開発できるWebサービス企業を目指しています。
https://pepabo.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした