7

I have a module in which I am performing all of my encryption/decryption tasks for a project. I would like to catch any OpenSSL::Cipher::CipherError exceptions that occur in this module so that I can handle them.

Is it possible to do something like

rescue_from OpenSSL::Cipher::CipherError, :with => :cipher_error

inside of a module?

Matt
  • 13,948
  • 6
  • 44
  • 68
  • 1
    are you including that module inside a controller or not? `rescue_from` is meant to be used inside controllers only, so doing something like this inside plain old ruby objects would include some very dirty hacks. – shime May 15 '13 at 16:24
  • Happy to help. Check here if you want to know what I meant by dirty hacks: http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-rescuable-and-rescue_from/ - I don't like how things look when you do it in Ruby and it seems like adding unnecessary clutter. Extracting exception handling in its own method is a way to improve confidence of your code, which seems to fit the most in your scenario. Read more about it here: http://avdi.org/talks/confident-code-railsconf-2011/ – shime May 15 '13 at 16:44
  • @shime If you'd care to post your comment as an answer I can accept and close this question. Thanks! – Matt Jun 09 '13 at 13:46

2 Answers2

8

I've investigated a little and came with a solution. You said you have a module in which you do your encryption. I'm guessing that module represents a singleton. My solution, however, requires you have an instance instead.

class Crypto
   def self.instance
      @__instance__ ||= new
   end
end

Extract encryption behavior in a module.

module Encryptable
   def encrypt
      # ...
   end

   def decrypt
      # ...
   end
end

Create a new module that handles exceptions.

module ExceptionHandler
  extend ActiveSupport::Concern

  included do
    include ActiveSupport::Rescuable
    rescue_from StandardError, :with => :known_error
  end

  def handle_known_exceptions
     yield
  rescue => ex
     rescue_with_handler(ex) || raise
  end

  def known_error(ex)
    Rails.logger.error "[ExceptionHandler] Exception #{ex.class}: #{ex.message}"
  end
end

So now you can use the newly defined handle_known_exceptions inside your Crypto. This is not very convenient because you haven't gained much. You still have to call the exception handler inside every method:

class Crypto
  include ExceptionHandler

  def print_bunnies
    handle_known_exceptions do
      File.open("bunnies")
    end
  end
end

No need to do this if we define a delegator that does that for us:

class CryptoDelegator
  include ExceptionHandler

  def initialize(target)
    @target = target
  end

  def method_missing(*args, &block)
    handle_known_exceptions do
      @target.send(*args, &block)
    end
  end
end

Completely override the initialization of Crypto, to use the delegator instead.

class Crypto
  include Encryptable

  def self.new(*args, &block)
    CryptoDelegator.new(super)
  end

  def self.instance
      @__instance__ ||= new
  end
end

And that's it!

user456584
  • 86,427
  • 15
  • 75
  • 107
shime
  • 8,746
  • 1
  • 30
  • 51
  • 1
    Holy smokes, that's pretty awesome - thanks so much! I'll give this a go!! – Matt Jun 14 '13 at 10:32
  • Haha, this was bothering me because I've asked myself your question a couple of times. Playing with this was so much fun for me, so I've decided to make a gem that enables `rescue_from` outside Rails. https://github.com/shime/rescue_from_ruby – shime Jun 15 '13 at 13:11
0

I do not understand clearly your use case. But I am writing this answer, so that, someone else can find it helpful.

Scenario: I want to show only Stripe errors, but want to hide other error.

Solution:

begin
  Stripe::PaymentMethod.detach(payment_method_in_db.stripe_pmid)
rescue => e
  if e.class.name[0..7] == "Stripe::"
    flash[:error] = e.message
  else
    flash[:error] = 'Something went wrong. Please Try again.'
  end
  redirect_to billing_url
  return
end

When e is Stripe::InvalidRequestError or Stripe::CardError, I show original error message from Stripe.

But, when e is system error (Like StandardError), I hide this error from user, because, it can reveal system vulnerability.

mahfuz
  • 2,728
  • 20
  • 18