ActiveRecordクラスに動的にバリデーションコードを追加する

RailsWay 250ページあたりに出てくる話題として、ActiveRecordでレコードを読み出す時に動的にバリデーションコードを定義するという話題があり、場合によっては非常に便利だが、RailsWayのコードそのままだとちょっとアレでうまくいかないので、正しいコードに修正してみた。

もともとは、こんな感じ:

class Account < ActiveRecord::Base 
... 
private 
  def after_find 
    singleton = class << self; self; end 
    singleton.class_eval(config) 
  end 
end 

これで基本は問題ないのだが、configの部分に投入するバリデーションコードは

validates_numericality_of :product_code, :only_integer => true 
validates_length_of :product_code, :is => 10 

ではなく、

ActiveRecord::Base.validates_numericality_of :product_code, :only_integer => true 
ActiveRecord::Base.validates_length_of :product_code, :is => 10 

のようにActiveRecord::Baseを指定してあげないとエラーが出る事もなくバリデーションが正しく実行されないという現象になる。

この例ではafter_findコールバックを使っているけど、before_validationなどで使うほうが一般的かもしれない。

ちなみになんらかの工夫をしないと多重にバリデーションを追加してしまう事態になるので要注意。