exception_notification 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +2 -2
  3. data/CHANGELOG.rdoc +14 -0
  4. data/CONTRIBUTING.md +18 -0
  5. data/Gemfile +1 -1
  6. data/README.md +64 -935
  7. data/Rakefile +2 -2
  8. data/docs/notifiers/campfire.md +50 -0
  9. data/docs/notifiers/custom.md +42 -0
  10. data/docs/notifiers/datadog.md +51 -0
  11. data/docs/notifiers/email.md +195 -0
  12. data/docs/notifiers/google_chat.md +31 -0
  13. data/docs/notifiers/hipchat.md +66 -0
  14. data/docs/notifiers/irc.md +97 -0
  15. data/docs/notifiers/mattermost.md +115 -0
  16. data/docs/notifiers/slack.md +161 -0
  17. data/docs/notifiers/sns.md +37 -0
  18. data/docs/notifiers/teams.md +54 -0
  19. data/docs/notifiers/webhook.md +60 -0
  20. data/examples/sample_app.rb +54 -0
  21. data/examples/sinatra/Gemfile +6 -6
  22. data/examples/sinatra/config.ru +1 -1
  23. data/examples/sinatra/sinatra_app.rb +14 -10
  24. data/exception_notification.gemspec +27 -22
  25. data/gemfiles/rails4_0.gemfile +3 -3
  26. data/gemfiles/rails4_1.gemfile +3 -3
  27. data/gemfiles/rails4_2.gemfile +3 -3
  28. data/gemfiles/rails5_0.gemfile +3 -3
  29. data/gemfiles/rails5_1.gemfile +3 -3
  30. data/gemfiles/rails5_2.gemfile +7 -0
  31. data/gemfiles/rails6_0.gemfile +7 -0
  32. data/lib/exception_notification.rb +1 -0
  33. data/lib/exception_notification/rack.rb +8 -21
  34. data/lib/exception_notification/resque.rb +8 -10
  35. data/lib/exception_notification/sidekiq.rb +8 -12
  36. data/lib/exception_notification/version.rb +3 -0
  37. data/lib/exception_notifier.rb +20 -3
  38. data/lib/exception_notifier/base_notifier.rb +2 -3
  39. data/lib/exception_notifier/campfire_notifier.rb +12 -13
  40. data/lib/exception_notifier/datadog_notifier.rb +153 -0
  41. data/lib/exception_notifier/email_notifier.rb +64 -87
  42. data/lib/exception_notifier/google_chat_notifier.rb +25 -119
  43. data/lib/exception_notifier/hipchat_notifier.rb +11 -12
  44. data/lib/exception_notifier/irc_notifier.rb +32 -30
  45. data/lib/exception_notifier/mattermost_notifier.rb +47 -140
  46. data/lib/exception_notifier/modules/backtrace_cleaner.rb +0 -2
  47. data/lib/exception_notifier/modules/error_grouping.rb +5 -5
  48. data/lib/exception_notifier/modules/formatter.rb +118 -0
  49. data/lib/exception_notifier/notifier.rb +5 -6
  50. data/lib/exception_notifier/slack_notifier.rb +63 -40
  51. data/lib/exception_notifier/sns_notifier.rb +17 -11
  52. data/lib/exception_notifier/teams_notifier.rb +58 -44
  53. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +1 -1
  54. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +1 -1
  55. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +1 -1
  56. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +2 -2
  57. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +2 -2
  58. data/lib/exception_notifier/webhook_notifier.rb +14 -11
  59. data/lib/generators/exception_notification/install_generator.rb +5 -5
  60. data/lib/generators/exception_notification/templates/{exception_notification.rb → exception_notification.rb.erb} +13 -11
  61. data/test/exception_notification/rack_test.rb +27 -11
  62. data/test/exception_notification/resque_test.rb +52 -0
  63. data/test/exception_notifier/campfire_notifier_test.rb +42 -42
  64. data/test/exception_notifier/datadog_notifier_test.rb +151 -0
  65. data/test/exception_notifier/email_notifier_test.rb +269 -153
  66. data/test/exception_notifier/google_chat_notifier_test.rb +154 -101
  67. data/test/exception_notifier/hipchat_notifier_test.rb +78 -81
  68. data/test/exception_notifier/irc_notifier_test.rb +34 -34
  69. data/test/exception_notifier/mattermost_notifier_test.rb +164 -67
  70. data/test/exception_notifier/modules/error_grouping_test.rb +39 -40
  71. data/test/exception_notifier/modules/formatter_test.rb +150 -0
  72. data/test/exception_notifier/sidekiq_test.rb +6 -6
  73. data/test/exception_notifier/slack_notifier_test.rb +61 -60
  74. data/test/exception_notifier/sns_notifier_test.rb +27 -32
  75. data/test/exception_notifier/teams_notifier_test.rb +23 -26
  76. data/test/exception_notifier/webhook_notifier_test.rb +48 -46
  77. data/test/exception_notifier_test.rb +41 -38
  78. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.html.erb +0 -0
  79. data/test/{dummy/app → support}/views/exception_notifier/_new_bkg_section.text.erb +0 -0
  80. data/test/{dummy/app → support}/views/exception_notifier/_new_section.html.erb +0 -0
  81. data/test/{dummy/app → support}/views/exception_notifier/_new_section.text.erb +0 -0
  82. data/test/test_helper.rb +11 -14
  83. metadata +136 -166
  84. data/test/dummy/.gitignore +0 -4
  85. data/test/dummy/Rakefile +0 -7
  86. data/test/dummy/app/controllers/application_controller.rb +0 -3
  87. data/test/dummy/app/controllers/posts_controller.rb +0 -30
  88. data/test/dummy/app/helpers/application_helper.rb +0 -2
  89. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  90. data/test/dummy/app/models/post.rb +0 -2
  91. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  92. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  93. data/test/dummy/app/views/posts/new.html.erb +0 -0
  94. data/test/dummy/app/views/posts/show.html.erb +0 -0
  95. data/test/dummy/config.ru +0 -4
  96. data/test/dummy/config/application.rb +0 -42
  97. data/test/dummy/config/boot.rb +0 -6
  98. data/test/dummy/config/database.yml +0 -22
  99. data/test/dummy/config/environment.rb +0 -17
  100. data/test/dummy/config/environments/development.rb +0 -25
  101. data/test/dummy/config/environments/production.rb +0 -50
  102. data/test/dummy/config/environments/test.rb +0 -35
  103. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  104. data/test/dummy/config/initializers/inflections.rb +0 -10
  105. data/test/dummy/config/initializers/mime_types.rb +0 -5
  106. data/test/dummy/config/initializers/secret_token.rb +0 -8
  107. data/test/dummy/config/initializers/session_store.rb +0 -8
  108. data/test/dummy/config/locales/en.yml +0 -5
  109. data/test/dummy/config/routes.rb +0 -3
  110. data/test/dummy/db/migrate/20110729022608_create_posts.rb +0 -15
  111. data/test/dummy/db/schema.rb +0 -24
  112. data/test/dummy/db/seeds.rb +0 -7
  113. data/test/dummy/lib/tasks/.gitkeep +0 -0
  114. data/test/dummy/public/404.html +0 -26
  115. data/test/dummy/public/422.html +0 -26
  116. data/test/dummy/public/500.html +0 -26
  117. data/test/dummy/public/favicon.ico +0 -0
  118. data/test/dummy/public/images/rails.png +0 -0
  119. data/test/dummy/public/index.html +0 -239
  120. data/test/dummy/public/javascripts/application.js +0 -2
  121. data/test/dummy/public/javascripts/controls.js +0 -965
  122. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  123. data/test/dummy/public/javascripts/effects.js +0 -1123
  124. data/test/dummy/public/javascripts/prototype.js +0 -6001
  125. data/test/dummy/public/javascripts/rails.js +0 -191
  126. data/test/dummy/public/robots.txt +0 -5
  127. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  128. data/test/dummy/public/stylesheets/scaffold.css +0 -56
  129. data/test/dummy/script/rails +0 -6
  130. data/test/dummy/test/functional/posts_controller_test.rb +0 -237
  131. data/test/dummy/test/test_helper.rb +0 -7
