inclusionなどのバリデーションで検証対象の範囲を検証時に取得する

at 2017-03-15 15:01 (UTC)

最初はこうだった。

class Member < ApplicationRecord
  # gender は 1: 男、2: 女
  belongs_to :gender
end

Rails5 ではこれだけで渡された gender_idgenders テーブルに存在するかどうか見てくれていた。

ところが、会員登録処理が色々と膨らんできたため、フォームクラスを利用せざるを得なくなった。
そこでこんなフォームを書いた。

class MemberForm
  include ActiveModel::Model

  attr_accessor :gender_id
  validates :gender_id, presence: true, inclusion: { in: Gender.pluck(:id), allow_blank: true }
end

実際に動かしてみると問題なく動いてくれたわけだが、テストがコケる。

というのも、バリデーションはクラスのロード時に評価されてクラス単位で持ち回されるので DB を空にして行うテストでは gender のテストデータが登録される前に MemberForm がロードされてしまい、gender_id に何を渡しても存在しないことになってしまう。

class MemberForm
  include ActiveModel::Model

  attr_accessor :gender_id
  validates :gender_id, presence: true, inclusion: { in: ->(_) { Gender.pluck(:id) }, allow_blank: true }
end

そんな場合はこんな感じに lambda を渡してやると良い。引数が渡ってくるので -> { Gender.pluck(:id) } ではエラーになるよ。