LoginSignup
9
12

More than 3 years have passed since last update.

[Rails] Transaction内で条件分岐してrescue

Last updated at Posted at 2020-01-07

こちらの記事をもし見られた方への注意事項

もしこちらの記事を見られた方はこの記事のコードをコピペ等はしないでください。
理由はコメントと、そちらにありますyoutubeでの解説動画を御覧ください。
伊藤さん(@jnchito)にぶった切られています!笑
自分と同じように安易にrescueしに来た方は悪い例を見てから、そちらを御覧ください!笑

そもそもTransactionなぜ必要なのか

複数にまたがるモデルのデータ同時に保存したいときや、find_eachなどで複数回データをいじって保存したいときに、Transactionを利用します。

users_conroller.rb
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が発生します。

users_controller.rb
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です!!

users_controller.rb
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をしないように書かないと余計に処理を走らせてしまいます。

users_controller.rb
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を切り分けてください。

users_controller.rb
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

こんな感じで切り分けて例外処理をしてやると大丈夫かなーと思います。

まとめ

これはあくまでも今回自分が実装したいものを実現するために書いたコードなので、おかしい部分はあるかと思います。
ほかにもっとキレイなコードがあれば是非教えてほしいです。

9
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12