掲題の通り、Railsでテーブルと紐付かないモデルにenumを実装してみました。
enumを使っている既存のモデルを、APIの返り値をActiveModelで受け取ってゴニョゴニョという形に移行するためにやってみました。
間違ってるよ!とかこうした方がいいよ!ってご意見頂けるとありがたいです。
どういうものを実装するのか?
まずはenumの動きを見てみました。
class Hoge < ActiveRecord::Base
enum status: { ok: true, ng: flase }
end
これで、動きとしては以下のようになりますね。
Hoge.statuses # { "ok" => true, "ng" => flase }
hoge = Hoge.new(status: true)
hoge.status # "ok"
hoge.ok? # true
hoge.ng? # false
hoge.ng!
hoge.status # "ng"
hoge.ok? # false
hoge.ng? # true
基本的に上記のことは出来るように実装していきます。
実装する
以下のように実装してみました.
class Hoge
include ActiveModel::Model
attr_accessor( :status )
STATUS = { ok: true, ng: false }.with_indifferent_access
class << self
def statuses
STATUS
end
end
STATUS.each do |key, value|
define_method("#{key}?") { @status == value }
define_method("#{key}!") { @status = value }
end
def status=(value = nil)
if STATUS.has_key?(value) || value.blank?
@status = STATUS[value]
elsif STATUS.has_value?(value)
@status = value
else
raise ArgumentError, "'#{value}' is not a valid status"
end
end
def status
STATUS.key(@status)
end
こんな感じの実装になりました。
詳細説明
まずはここの説明です。
STATUS = { ok: true, ng: false }.with_indifferent_access
STATUSという定数にハッシュを入れてます。
with_indifferent_access はシンボルでも値でも取り出せるためにやってます。
次はここ。
class << self
def statuses
STATUS
end
end
これは Hoge.statuses でenumの一覧を取ってくるところですね。
次。
STATUS.each do |key, value|
define_method("#{key}?") { @status == value }
define_method("#{key}!") { @status = value }
end
これはSTATUSの中身を元にインスタンスメソッドを作ってます。
引数がメソッド名で処理内容がProcに書いてます。
これで ? と ! の処理ができました。
次は status のset処理です。
def status=(value = nil)
if STATUS.has_key?(value) || value.blank?
@status = STATUS[value]
elsif STATUS.has_value?(value)
@status = value
else
raise ArgumentError, "'#{value}' is not a valid status"
end
end
これは引数の value と同値のキー値が存在すれば、そのキー値に対応する値を @status に入れます。
value が STATUS 内の値にあれば value を @status に入れます。
それ以外はエラーとして返します。
最後は status を返す処理です。
def status STATUS.key(@status) end
これはインスタンス変数の @status を値に持つキー値を返しています。
こういった感じでenumを実現してみました。
正直これ以外浮かばなかったのでもっと良い方法をご存じの方は教えて頂ければありがたいです。