Posted at

RailsでenumのカラムをSTIとして使う

More than 1 year has passed since last update.


enum の中身で処理が別れるようなコード

enum を使っていると以下のようなコードが増えていくことがある。

明らかに enum の値によって振舞いを変えているので、 enum の値によってそもそもクラスを変えていきたい。 というわけでenumで使っているカラムをSTIとしても使えないのか試してみました。

class Payment < ActiveRecord::Base

enum means: { credit_card: 0, bank_transfer: 10, paypal: 20 }

def complete!
if credit_card?
# do something
elsif bank_transfer?
# do something
end
end

def cancel!
if credit_card?
# do something
elsif bank_transfer?
# do something
end
end
end


enum のカラムをSTIでも使う

結論、以下のように書けばいけるっぽい。

class Payment < ActiveRecord::Base

enum type: { credit_card: 0, bank_transfer: 10, paypal: 20 }

class << self
def find_sti_class(type)
type_name = types.key(type.to_i) # enumのhashから該当するkeyを取得する
"Payment::#{type_name.to_s.camelize}".constantize
end

def sti_name
types[name.demodulize.underscore]
end
end

def complete!
fail NotImplementedError
end

def cancel!
fail NotImplementedError
end
end

こうしておけば、 通常のSTIと同じく、 各 type に対応するクラスを定義できる。

class Payment::CreditCard < Payment

def complete!
end

def cancel!
end
end

class Payment::BankTransfer < Payment
def complete!
end

def cancel!
end
end

class Payment::PayPal < Payment
def complete!
end

def cancel!
end
end

最初から enum で処理を変える必要があることがわかっているのであれば、素直にテーブルを分けたり普通のSTIを選択した方がハマりどころも少なそうで良いとは思います。

が、これはこれでDBにクラス名がそのまま入ることもないし、enum で生成されるスコープや credit_card? みたいなメソッドはそのまま使えるので、実は結構有りなのでは・・・!と思ったりしました。