Search code examples
ruby-on-railsrubyrescue

notification all errors and keep different rescues


I'm trying to send all rails errors as a notification, without disturbing other rescues.

ApplicationController:

class ApplicationController < ActionController::Base
around_filter :notify_errors

  def notify_errors
    begin
      yield
    rescue => e
      Notification.send_to(:admin, e)
    end
  end
end

SomeController function:

  def send_date
    date = Date.strptime('10/100/2013', '%m/%d/%Y')
    render json: {success: true, date: date}
  rescue ArgumentError
    render json: {success: false, msg: 'Bad date'}
  end

I get the "Bad date" json but not the Notification.send_to(:admin, e).


Solution

  • Is there a way to make it easier for each reraise error? A global solution or a function?

    You could monkeypatch raise.

    module RaiseNotify
      def raise(msg_or_exc, msg=msg_or_exc, trace=caller)
        Notification.send_to(:admin, msg_or_exc) if msg_or_exc.kind_of? StandardError
        fail msg_or_exc, msg=msg_or_exc, trace
      end
    end
    
    module Kernel
      include RaiseNotify
    end
    

    I haven't tested this, it would probably have impact beyond Rails, and I think it's a bad idea! Personally, I'd just call the notification code inside the initial rescue clause.

    def send_date
      date = Date.strptime('10/100/2013', '%m/%d/%Y')
      render json: {success: true, date: date}
    rescue ArgumentError => e
      Notification.send_to(:admin, e)
      render json: {success: false, msg: 'Bad date'}
    end
    

    This may be shortened with a method:

    def rescue_with_notify error_type=ArgumentError
      *yield 
    rescue error_type => e      
      Notification.send_to(:admin, e)
      [nil,false]
    end   
    

    The idea would be to wrap what you wish to check, and respond with an array, the end of which would be the "success" flag.

    def send_date date_string
      date,success = rescue_with_notify do
        Date.strptime(date_string, '%m/%d/%Y') 
      end
      success = true if success.nil?
      date ||= "Bad date"
      render json: {success: success, date: date} 
    end  
    

    But this is adding complexity and maybe extra lines for very little in return. I'd stick with pasting the notification code into rescue clauses as and when it's required.