@@ -0,0 +1,3 @@
1
+ module ExceptionNotification
2
+ VERSION = '4.4.0'.freeze
3
+ end
@@ -8,6 +8,7 @@ module ExceptionNotifier
8
8
  include ErrorGrouping
9
9
 
10
10
  autoload :BacktraceCleaner, 'exception_notifier/modules/backtrace_cleaner'
11
+ autoload :Formatter, 'exception_notifier/modules/formatter'
11
12
 
12
13
  autoload :Notifier, 'exception_notifier/notifier'
13
14
  autoload :EmailNotifier, 'exception_notifier/email_notifier'
@@ -20,6 +21,7 @@ module ExceptionNotifier
20
21
  autoload :TeamsNotifier, 'exception_notifier/teams_notifier'
21
22
  autoload :SnsNotifier, 'exception_notifier/sns_notifier'
22
23
  autoload :GoogleChatNotifier, 'exception_notifier/google_chat_notifier'
24
+ autoload :DatadogNotifier, 'exception_notifier/datadog_notifier'
23
25
 
24
26
  class UndefinedNotifierError < StandardError; end
25
27
 
@@ -29,7 +31,7 @@ module ExceptionNotifier
29
31
 
30
32
  # Define a set of exceptions to be ignored, ie, dont send notifications when any of them are raised.
