Lanbo
Lanbo

Reputation: 15702

Rails 4: around_action with parameter

In order to drastically reduce code repetition, I want to write up a concern with a generic way to add a special around_action to a controller. It is basically supposed to catch any exception, render the right template and add the exception as a notice. However, it must be applicable to different actions, and show different templates depending on the action. My goal is basically to be able to do this:

protect_from_exception_with 'index', only: [ :update ]

In order to achieve this, I tried to write up my concern like this (Using Rails 4.1):

module CatchException
  extend ActiveSupport::Concern

  module ClassMethods
    def protect_from_exception_with(failure_template, params)
      around_action -> { catch_exception_with(failure_template) }, params
    end
  end

  private

  def log_error(e)
    # Many things happen here
  end

  def catch_exception_with(failure_template)
    yield
  rescue => e
    log_error(e)
    render failure_template
  end
end

However, this leads to an error:

LocalJumpError: no block given (yield)

I have trying to find examples for around_action or around_filter with a parameter, but could only find them for before_action.

I hope what I'm trying to achieve is at all possible, otherwise I'd need to write a new method in every controller for every action I need to achieve this.

Upvotes: 1

Views: 1878

Answers (1)

Hieu Pham
Hieu Pham

Reputation: 6707

There are some clues:

  1. around_action receives a callback and a block as params, if we send a function as the 1st param, that function mustn't have any parameter!
  2. We can send a block instead (like you did) but we must pass the current given block to that block as well, your code misses the passing block, that is why the exception raised.
  3. In protect_from_exception_with, I can call block_given?, it returns true, but I don't know how to get the block out!

This works:

module CatchException
  extend ActiveSupport::Concern

  module ClassMethods
    def protect_from_exception_with(failure_template, params)
      around_action -> { catch_exception_with(failure_template) }, params
    end
  end

  private

  def log_error(e)
    # Many things happen here
  end

  def catch_exception_with(failure_template)
    self.send(params[:action])
  rescue => e
    log_error(e)
    render failure_template
  end
end

Thankfully, we still have the params in catch_exception_with, make it easy, call the action back to the controller!

Upvotes: 3

Related Questions