読者です 読者をやめる 読者になる 読者になる

mo-fu note

技術のこととか色々書きます

Active RecordのConditional Validation便利

rails
  • ステータス(status)が1になるまでデータが送られ続ける
  • ステータスが1の時は配送完了日(completion_date)が入る
  • ステータスが1以外の時は配送完了日がnilになる

みたいなデータ連携をした時に便利だった。 外部のアプリケーションで作られたデータを自分のアプリケーションのDBにInsertする時、 データの不整合が起きないように、条件を指定して検証できる。

実行環境

  • Ruby 2.2.2
  • Rails 4.2.3
  • RSpec 3.3
  • factory_girl 4.5.0
  • sqlite3 1.3.10

Item

delivered? が true の時(statusが1の時)だけ completion_date のバリデーションを実行する。 以下のように書くとできる。

class Item < ActiveRecord::Base
  validates :status, format: { with: /\A([1-9])\z/}, presence: true
  validates :completion_date,
    format: { with: /(\A\d{4}/\d{1,2}/\d{1,2}\z)/ },
    presence: true,
    if: :delivered?

  def delivered?
    status == 1
  end
end

バリデーションのテスト

RSpec.describe Item, :type => :model do
  describe 'completion_date' do
    it 'ステータスが4で対応完了日がnilの時' do
      item = build(:item, status: 4, completion_date: nil)
      expect(item.valid?).to be_truthy
    end

    it 'ステータスが1で対応完了日がnilの時' do
      item = build(:item, status: 1, completion_date: nil)
      expect(item.valid?).to be_falsey
    end

    it 'ステータスが1で対応完了日が2015/07/29の時' do
      item = build(:item, status: 1, completion_date: 2015/07/29)
      expect(item.valid?).to be_truthy
    end
  end
end

他にも条件を指定する方法はいくつかある。続きはRailsGuidesで。

参考サイト