この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
社会人1年目真っ最中の中西です。
今回は、model内でwith_optionsを用いた時の、ifの扱いについて紹介します。
環境
Rails 4.1.4
Ruby 2.1.2
with_optionsのif
model内で、バリデーションを書く時に、特定の場合のみバリデーションを動かしたい場合、ifを用います。
validates :test_id, length: { minimum: 50 }, if: test?
def test?
return false if hogehuga == 'hogehuga'
true
end
特定の場合のバリデーションが、1つの場合上記で問題ありません。
しかし、特定の場合のバリデーションが複数ある場合、それそれにifをつけるのは大変です。
そこで使用するのが、with_optionsになります。
with_options if: :test? do |v|
v.validates :test_id, length: { minimum: 50 }
v.validates :hoge, presence: true
end
with_optionsに条件を付けることにより、バリデーションをグルーピングすることができます。
この時に、with_optionsでまとめたバリデーションの1つに、さらに条件を加えたくなった場合、下記のように書きました。
with_options if: :test? do |v|
v.validates :test_id, length: { minimum: 50 }
v.validates :hoge, presence: true, if: :hoge
end
これで、大丈夫だと思っていたら、with_optionsでまとめたバリデーションを行う条件が、
「if: :test?」ではなく「if: :hoge」になっていました。
どうも、with_optionsの条件がifの場合、その中でさらにifを用いるとグルーピングの条件であるifを上書きしてしまうようです。
■ 解決方法
解決方法は簡単で、with_optionsの条件がifの場合は、その中ではifは使わない様にするだけです。
中でif文を使いたい場合は、逆のunlessを使います。
unlessを使えば、with_optionsのifを上書きすることはありません。
with_options if: :test? do |v|
v.validates :test_id, length: { minimum: 50 }
v.validates :hoge, presence: true, unless: 'hoge.blank?'
end
また、with_optionsの条件がunlessの場合には、その中でunlessは使わずに、ifを用いると条件を上書きせずにすみます。
rails4.2のwith_options
上記問題とは全く関係ありませんが、rails4.2では、with_optionsの書き方が少し簡略化されたようです。
■ rails4.1
with_options if: :test? do |v|
v.validates :test_id, length: { minimum: 50 }
v.validates :hoge, presence: true
end
■ raile4.2
with_options if: :test? do
validates :test_id, length: { minimum: 50 }
validates :hoge, presence: true
end
ブロック引数をつけなければいけなかったのが、つけなくて良くなったようです。
https://github.com/rails/rails/pull/16339
地味に楽になる気がします。