31
33
  mattr_accessor :ignored_exceptions
32
- @@ignored_exceptions = %w{ActiveRecord::RecordNotFound Mongoid::Errors::DocumentNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat ActionController::UrlGenerationError}
34
+ @@ignored_exceptions = %w[ActiveRecord::RecordNotFound Mongoid::Errors::DocumentNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat ActionController::UrlGenerationError]
33
35
 
34
36
  mattr_accessor :testing_mode
35
37
  @@testing_mode = false
@@ -45,9 +47,10 @@ module ExceptionNotifier
45
47
  self.testing_mode = true
46
48
  end
47
49
 
48
- def notify_exception(exception, options={}, &block)
50
+ def notify_exception(exception, options = {}, &block)
49
51
  return false if ignored_exception?(options[:ignore_exceptions], exception)
50
52
  return false if ignored?(exception, options)
53
+
51
54
  if error_grouping
52
55
  errors_count = group_error!(exception, options)
53
56
  return false unless send_notification?(exception, errors_count)
@@ -92,13 +95,20 @@ module ExceptionNotifier
92
95
  @@ignores << block
93
96
  end
94
97
 
98
+ def ignore_crawlers(crawlers)
99
+ ignore_if do |_exception, opts|
100
+ opts.key?(:env) && from_crawler(opts[:env], crawlers)
101
+ end
102
+ end
103
+
95
104
  def clear_ignore_conditions!
96
105
  @@ignores.clear
97
106
  end
98
107
 
99
108
  private
109
+
100
110
  def ignored?(exception, options)
101
- @@ignores.any?{ |condition| condition.call(exception, options) }
111
+ @@ignores.any? { |condition| condition.call(exception, options) }
102
112
  rescue Exception => e
103
113
  raise e if @@testing_mode
104
114
 
@@ -130,5 +140,12 @@ module ExceptionNotifier
130
140
  rescue NameError => e
131
141
  raise UndefinedNotifierError, "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
132
142
  end
143
+
144
+ def from_crawler(env, ignored_crawlers)
145
+ agent = env['HTTP_USER_AGENT']
146
+ Array(ignored_crawlers).any? do |crawler|
147
+ agent =~ Regexp.new(crawler)
148
+ end
149
+ end
133
150
  end
134
151
  end
@@ -2,11 +2,11 @@ module ExceptionNotifier
2
2
  class BaseNotifier
3
3
  attr_accessor :base_options
4
4
 
5
- def initialize(options={})
5
+ def initialize(options = {})
6
6
  @base_options = options
7
7
  end
8
8
 
9
- def send_notice(exception, options, message, message_opts=nil)
9
+ def send_notice(exception, options, message, message_opts = nil)
10
10
  _pre_callback(exception, options, message, message_opts)
11
11
  result = yield(message, message_opts)
12
12
  _post_callback(exception, options, message, message_opts)
@@ -20,6 +20,5 @@ module ExceptionNotifier
20
20
  def _post_callback(exception, options, message, message_opts)
21
21
  @base_options[:post_callback].call(options, self, exception.backtrace, message, message_opts) if @base_options[:post_callback].respond_to?(:call)
22
22
  end
23
-
24
23
  end
25
24
  end
@@ -1,6 +1,5 @@
1
1
  module ExceptionNotifier
