15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby on RailsAdvent Calendar 2018

Day 18

[Rails] IF文で囲まれたtransactionをrescue

Last updated at Posted at 2018-11-02

業務ロジックを実装するにはTransactionは必要不可欠である。**Railsドキュメント**によるとRailsのTransactionは下記の構文がある。

  User.transaction do
    # Something todo
  end
  render :text => 'success'
  rescue => e
  render :text => e.message
ActiveRecord::Base.transaction do
  # Something todo
end
  render :text => 'success'
rescue => e
  render :text => e.message

しかし、もし上記構文をif...endで囲むと、まさかのsyntax errorが発生します

現象

  • 成功する構文
def foo
  User.transaction do
    # Something todo
  end
  p "++AAAAAAAAAAA"
  p "Succeed"
  p "--AAAAAAAAAAA"
  render nothing: true
  rescue => e
  p "++BBBBBBBBBBB"
  p "Failed"
  p e
  p "--BBBBBBBBBBB"
  render nothing: true
end

結果:

...
   (0.1ms)  BEGIN
   (0.1ms)  COMMIT
"++AAAAAAAAAAA"
"Succeed"
"--AAAAAAAAAAA"
  Rendered text template (0.1ms)
...
  • 失敗する構文
def foo
  if true
    User.transaction do
      # Something todo
    end
    p "++AAAAAAAAAAA"
    p "Succeed"
    p "--AAAAAAAAAAA"
    render nothing: true
    rescue => e
    p "++BBBBBBBBBBB"
    p "Failed"
    p e
    p "--BBBBBBBBBBB"
    render nothing: true
  end
end

結果:

...
SyntaxError (.../foo/app/controllers/foo_controller.rb:96: syntax error, unexpected keyword_rescue, expecting keyword_end
      rescue => e
            ^):
  app/controllers/foo_controller.rb:96: syntax error, unexpected keyword_rescue, expecting keyword_end
...

もちろん、ActiveRecord::Base.transactionの構文も同じエラーが発生した。ただし、rescue以降の構文を削除すると、正しく動く。

解決策

色々模索した結果、下記の構文にたどり着いた。

伝統的なbegin...rescue...end構文でエラーをキャッチ

def foo
  if true
    begin
      User.transaction do
        # Something todo
        # raise ActiveRecord::RecordInvalid.new(User.new)
      end
      p "++AAAAAAAAAAA"
      p "Succeed"
      p "--AAAAAAAAAAA"
      render nothing: true
    rescue => e
      p "++BBBBBBBBBBB"
      p "Failed"
      p e
      p "--BBBBBBBBBBB"
      render nothing: true
    end
  end
end

成功した場合:

...
   (0.2ms)  BEGIN
   (0.1ms)  COMMIT
"++AAAAAAAAAAA"
"Succeed"
"--AAAAAAAAAAA"
  Rendered text template (0.0ms)
...

失敗した場合:

...
   (0.1ms)  BEGIN
   (0.2ms)  ROLLBACK
"++BBBBBBBBBBB"
"Failed"
#<ActiveRecord::RecordInvalid: Failed!: >
"--BBBBBBBBBBB"
  Rendered text template (0.0ms)
...

begin...end構文でTransactionを囲む

def foo
  if true
    begin
      User.transaction do
        # Something todo
        # raise ActiveRecord::RecordInvalid.new(User.new)
      end
        p "++AAAAAAAAAAA"
        p "Succeed"
        p "--AAAAAAAAAAA"
        render nothing: true
      rescue => e
        p "++BBBBBBBBBBB"
        p "Failed"
        p e
        p "--BBBBBBBBBBB"
        render nothing: true
    end
  end
end

成功した場合:

...
   (0.2ms)  BEGIN
   (0.1ms)  COMMIT
"++AAAAAAAAAAA"
"Succeed"
"--AAAAAAAAAAA"
  Rendered text template (0.0ms)
...

失敗した場合:

...
   (0.1ms)  BEGIN
   (0.2ms)  ROLLBACK
"++BBBBBBBBBBB"
"Failed"
#<ActiveRecord::RecordInvalid: Failed!: >
"--BBBBBBBBBBB"
  Rendered text template (0.0ms)
...

UPDATE 2018-11-05

@scivola さんが指摘した通り、rescueキーワードはdef..endbegin...endの直下でなければならないそうだ。ゆえに下記構文もリーガル的である。

def foo
  if true
    User.transaction do
      # Something todo
      # raise ActiveRecord::RecordInvalid.new(User.new)
    end
      p "++AAAAAAAAAAA"
      p "Succeed"
      p "--AAAAAAAAAAA"
      render nothing: true
  end

  rescue => e
    p "++BBBBBBBBBBB"
    p "Failed"
    p e
    p "--BBBBBBBBBBB"
    render nothing: true
end

余談

余談ですが、Transactionの中でraise ActiveRecord::RollbackでROLLBACKを強制発生させる場合、Railsでは「成功」とみなし、rescueに通らない現象が確認できた。

参考

15
8
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
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?