ホーム DoRuby with_optionsでifを用いる時の注意

with_optionsでifを用いる時の注意

この記事はアピリッツの技術ブログ「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

地味に楽になる気がします。

記事を共有

最近人気な記事