2
2
  class CampfireNotifier < BaseNotifier
3
-
4
3
  attr_accessor :subdomain
5
4
  attr_accessor :token
6
5
  attr_accessor :room
@@ -12,22 +11,22 @@ module ExceptionNotifier
12
11
  room_name = options.delete(:room_name)
13
12
  @campfire = Tinder::Campfire.new subdomain, options
14
13
  @room = @campfire.find_room_by_name room_name
15
- rescue
14
+ rescue StandardError
16
15
  @campfire = @room = nil
17
16
  end
18
17
  end
19
18
 
20
- def call(exception, options={})
21
- if active?
22
- message = if options[:accumulated_errors_count].to_i > 1
23
- "The exception occurred #{options[:accumulated_errors_count]} times: '#{exception.message}'"
24
- else
25
- "A new exception occurred: '#{exception.message}'"
26
- end
27
- message += " on '#{exception.backtrace.first}'" if exception.backtrace
28
- send_notice(exception, options, message) do |msg, _|
29
- @room.paste msg
30
- end
19
+ def call(exception, options = {})
20
+ return unless active?
21
+
22
+ message = if options[:accumulated_errors_count].to_i > 1
23
+ "The exception occurred #{options[:accumulated_errors_count]} times: '#{exception.message}'"
24
+ else
25
+ "A new exception occurred: '#{exception.message}'"
26
+ end
27
+ message += " on '#{exception.backtrace.first}'" if exception.backtrace
28
+ send_notice(exception, options, message) do |msg, _|
29
+ @room.paste msg
31
30
  end
32
31
  end
33
32
 
@@ -0,0 +1,153 @@
1
+ require 'action_dispatch'
2
+
3
+ module ExceptionNotifier
4
+ class DatadogNotifier < BaseNotifier
5
+ attr_reader :client,
6
+ :default_options
7
+
8
+ def initialize(options)
9
+ super
10
+ @client = options.fetch(:client)
11
+ @default_options = options
12
+ end
13
+
14
+ def call(exception, options = {})
15
+ client.emit_event(
16
+ datadog_event(exception, options)
17
+ )
18
+ end
19
+
20
+ def datadog_event(exception, options = {})
21
+ DatadogExceptionEvent.new(
22
+ exception,
23
+ options.reverse_merge(default_options)
24
+ ).event
25
+ end
26
+
27
+ class DatadogExceptionEvent
28
+ include ExceptionNotifier::BacktraceCleaner
29
+
30
+ MAX_TITLE_LENGTH = 120
31
+ MAX_VALUE_LENGTH = 300
32
+ MAX_BACKTRACE_SIZE = 3
33
+ ALERT_TYPE = 'error'.freeze
34
+
35
+ attr_reader :exception,
36
+ :options
37
+
38
+ def initialize(exception, options)
39
+ @exception = exception
40
+ @options = options
41
+ end
42
+
43
+ def request
44
+ @request ||= ActionDispatch::Request.new(options[:env]) if options[:env]
45
+ end
46
+
47
+ def controller
48
+ @controller ||= options[:env] && options[:env]['action_controller.instance']
49
+ end
50
+
51
+ def backtrace
52
+ @backtrace ||= exception.backtrace ? clean_backtrace(exception) : []
53
+ end
54
+
55
+ def tags
56
+ options[:tags] || []
57
+ end
58
+
59
+ def title_prefix
60
+ options[:title_prefix] || ''
61
+ end
62
+
63
+ def event
64
+ title = formatted_title
65
+ body = formatted_body
66
+
67
+ Dogapi::Event.new(
68
+ body,
69
+ msg_title: title,
70
+ alert_type: ALERT_TYPE,
71
+ tags: tags,
72
+ aggregation_key: [title]
73
+ )
74
+ end
75
+
76
+ def formatted_title
77
+ title = ''
78
+ title << title_prefix
79
+ title << "#{controller.controller_name} #{controller.action_name}" if controller
80
+ title << " (#{exception.class})"
81
+ title << " #{exception.message.inspect}"
82
+
83
+ truncate(title, MAX_TITLE_LENGTH)
84
+ end
85
+
86
+ def formatted_body
87
+ text = []
88
+
89
+ text << '%%%'
90
+ text << formatted_request if request
91
+ text << formatted_session if request
92
+ text << formatted_backtrace
93
+ text << '%%%'
94
+
95
+ text.join("\n")
96
+ end
97
+
98
+ def formatted_key_value(key, value)
99
+ "**#{key}:** #{value}"
100
+ end
101
+
102
+ def formatted_request
103
+ text = []
104
+ text << '### **Request**'
105
+ text << formatted_key_value('URL', request.url)
106
+ text << formatted_key_value('HTTP Method', request.request_method)
107
+ text << formatted_key_value('IP Address', request.remote_ip)
108
+ text << formatted_key_value('Parameters', request.filtered_parameters.inspect)
109
+ text << formatted_key_value('Timestamp', Time.current)
110
+ text << formatted_key_value('Server', Socket.gethostname)
111
+ if defined?(Rails) && Rails.respond_to?(:root)
112
+ text << formatted_key_value('Rails root', Rails.root)
113
+ end
114
+ text << formatted_key_value('Process', $PROCESS_ID)
115
+ text << '___'
116
+ text.join("\n")
117
+ end
118
+
119
+ def formatted_session
120
+ text = []
121
+ text << '### **Session**'
122
+ text << formatted_key_value('Data', request.session.to_hash)
123
+ text << '___'
124
+ text.join("\n")
125
+ end
126
+
127
+ def formatted_backtrace
128
+ size = [backtrace.size, MAX_BACKTRACE_SIZE].min
129
+
130
+ text = []
131
+ text << '### **Backtrace**'
132
+ text << '````'
133
+ size.times { |i| text << backtrace[i] }
134
+ text << '````'
135
+ text << '___'
136
+ text.join("\n")
137
+ end
138
+
139
+ def truncate(string, max)
140
+ string.length > max ? "#{string[0...max]}..." : string
141
+ end
142
+
143
+ def inspect_object(object)
144
+ case object
145
+ when Hash, Array
146
+ truncate(object.inspect, MAX_VALUE_LENGTH)
147
+ else
148
+ object.to_s
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -1,4 +1,3 @@
1
- require "active_support/core_ext/hash/reverse_merge"
2
1
  require 'active_support/core_ext/time'
