exception_notification_more_info 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +7 -0
  3. data/CHANGELOG.rdoc +141 -0
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/CONTRIBUTING.md +42 -0
  6. data/Gemfile +3 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +839 -0
  9. data/Rakefile +23 -0
  10. data/examples/sinatra/Gemfile +8 -0
  11. data/examples/sinatra/Gemfile.lock +95 -0
  12. data/examples/sinatra/Procfile +2 -0
  13. data/examples/sinatra/README.md +11 -0
  14. data/examples/sinatra/config.ru +3 -0
  15. data/examples/sinatra/sinatra_app.rb +32 -0
  16. data/exception_notification_more_info.gemspec +34 -0
  17. data/gemfiles/rails4_0.gemfile +7 -0
  18. data/gemfiles/rails4_1.gemfile +7 -0
  19. data/gemfiles/rails4_2.gemfile +7 -0
  20. data/lib/exception_notification.rb +11 -0
  21. data/lib/exception_notification/rack.rb +59 -0
  22. data/lib/exception_notification/rails.rb +8 -0
  23. data/lib/exception_notification/resque.rb +24 -0
  24. data/lib/exception_notification/sidekiq.rb +31 -0
  25. data/lib/exception_notifier.rb +121 -0
  26. data/lib/exception_notifier/base_notifier.rb +25 -0
  27. data/lib/exception_notifier/campfire_notifier.rb +36 -0
  28. data/lib/exception_notifier/email_notifier.rb +204 -0
  29. data/lib/exception_notifier/hipchat_notifier.rb +45 -0
  30. data/lib/exception_notifier/irc_notifier.rb +51 -0
  31. data/lib/exception_notifier/modules/backtrace_cleaner.rb +13 -0
  32. data/lib/exception_notifier/notifier.rb +16 -0
  33. data/lib/exception_notifier/slack_notifier.rb +73 -0
  34. data/lib/exception_notifier/views/exception_notifier/_backtrace.html.erb +3 -0
  35. data/lib/exception_notifier/views/exception_notifier/_backtrace.text.erb +1 -0
  36. data/lib/exception_notifier/views/exception_notifier/_data.html.erb +6 -0
  37. data/lib/exception_notifier/views/exception_notifier/_data.text.erb +1 -0
  38. data/lib/exception_notifier/views/exception_notifier/_environment.html.erb +10 -0
  39. data/lib/exception_notifier/views/exception_notifier/_environment.text.erb +5 -0
  40. data/lib/exception_notifier/views/exception_notifier/_request.html.erb +36 -0
  41. data/lib/exception_notifier/views/exception_notifier/_request.text.erb +10 -0
  42. data/lib/exception_notifier/views/exception_notifier/_session.html.erb +10 -0
  43. data/lib/exception_notifier/views/exception_notifier/_session.text.erb +2 -0
  44. data/lib/exception_notifier/views/exception_notifier/_title.html.erb +3 -0
  45. data/lib/exception_notifier/views/exception_notifier/_title.text.erb +3 -0
  46. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.html.erb +53 -0
  47. data/lib/exception_notifier/views/exception_notifier/background_exception_notification.text.erb +14 -0
  48. data/lib/exception_notifier/views/exception_notifier/exception_notification.html.erb +54 -0
  49. data/lib/exception_notifier/views/exception_notifier/exception_notification.text.erb +24 -0
  50. data/lib/exception_notifier/webhook_notifier.rb +47 -0
  51. data/lib/generators/exception_notification/install_generator.rb +15 -0
  52. data/lib/generators/exception_notification/templates/exception_notification.rb +53 -0
  53. data/test/dummy/.gitignore +4 -0
  54. data/test/dummy/Gemfile +34 -0
  55. data/test/dummy/Gemfile.lock +137 -0
  56. data/test/dummy/Rakefile +7 -0
  57. data/test/dummy/app/controllers/application_controller.rb +3 -0
  58. data/test/dummy/app/controllers/posts_controller.rb +30 -0
  59. data/test/dummy/app/helpers/application_helper.rb +2 -0
  60. data/test/dummy/app/helpers/posts_helper.rb +2 -0
  61. data/test/dummy/app/models/post.rb +2 -0
  62. data/test/dummy/app/views/exception_notifier/_new_bkg_section.html.erb +1 -0
  63. data/test/dummy/app/views/exception_notifier/_new_bkg_section.text.erb +1 -0
  64. data/test/dummy/app/views/exception_notifier/_new_section.html.erb +1 -0
  65. data/test/dummy/app/views/exception_notifier/_new_section.text.erb +1 -0
  66. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  67. data/test/dummy/app/views/posts/_form.html.erb +0 -0
  68. data/test/dummy/app/views/posts/new.html.erb +0 -0
  69. data/test/dummy/app/views/posts/show.html.erb +0 -0
  70. data/test/dummy/config.ru +4 -0
  71. data/test/dummy/config/application.rb +42 -0
  72. data/test/dummy/config/boot.rb +6 -0
  73. data/test/dummy/config/database.yml +22 -0
  74. data/test/dummy/config/environment.rb +17 -0
  75. data/test/dummy/config/environments/development.rb +25 -0
  76. data/test/dummy/config/environments/production.rb +50 -0
  77. data/test/dummy/config/environments/test.rb +38 -0
  78. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/test/dummy/config/initializers/inflections.rb +10 -0
  80. data/test/dummy/config/initializers/mime_types.rb +5 -0
  81. data/test/dummy/config/initializers/secret_token.rb +8 -0
  82. data/test/dummy/config/initializers/session_store.rb +8 -0
  83. data/test/dummy/config/locales/en.yml +5 -0
  84. data/test/dummy/config/routes.rb +3 -0
  85. data/test/dummy/db/migrate/20110729022608_create_posts.rb +15 -0
  86. data/test/dummy/db/schema.rb +24 -0
  87. data/test/dummy/db/seeds.rb +7 -0
  88. data/test/dummy/lib/tasks/.gitkeep +0 -0
  89. data/test/dummy/public/404.html +26 -0
  90. data/test/dummy/public/422.html +26 -0
  91. data/test/dummy/public/500.html +26 -0
  92. data/test/dummy/public/favicon.ico +0 -0
  93. data/test/dummy/public/images/rails.png +0 -0
  94. data/test/dummy/public/index.html +239 -0
  95. data/test/dummy/public/javascripts/application.js +2 -0
  96. data/test/dummy/public/javascripts/controls.js +965 -0
  97. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  98. data/test/dummy/public/javascripts/effects.js +1123 -0
  99. data/test/dummy/public/javascripts/prototype.js +6001 -0
  100. data/test/dummy/public/javascripts/rails.js +191 -0
  101. data/test/dummy/public/robots.txt +5 -0
  102. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  103. data/test/dummy/public/stylesheets/scaffold.css +56 -0
  104. data/test/dummy/script/rails +6 -0
  105. data/test/dummy/test/fixtures/posts.yml +11 -0
  106. data/test/dummy/test/functional/posts_controller_test.rb +224 -0
  107. data/test/dummy/test/test_helper.rb +13 -0
  108. data/test/exception_notification/rack_test.rb +20 -0
  109. data/test/exception_notifier/campfire_notifier_test.rb +100 -0
  110. data/test/exception_notifier/email_notifier_test.rb +185 -0
  111. data/test/exception_notifier/hipchat_notifier_test.rb +177 -0
  112. data/test/exception_notifier/irc_notifier_test.rb +121 -0
  113. data/test/exception_notifier/sidekiq_test.rb +27 -0
  114. data/test/exception_notifier/slack_notifier_test.rb +179 -0
  115. data/test/exception_notifier/webhook_notifier_test.rb +68 -0
  116. data/test/exception_notifier_test.rb +103 -0
  117. data/test/test_helper.rb +18 -0
  118. metadata +428 -0
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ Bundler::GemHelper.install_tasks
4
+ require 'appraisal'
5
+
6
+ require 'rake/testtask'
7
+
8
+ task 'setup_dummy_app' do
9
+ unless File.exists? "test/dummy/db/test.sqlite3"
10
+ Bundler.with_clean_env do
11
+ sh "cd test/dummy; bundle; bundle exec rake db:migrate; bundle exec rake db:test:prepare; cd ../../;"
12
+ end
13
+ end
14
+ end
15
+
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.libs << 'lib'
18
+ t.libs << 'test'
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = true
21
+ end
22
+
23
+ task :default => [:setup_dummy_app, :test]
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "exception_notification", path: "../../"
4
+
5
+ gem "thin", "~> 1.5.1"
6
+ gem "sinatra", "~> 1.3.5"
7
+ gem "foreman"
8
+ gem "mailcatcher"
@@ -0,0 +1,95 @@
1
+ PATH
2
+ remote: ../../
3
+ specs:
4
+ exception_notification (3.0.1)
5
+ actionmailer (>= 3.0.4)
6
+ activesupport (>= 3.0.4)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionmailer (3.2.13)
12
+ actionpack (= 3.2.13)
13
+ mail (~> 2.5.3)
14
+ actionpack (3.2.13)
15
+ activemodel (= 3.2.13)
16
+ activesupport (= 3.2.13)
17
+ builder (~> 3.0.0)
18
+ erubis (~> 2.7.0)
19
+ journey (~> 1.0.4)
20
+ rack (~> 1.4.5)
21
+ rack-cache (~> 1.2)
22
+ rack-test (~> 0.6.1)
23
+ sprockets (~> 2.2.1)
24
+ activemodel (3.2.13)
25
+ activesupport (= 3.2.13)
26
+ builder (~> 3.0.0)
27
+ activesupport (3.2.13)
28
+ i18n (= 0.6.1)
29
+ multi_json (~> 1.0)
30
+ builder (3.0.4)
31
+ daemons (1.1.9)
32
+ erubis (2.7.0)
33
+ eventmachine (1.0.3)
34
+ foreman (0.61.0)
35
+ thor (>= 0.13.6)
36
+ haml (4.0.2)
37
+ tilt
38
+ hike (1.2.1)
39
+ i18n (0.6.1)
40
+ journey (1.0.4)
41
+ mail (2.5.3)
42
+ i18n (>= 0.4.0)
43
+ mime-types (~> 1.16)
44
+ treetop (~> 1.4.8)
45
+ mailcatcher (0.5.11)
46
+ activesupport (~> 3.0)
47
+ eventmachine (~> 1.0.0)
48
+ haml (>= 3.1, < 5)
49
+ mail (~> 2.3)
50
+ sinatra (~> 1.2)
51
+ skinny (~> 0.2.3)
52
+ sqlite3 (~> 1.3)
53
+ thin (~> 1.5.0)
54
+ mime-types (1.22)
55
+ multi_json (1.7.2)
56
+ polyglot (0.3.3)
57
+ rack (1.4.5)
58
+ rack-cache (1.2)
59
+ rack (>= 0.4)
60
+ rack-protection (1.5.0)
61
+ rack
62
+ rack-test (0.6.2)
63
+ rack (>= 1.0)
64
+ sinatra (1.3.6)
65
+ rack (~> 1.4)
66
+ rack-protection (~> 1.3)
67
+ tilt (~> 1.3, >= 1.3.3)
68
+ skinny (0.2.3)
69
+ eventmachine (~> 1.0.0)
70
+ thin (~> 1.5.0)
71
+ sprockets (2.2.2)
72
+ hike (~> 1.2)
73
+ multi_json (~> 1.0)
74
+ rack (~> 1.0)
75
+ tilt (~> 1.1, != 1.3.0)
76
+ sqlite3 (1.3.7)
77
+ thin (1.5.1)
78
+ daemons (>= 1.0.9)
79
+ eventmachine (>= 0.12.6)
80
+ rack (>= 1.0.0)
81
+ thor (0.18.1)
82
+ tilt (1.3.6)
83
+ treetop (1.4.12)
84
+ polyglot
85
+ polyglot (>= 0.3.1)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ exception_notification!
92
+ foreman
93
+ mailcatcher
94
+ sinatra (~> 1.3.5)
95
+ thin (~> 1.5.1)
@@ -0,0 +1,2 @@
1
+ web: bundle exec thin start --port 3000
2
+ mail: bundle exec mailcatcher --foreground --smtp-port 1025 --http-port 1080
@@ -0,0 +1,11 @@
1
+ # Using Exception Notification with Sinatra
2
+
3
+ ## Quick start
4
+
5
+ git clone git@github.com:smartinez87/exception_notification.git
6
+ cd exception_notification/examples/sinatra
7
+ bundle install
8
+ bundle exec foreman start
9
+
10
+
11
+ The last command starts two services, a smtp server and the sinatra app itself. Thus, visit [http://localhost:1080/](http://localhost:1080/) to check the emails sent and, in a separated tab, visit [http://localhost:3000](http://localhost:3000) and cause some errors. For more info, use the [source](https://github.com/smartinez87/exception_notification/blob/master/examples/sinatra/sinatra_app.rb) Luke.
@@ -0,0 +1,3 @@
1
+ require ::File.expand_path('../sinatra_app', __FILE__)
2
+
3
+ run SinatraApp
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'sinatra/base'
4
+ require 'exception_notification'
5
+
6
+ class SinatraApp < Sinatra::Base
7
+ use Rack::Config do |env|
8
+ env["action_dispatch.parameter_filter"] = [:password] # This is highly recommended. It will prevent the ExceptionNotification email from including your users' passwords
9
+ end
10
+
11
+ use ExceptionNotification::Rack,
12
+ :email => {
13
+ :email_prefix => "[Example] ",
14
+ :sender_address => %{"notifier" <notifier@example.com>},
15
+ :exception_recipients => %w{exceptions@example.com},
16
+ :smtp_settings => { :address => "localhost", :port => 1025 }
17
+ }
18
+
19
+ get '/' do
20
+ raise StandardError, "ERROR: #{params[:error]}" unless params[:error].blank?
21
+ 'Everything is fine! Now, lets break things clicking <a href="/?error=ops"> here </a>. Dont forget to see the emails at <a href="http://localhost:1080">mailcatcher</a> !'
22
+ end
23
+
24
+ get '/background_notification' do
25
+ begin
26
+ 1/0
27
+ rescue Exception => e
28
+ ExceptionNotifier.notify_exception(e, :data => {:msg => "Cannot divide by zero!"})
29
+ end
30
+ 'Check email at <a href="http://localhost:1080">mailcatcher</a>.'
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'exception_notification_more_info'
3
+ s.version = '1.0.0'
4
+ s.authors = ["Jamis Buck", "Josh Peek", "Hyunjae Park"]
5
+ s.summary = "Exception notification for Rails apps(+ More Info!)"
6
+ s.homepage = "https://smartinez87.github.io/exception_notification/"
7
+ s.email = "dev.hyunjae.park@gmail.com"
8
+ s.license = "MIT"
9
+
10
+ s.required_ruby_version = '>= 2.0'
11
+ s.required_rubygems_version = '>= 1.8.11'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.files -= `git ls-files -- .??*`.split("\n")
15
+ s.test_files = `git ls-files -- test`.split("\n")
16
+ s.require_path = 'lib'
17
+
18
+ s.add_dependency("actionmailer", "~> 4.0")
19
+ s.add_dependency("activesupport", "~> 4.0")
20
+
21
+ s.add_development_dependency "rails", "~> 4.0"
22
+ s.add_development_dependency "resque", "~> 1.2.0"
23
+ # Sidekiq 3.2.2 does not support Ruby 1.9.
24
+ s.add_development_dependency "sidekiq", "~> 3.0.0", "< 3.2.2"
25
+ s.add_development_dependency "tinder", "~> 1.8"
26
+ s.add_development_dependency "httparty", "~> 0.10.2"
27
+ s.add_development_dependency "mocha", ">= 0.13.0"
28
+ s.add_development_dependency "sqlite3", ">= 1.3.4"
29
+ s.add_development_dependency "coveralls", "~> 0.8.2"
30
+ s.add_development_dependency "appraisal", "~> 2.0.0"
31
+ s.add_development_dependency "hipchat", ">= 1.0.0"
32
+ s.add_development_dependency "carrier-pigeon", ">= 0.7.0"
33
+ s.add_development_dependency "slack-notifier", ">= 1.0.0"
34
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.0.5"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.1.1"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ require 'exception_notifier'
2
+ require 'exception_notification/rack'
3
+
4
+ module ExceptionNotification
5
+ # Alternative way to setup ExceptionNotification.
6
+ # Run 'rails generate exception_notification:install' to create
7
+ # a fresh initializer with all configuration values.
8
+ def self.configure
9
+ yield ExceptionNotifier
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module ExceptionNotification
2
+ class Rack
3
+ class CascadePassException < Exception; end
4
+
5
+ def initialize(app, options = {})
6
+ @app = app
7
+
8
+ ExceptionNotifier.ignored_exceptions = options.delete(:ignore_exceptions) if options.key?(:ignore_exceptions)
9
+
10
+ if options.key?(:ignore_if)
11
+ rack_ignore = options.delete(:ignore_if)
12
+ ExceptionNotifier.ignore_if do |exception, options|
13
+ options.key?(:env) && rack_ignore.call(options[:env], exception)
14
+ end
15
+ end
16
+
17
+ if options.key?(:ignore_crawlers)
18
+ ignore_crawlers = options.delete(:ignore_crawlers)
19
+ ExceptionNotifier.ignore_if do |exception, options|
20
+ options.key?(:env) && from_crawler(options[:env], ignore_crawlers)
21
+ end
22
+ end
23
+
24
+ @ignore_cascade_pass = options.delete(:ignore_cascade_pass) { true }
25
+
26
+ options.each do |notifier_name, options|
27
+ ExceptionNotifier.register_exception_notifier(notifier_name, options)
28
+ end
29
+ end
30
+
31
+ def call(env)
32
+ _, headers, _ = response = @app.call(env)
33
+
34
+ if !@ignore_cascade_pass && headers['X-Cascade'] == 'pass'
35
+ msg = "This exception means that the preceding Rack middleware set the 'X-Cascade' header to 'pass' -- in " <<
36
+ "Rails, this often means that the route was not found (404 error)."
37
+ raise CascadePassException, msg
38
+ end
39
+
40
+ response
41
+ rescue Exception => exception
42
+ if ExceptionNotifier.notify_exception(exception, :env => env)
43
+ env['exception_notifier.delivered'] = true
44
+ end
45
+
46
+ raise exception unless exception.is_a?(CascadePassException)
47
+ response
48
+ end
49
+
50
+ private
51
+
52
+ def from_crawler(env, ignored_crawlers)
53
+ agent = env['HTTP_USER_AGENT']
54
+ Array(ignored_crawlers).any? do |crawler|
55
+ agent =~ Regexp.new(crawler)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ module ExceptionNotification
2
+ class Engine < ::Rails::Engine
3
+ config.exception_notification = ExceptionNotifier
4
+ config.exception_notification.logger = Rails.logger
5
+
6
+ config.app_middleware.use ExceptionNotification::Rack
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'resque/failure/base'
2
+
3
+ module ExceptionNotification
4
+ class Resque < Resque::Failure::Base
5
+
6
+ def self.count
7
+ Stat[:failed]
8
+ end
9
+
10
+ def save
11
+ data = {
12
+ :failed_at => Time.now.to_s,
13
+ :queue => queue,
14
+ :worker => worker.to_s,
15
+ :payload => payload,
16
+ :error_class => exception.class.name,
17
+ :error_message => exception.message
18
+ }
19
+
20
+ ExceptionNotifier.notify_exception(exception, :data => { :resque => data })
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'sidekiq'
2
+
3
+ # Note: this class is only needed for Sidekiq version < 3.
4
+ module ExceptionNotification
5
+ class Sidekiq
6
+
7
+ def call(worker, msg, queue)
8
+ begin
9
+ yield
10
+ rescue Exception => exception
11
+ ExceptionNotifier.notify_exception(exception, :data => { :sidekiq => msg })
12
+ raise exception
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+
19
+ if ::Sidekiq::VERSION < '3'
20
+ ::Sidekiq.configure_server do |config|
21
+ config.server_middleware do |chain|
22
+ chain.add ::ExceptionNotification::Sidekiq
23
+ end
24
+ end
25
+ else
26
+ ::Sidekiq.configure_server do |config|
27
+ config.error_handlers << Proc.new { |ex, context|
28
+ ExceptionNotifier.notify_exception(ex, :data => { :sidekiq => context })
29
+ }
30
+ end
31
+ end
@@ -0,0 +1,121 @@
1
+ require 'logger'
2
+ require 'active_support/core_ext/string/inflections'
3
+ require 'active_support/core_ext/module/attribute_accessors'
4
+ require 'exception_notifier/base_notifier'
5
+
6
+ module ExceptionNotifier
7
+
8
+ autoload :BacktraceCleaner, 'exception_notifier/modules/backtrace_cleaner'
9
+
10
+ autoload :Notifier, 'exception_notifier/notifier'
11
+ autoload :EmailNotifier, 'exception_notifier/email_notifier'
12
+ autoload :CampfireNotifier, 'exception_notifier/campfire_notifier'
13
+ autoload :HipchatNotifier, 'exception_notifier/hipchat_notifier'
14
+ autoload :WebhookNotifier, 'exception_notifier/webhook_notifier'
15
+ autoload :IrcNotifier, 'exception_notifier/irc_notifier'
16
+ autoload :SlackNotifier, 'exception_notifier/slack_notifier'
17
+
18
+ class UndefinedNotifierError < StandardError; end
19
+
20
+ # Define logger
21
+ mattr_accessor :logger
22
+ @@logger = Logger.new(STDOUT)
23
+
24
+ # Define a set of exceptions to be ignored, ie, dont send notifications when any of them are raised.
25
+ mattr_accessor :ignored_exceptions
26
+ @@ignored_exceptions = %w{ActiveRecord::RecordNotFound AbstractController::ActionNotFound ActionController::RoutingError ActionController::UnknownFormat}
27
+
28
+ mattr_accessor :testing_mode
29
+ @@testing_mode = false
30
+
31
+ class << self
32
+ # Store conditions that decide when exceptions must be ignored or not.
33
+ @@ignores = []
34
+
35
+ # Store notifiers that send notifications when exceptions are raised.
36
+ @@notifiers = {}
37
+
38
+ def testing_mode!
39
+ self.testing_mode = true
40
+ end
41
+
42
+ def notify_exception(exception, options={})
43
+ return false if ignored_exception?(options[:ignore_exceptions], exception)
44
+ return false if ignored?(exception, options)
45
+ selected_notifiers = options.delete(:notifiers) || notifiers
46
+ [*selected_notifiers].each do |notifier|
47
+ fire_notification(notifier, exception, options.dup)
48
+ end
49
+ true
50
+ end
51
+
52
+ def register_exception_notifier(name, notifier_or_options)
53
+ if notifier_or_options.respond_to?(:call)
54
+ @@notifiers[name] = notifier_or_options
55
+ elsif notifier_or_options.is_a?(Hash)
56
+ create_and_register_notifier(name, notifier_or_options)
57
+ else
58
+ raise ArgumentError, "Invalid notifier '#{name}' defined as #{notifier_or_options.inspect}"
59
+ end
60
+ end
61
+ alias add_notifier register_exception_notifier
62
+
63
+ def unregister_exception_notifier(name)
64
+ @@notifiers.delete(name)
65
+ end
66
+
67
+ def registered_exception_notifier(name)
68
+ @@notifiers[name]
69
+ end
70
+
71
+ def notifiers
72
+ @@notifiers.keys
73
+ end
74
+
75
+ # Adds a condition to decide when an exception must be ignored or not.
76
+ #
77
+ # ExceptionNotifier.ignore_if do |exception, options|
78
+ # not Rails.env.production?
79
+ # end
80
+ def ignore_if(&block)
81
+ @@ignores << block
82
+ end
83
+
84
+ def clear_ignore_conditions!
85
+ @@ignores.clear
86
+ end
87
+
88
+ private
89
+ def ignored?(exception, options)
90
+ @@ignores.any?{ |condition| condition.call(exception, options) }
91
+ rescue Exception => e
92
+ raise e if @@testing_mode
93
+
94
+ logger.warn "An error occurred when evaluating an ignore condition. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
95
+ false
96
+ end
97
+
98
+ def ignored_exception?(ignore_array, exception)
99
+ (Array(ignored_exceptions) + Array(ignore_array)).map(&:to_s).include?(exception.class.name)
100
+ end
101
+
102
+ def fire_notification(notifier_name, exception, options)
103
+ notifier = registered_exception_notifier(notifier_name)
104
+ notifier.call(exception, options)
105
+ rescue Exception => e
106
+ raise e if @@testing_mode
107
+
108
+ logger.warn "An error occurred when sending a notification using '#{notifier_name}' notifier. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
109
+ false
110
+ end
111
+
112
+ def create_and_register_notifier(name, options)
113
+ notifier_classname = "#{name}_notifier".camelize
114
+ notifier_class = ExceptionNotifier.const_get(notifier_classname)
115
+ notifier = notifier_class.new(options)
116
+ register_exception_notifier(name, notifier)
117
+ rescue NameError => e
118
+ raise UndefinedNotifierError, "No notifier named '#{name}' was found. Please, revise your configuration options. Cause: #{e.message}"
119
+ end
120
+ end
121
+ end