1

I have a Rails 6 application.

class PostsController
  rescue_from MyException, :handle_my_exception
  around_action :my_around_action

  def create
    Rails.logger.info "Executing create code"
    raise MyException.new("Error Message!")
  end

  def my_around_action
    Rails.logger.info "Executing my_around_action"
    yield
  ensure
    Rails.logger.info "Inside ensure block of my_around_action"
  end

  def handle_my_exception(e)
    Rails.logger.info "Inside Handle My Exception"
  end
end

After calling create action I get output like this in below order

Executing my_around_action
Executing create code
Inside ensure block of my_around_action
Inside Handle My Exception

But I want output in this order.

Executing my_around_action
Executing create code
Inside Handle My Exception
Inside ensure block of my_around_action

What should I do? I must have to use rescue_from for dry purpose. Below is what I tried to overcome the problem, but I am looking to solve it using rescue_from.

class PostsController
  # rescue_from MyException, :handle_my_exception
  around_action :my_around_action

  def create
    Rails.logger.info "Executing create code"
    raise MyException.new("Error Message!")
  rescue MyException => e
    handle_my_exception(e)
  end

  def my_around_action
    Rails.logger.info "Executing my_around_action"
    yield
  ensure
    Rails.logger.info "Inside ensure block of my_around_action"
  end

  def handle_my_exception(e)
    Rails.logger.info "Inside Handle My Exception"
  end
end

When I manually handled exception then it outputs in correct order, but do not output in proper order when I try to use rescue_from.

Omkar
  • 112
  • 9

3 Answers3

2

Just put the rescue in your around handler:

def my_around_action
  Rails.logger.info "Executing my_around_action"
  yield
rescue MyException => e
  handle_my_exception e
ensure
  Rails.logger.info "Inside ensure block of my_around_action"
end
smathy
  • 26,283
  • 5
  • 48
  • 68
  • 1
    Able to solve with this and conceptually this is correct. rescue_from is supposed to work after all actions are executed. So required to handle in around action. – Omkar Jul 13 '23 at 14:46
1

Your solution is exactly what you requested for when you stated: "But I want output in this order.". Be sure, it is a good solution, even better than with rescue_from.

1

What about taking the code from the ensure block and wrapping it into lambda? MyException could be changed a bit so that you can store the lambda access it later in the handle_my_exception method using $!

class MyException < Exception
  def initialize(message, ensure_lambda = nil)
    @ensure_lambda = ensure_lambda
    super(message)
  end

  attr_accessor :ensure_lambda
end
class PostsController < ApplicationController
  rescue_from MyException, with: :handle_my_exception
  around_action :my_around_action

  def create
    Rails.logger.info "Executing create code"
    raise MyException.new("Error Message!")
  end

  def my_around_action
    Rails.logger.info "Executing my_around_action"
    yield
  ensure
    $!.ensure_lambda = -> { Rails.logger.info "Inside ensure block of my_around_action" }
  end

  def handle_my_exception(e)
    Rails.logger.info "Inside Handle My Exception"
    e.ensure_lambda.call
  end
end
  • thanks for the suggestion. I was not aware about `$!`. Can you please share guide for this? – Omkar Jul 13 '23 at 14:48
  • It's the global variable that points to the latest exception. Accessible in the rescue/ensure block https://stackoverflow.com/a/27854841/22167274 https://ruby-doc.org/3.2.2/globals_rdoc.html – Jakub Górecki Jul 17 '23 at 11:01