3
2
  require 'action_mailer'
4
3
  require 'action_dispatch'
@@ -6,47 +5,61 @@ require 'pp'
6
5
 
7
6
  module ExceptionNotifier
8
7
  class EmailNotifier < BaseNotifier
9
- attr_accessor(:sender_address, :exception_recipients,
10
- :pre_callback, :post_callback,
11
- :email_prefix, :email_format, :sections, :background_sections,
12
- :verbose_subject, :normalize_subject, :include_controller_and_action_names_in_subject,
13
- :delivery_method, :mailer_settings, :email_headers, :mailer_parent, :template_path, :deliver_with)
8
+ DEFAULT_OPTIONS = {
9
+ sender_address: %("Exception Notifier" <exception.notifier@example.com>),
10
+ exception_recipients: [],
11
+ email_prefix: '[ERROR] ',
12
+ email_format: :text,
13
+ sections: %w[request session environment backtrace],
14
+ background_sections: %w[backtrace data],
15
+ verbose_subject: true,
16
+ normalize_subject: false,
17
+ include_controller_and_action_names_in_subject: true,
18
+ delivery_method: nil,
19
+ mailer_settings: nil,
20
+ email_headers: {},
21
+ mailer_parent: 'ActionMailer::Base',
22
+ template_path: 'exception_notifier',
23
+ deliver_with: nil
24
+ }.freeze
14
25
 
15
26
  module Mailer
16
27
  class MissingController
17
- def method_missing(*args, &block)
18
- end
28
+ def method_missing(*args, &block); end
19
29
  end
20
30
 
21
31
  def self.extended(base)
22
32
  base.class_eval do
23
- self.send(:include, ExceptionNotifier::BacktraceCleaner)
33
+ send(:include, ExceptionNotifier::BacktraceCleaner)
24
34
 
25
35
  # Append application view path to the ExceptionNotifier lookup context.
26
- self.append_view_path "#{File.dirname(__FILE__)}/views"
36
+ append_view_path "#{File.dirname(__FILE__)}/views"
27
37
 
28
- def exception_notification(env, exception, options={}, default_options={})
38
+ def exception_notification(env, exception, options = {}, default_options = {})
29
39
  load_custom_views
