結構調べて対応したので、メモしておきます。
##transactionとは
ソフトウェアの処理方式の一つで、互いに関連・依存する複数の処理をまとめ、ひとつの処理単位として扱うこと。つまり複数の処理をひとつの処理として考えるということで処理A、処理Bをひとつの処理Cとして扱う。処理Aが終了し処理Bを実行しているときにエラーが起こると処理Cがなかったことになる(この場合処理Aがなかったことになるということ)。
##delete もしくは destroyを使用したとき
コントローラの中で要素を削除するという処理を加え、保存するときにこのtransactionを使用してみた。で例外処理としてその要素にnilを入れたかったので以下のようなコードにした。
if client_attributes[:client_hoge_attributes]
@client_temp.client_hoge.each { |obj| obj.delete }
client_attributes[:client_hoge_attributes].each do |key, val|
if client_attributes[:client_hoge_attributes][key]
client_attributes[:client_hoge_attributes][key].delete("id")
end
end
end
@client_temp.attributes = client_attributes
@client_temp.delete_blank
begin
ClientTemp.transaction do
@client_temp.save! validate: @client_temp.applied?
@client_temp.client.update_photo_used
end
if @client_temp.temp?
flash[:notice] = I18n.t "clients.update.temp"
else
flash[:notice] = I18n.t "clients.update.offer"
end
redirect_to client_top_path and return
rescue
@errors = @client_temp.errors
@client_temp.client_hoge.each{|obj| obj.client_temp_id = nil }
@client_temp.fill_blank
render action: :edit
end
特定の条件下でRuntimeError Can't modify frozen hashというエラー発生!
こいつがやっかいだった。
##RuntimeError Can't modify frozen hash
これの意味は「オブジェクトの中身を変更できなくなっている」のに変更をしようとしているので出るエラー。
transactionのブロック内で破壊的メソッドのsave!をしている。ここで例外処理に移ったときdeleteで削除されているidも絡んで起こるエラーっぽいです。
いまいち腑に落ちていないのですがまあ対応してみた。
##方法
freezeを解除する方法があれば解決するのでは?とあたりをつけて調べてみた。
dup を使えばほぼ同じ内容の凍結されていないオブジェクトを得ることはできます。とかこういうのを参考にしました。ActiveRecord オブジェクトをコピーする
ってことで
rescue
@errors = @client_temp.errors
@client_temp = @client_temp.dup
@client_temp.client_hoge.each{|obj| obj.client_temp_id = nil }
@client_temp.fill_blank
render action: :edit
end
みたいに対応してみたら、、、成功!!
エラーの意味の詳細がわかり次第、更新します。