こちらの記事をもし見られた方への注意事項
もしこちらの記事を見られた方はこの記事のコードをコピペ等はしないでください。
理由はコメントと、そちらにありますyoutubeでの解説動画を御覧ください。
伊藤さん(@jnchito)にぶった切られています!笑
自分と同じように安易にrescueしに来た方は悪い例を見てから、そちらを御覧ください!笑
そもそもTransactionなぜ必要なのか
複数にまたがるモデルのデータ同時に保存したいときや、find_each
などで複数回データをいじって保存したいときに、Transaction
を利用します。
def create
User.transaction do
@user.save!
respond_to do |format|
format.js { render : 'action名' }
end
rescue => e
logger.debug("failed. because of #{e}")
redirect_to ~~~path
end
end
上記のようなコードは問題なく動作します。
しかし下記の様にもしcontroller内に渡ってくるparamsによってif..end
で囲むと、syntax error
が発生します。
def create
if user_params[:user_id].present?
User.transaction do
@user.save!
respond_to do |format|
format.js { render : 'action' }
end
rescue => e
logger.debug("failed. because of #{e}")
redirect_to ~~~path
end
else
# 処理
end
end
結果
SyntaxError (.../hoge/app/controllers/user_controller.rb:96: syntax error, unexpected keyword_rescue, expecting keyword_end
rescue => e
^):
app/controllers/user_controller.rb:96: syntax error, unexpected keyword_rescue, expecting keyword_end
- ただし、rescueを削除するとうまく動作します。
解決策
いろんな記事でよく目にすると思いますが、begin..rescue..end
で書くとOKです!!
def create
if user_params[:user_id].present?
begin
User.transaction do
@user.save!
respond_to do |format|
format.js { render : 'action' }
end
rescue => e
logger.debug("failed. because of #{e}")
redirect_to ~~~path
end
else
# 処理
end
end
みたいな感じで条件分岐すると、うまく動きます。
注意点
最初にfind_each
をcontroller内で使用する場合ですが、何度もredirect
をしないように書かないと余計に処理を走らせてしまいます。
def create
User.transaction do
@users.find_each do |user|
if user.save
respond_to do |format|
format.js { render : 'action' }
end
else
# コード
end
end
end
end
だったら駄目なのでfind_each
の中からは、respond_to
を切り分けてください。
def create
User.transaction do
@users.find_each do |user|
user.save!
end
respond_to do |format|
format.js { render : 'action' }
end
rescue => e
logger.debug("failed. because of #{e}")
redirect_to ~~~path
end
end
こんな感じで切り分けて例外処理をしてやると大丈夫かなーと思います。
まとめ
これはあくまでも今回自分が実装したいものを実現するために書いたコードなので、おかしい部分はあるかと思います。
ほかにもっとキレイなコードがあれば是非教えてほしいです。