Module: Bugsage::ExceptionSupport

Defined in:
lib/bugsage/exception_support.rb

Constant Summary collapse

ENV_KEYS =
%w[
  action_dispatch.exception
  action_dispatch.original_exception
  action_dispatch.debug_exception
  rack.exception
].freeze

Class Method Summary collapse

Class Method Details

.env_values(env) ⇒ Object



19
20
21
22
23
# File 'lib/bugsage/exception_support.rb', line 19

def env_values(env)
  return [] unless env.is_a?(Hash)

  ENV_KEYS.filter_map { |key| env[key] }
end

.exception_ancestor_names(exception) ⇒ Object

Returns the class and its ancestor class names so that subclasses of a known exception (e.g. PG::UniqueViolation < PG::Error, or a wrapped ActiveRecord::StatementInvalid) still match their base rule.



81
82
83
84
85
# File 'lib/bugsage/exception_support.rb', line 81

def exception_ancestor_names(exception)
  exception.class.ancestors.filter_map do |ancestor|
    ancestor.name if ancestor.is_a?(Class)
  end
end

.exception_class_name(exception) ⇒ Object



74
75
76
# File 'lib/bugsage/exception_support.rb', line 74

def exception_class_name(exception)
  exception.class.name.to_s
end

.extract(env, exception = nil) ⇒ Object



14
15
16
17
# File 'lib/bugsage/exception_support.rb', line 14

def extract(env, exception = nil)
  candidate = exception || env_values(env).find { |value| value } || request_exception(env)
  unwrap(candidate)
end

.matches_any_class?(exception, *class_names) ⇒ Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/bugsage/exception_support.rb', line 66

def matches_any_class?(exception, *class_names)
  exception_ancestor_names(exception).intersect?(class_names)
end

.matches_class?(exception, class_name) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/bugsage/exception_support.rb', line 62

def matches_class?(exception, class_name)
  exception_ancestor_names(exception).include?(class_name)
end

.message_matches?(exception, pattern) ⇒ Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/bugsage/exception_support.rb', line 70

def message_matches?(exception, pattern)
  exception.message.to_s.match?(pattern)
end

.next_exception_candidate(object) ⇒ Object



38
39
40
41
42
43
44
45
46
47
# File 'lib/bugsage/exception_support.rb', line 38

def next_exception_candidate(object)
  return object.unwrapped_exception if object.respond_to?(:unwrapped_exception)

  if object.respond_to?(:exception)
    candidate = object.exception
    return candidate unless candidate.equal?(object)
  end

  nil
end

.record_not_found?(exception) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/bugsage/exception_support.rb', line 58

def record_not_found?(exception)
  matches_class?(exception, "ActiveRecord::RecordNotFound")
end

.request_exception(env) ⇒ Object



87
88
89
90
91
92
93
# File 'lib/bugsage/exception_support.rb', line 87

def request_exception(env)
  request = env["action_dispatch.request"]
  return unless request
  return request.exception if request.respond_to?(:exception) && request.exception

  nil
end

.routing_error?(exception) ⇒ Boolean

Returns:

  • (Boolean)


53
54
55
56
# File 'lib/bugsage/exception_support.rb', line 53

def routing_error?(exception)
  matches_class?(exception, "ActionController::RoutingError") ||
    message_matches?(exception, /No route matches/i)
end

.unwrap(object) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/bugsage/exception_support.rb', line 25

def unwrap(object)
  current = object

  5.times do
    break if current.nil?
    return current if current.is_a?(Exception)

    current = next_exception_candidate(current)
  end

  current.is_a?(Exception) ? current : nil
end

.wrapper?(object) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/bugsage/exception_support.rb', line 49

def wrapper?(object)
  object.class.name&.end_with?("ExceptionWrapper")
end