class RescueRegistry::Registry

Attributes

owner[RW]

Public Class Methods

new(owner) click to toggle source
# File lib/rescue_registry/registry.rb, line 5
def initialize(owner)
  @owner = owner
  @handlers = { }
end

Public Instance Methods

build_response(content_type, exception, **options) click to toggle source
# File lib/rescue_registry/registry.rb, line 71
def build_response(content_type, exception, **options)
  handler = handler_for_exception(exception)
  handler.formatted_response(content_type, **options)
end
handler_for_exception(exception) click to toggle source
# File lib/rescue_registry/registry.rb, line 43
def handler_for_exception(exception)
  handler_info = handler_info_for_exception(exception)
  raise HandlerNotFound, "no handler found for #{exception.class}" unless handler_info

  handler_class, handler_options = handler_info

  if handler_options[:status] == :passthrough
    handler_options = handler_options.merge(status: passthrough_status(exception))
  end

  handler_class.new(exception, **handler_options)
end
handles_exception?(exception) click to toggle source
# File lib/rescue_registry/registry.rb, line 56
def handles_exception?(exception)
  !handler_info_for_exception(exception).nil?
end
initialize_dup(_other) click to toggle source
# File lib/rescue_registry/registry.rb, line 10
def initialize_dup(_other)
  @handlers = @handlers.dup
end
passthrough_allowed?() click to toggle source
# File lib/rescue_registry/registry.rb, line 14
def passthrough_allowed?
  defined?(ActionDispatch::ExceptionWrapper)
end
passthrough_status(exception) click to toggle source
# File lib/rescue_registry/registry.rb, line 18
def passthrough_status(exception)
  ::ActionDispatch::ExceptionWrapper.status_code_for_exception(exception.class.name)
end
register_exception(exception_class, handler: nil, **options) click to toggle source

TODO: Support a shorthand for handler

# File lib/rescue_registry/registry.rb, line 23
def register_exception(exception_class, handler: nil, **options)
  raise ArgumentError, "#{exception_class} is not an Exception" unless exception_class <= Exception

  if owner.respond_to?(:default_exception_handler)
    handler ||= owner.default_exception_handler
  end
  raise ArgumentError, "handler must be provided" unless handler

  status = options[:status] ||= handler.default_status
  raise ArgumentError, "status must be provided" unless status
  unless status.is_a?(Integer) || (passthrough_allowed? && status == :passthrough)
    raise ArgumentError, "invalid status: #{status}"
  end

  # TODO: Validate options here

  # We assign the status here as a default when looking up by class (and not instance)
  @handlers[exception_class] = [handler, options]
end
response_for_debugging(content_type, exception, traces: nil, fallback: :none) click to toggle source
# File lib/rescue_registry/registry.rb, line 76
def response_for_debugging(content_type, exception, traces: nil, fallback: :none)
  build_response(content_type, exception, show_details: true, traces: traces, fallback: fallback)
end
response_for_public(content_type, exception, fallback: :none) click to toggle source
# File lib/rescue_registry/registry.rb, line 80
def response_for_public(content_type, exception, fallback: :none)
  build_response(content_type, exception, fallback: fallback)
end
status_code_for_exception(exception, passthrough: true) click to toggle source
# File lib/rescue_registry/registry.rb, line 60
def status_code_for_exception(exception, passthrough: true)
  _, options = handler_info_for_exception(exception)
  return unless options

  if options[:status] == :passthrough
    passthrough ? passthrough_status(exception) : nil
  else
    options[:status]
  end
end

Private Instance Methods

handler_info_for_exception(exception) click to toggle source
# File lib/rescue_registry/registry.rb, line 86
def handler_info_for_exception(exception)
  exception_class =
    case exception
    when String
      exception.safe_constantize
    when Class
      exception
    else
      exception.class
    end

  return unless exception_class

  raise ArgumentError, "#{exception_class} is not an Exception" unless exception_class <= Exception

  # Reverse so most recently defined takes precedence
  registry = @handlers.to_a.reverse

  # Look for an exact class, then for the superclass and so on.
  # There might be a more efficient way to do this, but this is pretty readable
  match_class = exception_class
  loop do
    if (found = registry.find { |(klass, _)| klass == match_class })
      return found.last
    elsif match_class == Exception
      # We've exhausted our options
      return nil
    end

    match_class = match_class.superclass
  end
end