30
40
 
31
41
  @env = env
32
42
  @exception = exception
33
- @options = options.reverse_merge(env['exception_notifier.options'] || {}).reverse_merge(default_options)
43
+
44
+ env_options = env['exception_notifier.options'] || {}
45
+ @options = default_options.merge(env_options).merge(options)
46
+
34
47
  @kontroller = env['action_controller.instance'] || MissingController.new
35
48
  @request = ActionDispatch::Request.new(env)
36
49
  @backtrace = exception.backtrace ? clean_backtrace(exception) : []
37
50
  @timestamp = Time.current
38
51
  @sections = @options[:sections]
39
52
  @data = (env['exception_notifier.exception_data'] || {}).merge(options[:data] || {})
40
- @sections = @sections + %w(data) unless @data.empty?
53
+ @sections += %w[data] unless @data.empty?
41
54
 
42
55
  compose_email
43
56
  end
44
57
 
45
- def background_exception_notification(exception, options={}, default_options={})
58
+ def background_exception_notification(exception, options = {}, default_options = {})
46
59
  load_custom_views
47
60
 
48
61
  @exception = exception
49
- @options = options.reverse_merge(default_options).symbolize_keys
62
+ @options = default_options.merge(options).symbolize_keys
50
63
  @backtrace = exception.backtrace || []
51
64
  @timestamp = Time.current
52
65
  @sections = @options[:background_sections]
@@ -59,13 +72,13 @@ module ExceptionNotifier
59
72
  private
60
73
 
61
74
  def compose_subject
62
- subject = "#{@options[:email_prefix]}"
75
+ subject = @options[:email_prefix].to_s.dup
63
76
  subject << "(#{@options[:accumulated_errors_count]} times)" if @options[:accumulated_errors_count].to_i > 1
64
77
  subject << "#{@kontroller.controller_name} #{@kontroller.action_name}" if @kontroller && @options[:include_controller_and_action_names_in_subject]
65
78
  subject << " (#{@exception.class})"
66
79
  subject << " #{@exception.message.inspect}" if @options[:verbose_subject]
67
80
  subject = EmailNotifier.normalize_digits(subject) if @options[:normalize_subject]
68
- subject.length > 120 ? subject[0...120] + "..." : subject
81
+ subject.length > 120 ? subject[0...120] + '...' : subject
69
82
  end
70
83
 
71
84
  def set_data_variables
@@ -82,17 +95,17 @@ module ExceptionNotifier
82
95
 
83
96
  def inspect_object(object)
84
97
  case object
85
- when Hash, Array
86
- truncate(object.inspect, 300)
87
- else
88
- object.to_s
98
+ when Hash, Array
99
+ truncate(object.inspect, 300)
100
+ else
101
+ object.to_s
89
102
  end
90
103
  end
91
104
 
92
105
  helper_method :safe_encode
93
106
 
94
107
  def safe_encode(value)
95
- value.encode("utf-8", invalid: :replace, undef: :replace, replace: "_")
108
+ value.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
96
109
  end
97
110
 
98
111
  def html_mail?
@@ -106,11 +119,11 @@ module ExceptionNotifier
106
119
  exception_recipients = maybe_call(@options[:exception_recipients])
107
120
 
108
121
  headers = {
109
- :delivery_method => @options[:delivery_method],
110
- :to => exception_recipients,
111
- :from => @options[:sender_address],
112
- :subject => subject,
113
- :template_name => name
122
+ delivery_method: @options[:delivery_method],
123
+ to: exception_recipients,
124
+ from: @options[:sender_address],
125
+ subject: subject,
126
+ template_name: name
114
127
  }.merge(@options[:email_headers])
115
128
 
116
129
  mail = mail(headers) do |format|
@@ -124,9 +137,7 @@ module ExceptionNotifier
124
137
  end
125
138
 
126
139
  def load_custom_views
127
- if defined?(Rails) && Rails.respond_to?(:root)
128
- self.prepend_view_path Rails.root.nil? ? "app/views" : "#{Rails.root}/app/views"
129
- end
140
+ prepend_view_path Rails.root.nil? ? 'app/views' : "#{Rails.root}/app/views" if defined?(Rails) && Rails.respond_to?(:root)
130
141
  end
131
142
 
