paranitips

Never stop learning! がモットーのゆるふわエンジニアブログ

with_optionsを使う際の注意点

環境

Railsのバージョンは4.1です。

$ rails -v
Rails 4.1.1

そもそもwith_optionsって?

オプションの記述が冗長にならないようにまとめてくれる便利メソッドです。

class User < ActiveRecord::Base
  validate :name, presence: true, if: :hoge
  validate :address, presence: true, if: :hoge
  validate :email, presence: true, if: :hoge
end

例えば上記はこう書けます。

class User < ActiveRecord::Base
  with_options if: :hoge do |u|
    u.validate :name, presence: true
    u.validate :address, presence: true
    u.validate :email, presence: true
  end
end

validateで使うことが多いですが、他にもいろいろ使えます。

class Account < ActiveRecord::Base
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

ハマりポイント

with_options if:の中でif:を使うと上書きされてしまう。 例えば下記の場合、

class User < ActiveRecord::Base
  with_options if: A do |u|
    u.validate :name, presence: true
    u.validate :address, presence: true, if: B
  end
end

adressはif Aand Bにはならず、if: Bになる

class User < ActiveRecord::Base
  u.validate :name, presence: true, if: A
  u.validate :address, presence: true, if: B #=> 条件Aは無視
end

対処法

with_optionsの中で同じオプションを使わない

さきほどの場合は、unless:を使って対応します。

class User < ActiveRecord::Base
  with_options if: A do |u|
    u.validate :name, presence: true
    u.validate :address, presence: true, unless: !B #=> if: Bと等価
  end
end

with_optionsを分ける

ネストが増えそうな場合はそもそも分けてやったほうがいいです。

class User < ActiveRecord::Base
  with_options if: A do |u|
    u.validate :name, presence: true
  end
  with_options if: [A, B] do |u|
    u.validate :address, presence: true
  end
end

以上です。 意外とハマりがちなので注意しましょう〜。

参考