バリデーション(5)

バリデーションの仕上げにトランザクションの書き方メモ。

Rails 2.0のActiveRecordでは、トランザクションブロックにブロック引数が渡されないようだ。*1

古いヘルプによると以前は

# 以下のようにバリデーションがある場合
# Class Sample < ActiveRecord::Base
#   validates_presence_of :name
# end

transaction do |t|
  s1 = Sample.create(:name => 'n1', :value => 'v1')
  s2 = Sample.create(:value => 'v2') # error!
  if s1.errors.count + s2.errors.count > 0
    t.rollback
  end
end

こんな感じに書けたらしいが、現状だとこの書き方はできない*2。どうするかというと

transaction do
  s1 = Sample.create!(:name => 'n1', :value => 'v1')
  s2 = Sample.create!(:value => 'v2') # error!
end rescue # rollback!!

こんなふうに、create!やsave!を使って例外を投げ、トランザクションの外側で受けるとよい。ちなみにトランザクションの内側で例外を受けてしまうとロールバックされない。

バリデーションメッセージを使いたい場合は、オブジェクトへの参照を設定してから例外を投げるとよい。

transaction do
  s1 = (@sample = Sample.new(:name => 'n1', :value => 'v1').save!
  s2 = (@sample = Sample.new(:value => 'v2')).save! # error!
end rescue

こうしておくとビュー側の <%= error_messages_for :sample %> でバリデーションメッセージが拾える。

*1:環境はrails 2.0.2 + Sqlite 3.4.0

*2:rollback_db_transactionというメソッドがあるようだが、呼び方がわからなかった