132
143
  def maybe_call(maybe_proc)
@@ -138,82 +149,48 @@ module ExceptionNotifier
138
149
 
139
150
  def initialize(options)
140
151
  super
152
+
141
153
  delivery_method = (options[:delivery_method] || :smtp)
142
154
  mailer_settings_key = "#{delivery_method}_settings".to_sym
143
155
  options[:mailer_settings] = options.delete(mailer_settings_key)
144
156
 
145
- options.reverse_merge(EmailNotifier.default_options).select{|k,v|[
146
- :sender_address, :exception_recipients, :pre_callback,
147
- :post_callback, :email_prefix, :email_format,
148
- :sections, :background_sections, :verbose_subject, :normalize_subject,
149
- :include_controller_and_action_names_in_subject, :delivery_method, :mailer_settings,
150
- :email_headers, :mailer_parent, :template_path, :deliver_with].include?(k)}.each{|k,v| send("#{k}=", v)}
151
- end
152
-
153
- def options
154
- @options ||= {}.tap do |opts|
155
- self.instance_variables.each { |var| opts[var[1..-1].to_sym] = self.instance_variable_get(var) }
156
- end
157
- end
158
-
159
- def mailer
160
- @mailer ||= Class.new(mailer_parent.constantize).tap do |mailer|
161
- mailer.extend(EmailNotifier::Mailer)
162
- mailer.mailer_name = template_path
163
- end
157
+ @base_options = DEFAULT_OPTIONS.merge(options)
164
158
  end
165
159
 
166
- def call(exception, options={})
160
+ def call(exception, options = {})
167
161
  message = create_email(exception, options)
168
162
 
169
- # FIXME: use `if Gem::Version.new(ActionMailer::VERSION::STRING) < Gem::Version.new('4.1')`
170
- if deliver_with == :default
171
- if message.respond_to?(:deliver_now)
172
- message.deliver_now
173
- else
174
- message.deliver
175
- end
176
- else
177
- message.send(deliver_with)
178
- end
163
+ message.send(base_options[:deliver_with] || default_deliver_with(message))
179
164
  end
180
165
 
181
- def create_email(exception, options={})
166
+ def create_email(exception, options = {})
182
167
  env = options[:env]
183
- default_options = self.options
184
- if env.nil?
185
- send_notice(exception, options, nil, default_options) do |_, default_opts|
168
+
169
+ send_notice(exception, options, nil, base_options) do |_, default_opts|
170
+ if env.nil?
186
171
  mailer.background_exception_notification(exception, options, default_opts)
187
- end
188
- else
189
- send_notice(exception, options, nil, default_options) do |_, default_opts|
172
+ else
190
173
  mailer.exception_notification(env, exception, options, default_opts)
191
174
  end
192
175
  end
193
176
  end
194
177
 
195
- def self.default_options
196
- {
197
- :sender_address => %("Exception Notifier" <exception.notifier@example.com>),
198
- :exception_recipients => [],
199
- :email_prefix => "[ERROR] ",
200
- :email_format => :text,
201
- :sections => %w(request session environment backtrace),
202
- :background_sections => %w(backtrace data),
203
- :verbose_subject => true,
204
- :normalize_subject => false,
205
- :include_controller_and_action_names_in_subject => true,
206
- :delivery_method => nil,
207
- :mailer_settings => nil,
208
- :email_headers => {},
209
- :mailer_parent => 'ActionMailer::Base',
210
- :template_path => 'exception_notifier',
211
- :deliver_with => :default
212
- }
213
- end
214
-
215
178
  def self.normalize_digits(string)
216
179
  string.gsub(/[0-9]+/, 'N')
217
180
  end
181
+
182
+ private
183
+
184
+ def mailer
185
+ @mailer ||= Class.new(base_options[:mailer_parent].constantize).tap do |mailer|
186
+ mailer.extend(EmailNotifier::Mailer)
187
+ mailer.mailer_name = base_options[:template_path]
188
+ end
189
+ end
190
+
191
+ def default_deliver_with(message)
192
+ # FIXME: use `if Gem::Version.new(ActionMailer::VERSION::STRING) < Gem::Version.new('4.1')`
193
+ message.respond_to?(:deliver_now) ? :deliver_now : :deliver
194
+ end
218
195
  end
219
196
  end