生涯未熟

プログラミングをちょこちょこと。

テーブルと紐付かないモデルでenumを実装してみた

掲題の通り、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.statusesenumの一覧を取ってくるところですね。

次。

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 に入れます。

valueSTATUS 内の値にあれば value@status に入れます。

それ以外はエラーとして返します。

最後は status を返す処理です。

def status
  STATUS.key(@status)
end

これはインスタンス変数の @status を値に持つキー値を返しています。

こういった感じでenumを実現してみました。

正直これ以外浮かばなかったのでもっと良い方法をご存じの方は教えて頂ければありがたいです。