掲題の通り、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を実現してみました。
正直これ以外浮かばなかったのでもっと良い方法をご存じの方は教えて頂ければありがたいです。