Commit 3dde7609e7c14ead995a1670d15029f8b1bc36e9
0 parents
Exists in
master
first commit
Showing
20 changed files
with
616 additions
and
0 deletions
Show diff stats
1 | +++ a/.travis.yml | |
... | ... | @@ -0,0 +1,23 @@ |
1 | +language: ruby | |
2 | +cache: bundler | |
3 | + | |
4 | +before_install: gem install bundler | |
5 | + | |
6 | +rvm: | |
7 | + - 3.0.0 | |
8 | + - 2.7.2 | |
9 | + - 2.6.6 | |
10 | + - 2.5.8 | |
11 | + - 2.4.10 | |
12 | +gemfile: | |
13 | + - gemfiles/Gemfile.rails_6.1 | |
14 | + - gemfiles/Gemfile.rails_6.0 | |
15 | + - gemfiles/Gemfile.rails_5.2 | |
16 | +jobs: | |
17 | + exclude: | |
18 | + - rvm: 3.0.0 | |
19 | + gemfile: gemfiles/Gemfile.rails_5.2.4 | |
20 | + - rvm: 2.4.10 | |
21 | + gemfile: gemfiles/Gemfile.rails_6.0 | |
22 | + - rvm: 2.4.10 | |
23 | + gemfile: gemfiles/Gemfile.rails_6.1 | ... | ... |
1 | +++ a/LICENSE.txt | |
... | ... | @@ -0,0 +1,21 @@ |
1 | +The MIT License (MIT) | |
2 | + | |
3 | +Copyright (c) 2017 Koji Onishi | |
4 | + | |
5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | +of this software and associated documentation files (the "Software"), to deal | |
7 | +in the Software without restriction, including without limitation the rights | |
8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | +copies of the Software, and to permit persons to whom the Software is | |
10 | +furnished to do so, subject to the following conditions: | |
11 | + | |
12 | +The above copyright notice and this permission notice shall be included in | |
13 | +all copies or substantial portions of the Software. | |
14 | + | |
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | +THE SOFTWARE. | ... | ... |
1 | +++ a/README.md | |
... | ... | @@ -0,0 +1,93 @@ |
1 | +# Exception File Notifier | |
2 | + | |
3 | +[](https://badge.fury.io/rb/exception_file_notifier) | |
4 | +[](http://travis-ci.org/fursich/exception_file_notifier) | |
5 | +[](LICENSE) | |
6 | + | |
7 | +**Exception File Notifier** is a custom notifier for [Exception Notification](https://github.com/smartinez87/exception_notification), that records notifications onto a log file when errors occur in a Rack/Rails application. | |
8 | + | |
9 | +All the error logs are converted into JSON format. This would be useful typically when you wish to monitor an app with monitoring tools - e.g. Kibana + ElasticSearch + Fluentd, or anything alike. | |
10 | + | |
11 | +## Installation | |
12 | + | |
13 | +Add this line to your application's Gemfile: | |
14 | + | |
15 | +```ruby | |
16 | +gem 'exception_file_notifier' | |
17 | +``` | |
18 | + | |
19 | +And then execute: | |
20 | + | |
21 | + $ bundle | |
22 | + | |
23 | +Or install it yourself as: | |
24 | + | |
25 | + $ gem install exception_file_notifier | |
26 | + | |
27 | +## Usage | |
28 | + | |
29 | +Set proper configs in *config/initializers/exception_notification.rb* | |
30 | + | |
31 | +To get started, use the simplest settings: | |
32 | + | |
33 | +```ruby:config/initializers/exception_notification.rb | |
34 | +ExceptionNotification.configure do |config| | |
35 | + | |
36 | + config.add_notifier :file, { | |
37 | + filename: "#{Rails.root}/log/exceptions.log" | |
38 | + } | |
39 | +``` | |
40 | + | |
41 | +That works in all the environment (including test). | |
42 | + | |
43 | +You could also customize settings like so: | |
44 | + | |
45 | +```ruby:config/initializers/exception_notification.rb | |
46 | +ExceptionNotification.configure do |config| | |
47 | + | |
48 | +# disable all the notifiers in certain environment | |
49 | + config.ignore_if do |exception, options| | |
50 | + Rails.env.test? | |
51 | + end | |
52 | + | |
53 | + config.add_notifier :file, { | |
54 | + filename: "#{Rails.root}/log/exceptions_#{Rails.env}.log", # generate different log files depending on environments | |
55 | + shift_age: 'daily' # use shift_age/shift_size options to rotate log files | |
56 | + } | |
57 | +``` | |
58 | + | |
59 | +You could also pass as many original values as you like, which will be evaluated JSON-ified at the time when an exception occurs. For detailed setting you may wish to consult with the Exception Notification [original readme](https://github.com/smartinez87/exception_notification). | |
60 | + | |
61 | +#### available options: | |
62 | + | |
63 | +- filename: specify the log file (preferablly with its absolute path) | |
64 | + | |
65 | +- shift_age: option for log file rotation: directly passed to Ruby Logger | |
66 | + | |
67 | +- shift_size: option for log file rotation: directly passed to Ruby Logger | |
68 | + | |
69 | +(for the latter options see also: https://docs.ruby-lang.org/ja/latest/method/Logger/s/new.html) | |
70 | + | |
71 | +#### Note | |
72 | + | |
73 | +Due to [a bug](https://bugs.ruby-lang.org/issues/12948) with ruby Logger discovered in ruby 2.2 - 2.3, it might happen that you cannot rotate logs by using shift_age. (for those who come up with any workaround for this, please let us know / PR are welcomed) | |
74 | + | |
75 | +Meanwhile you could cope with either 1) upgrading your ruby version upto 2.4, or 2) rotate logs by size, not date | |
76 | + | |
77 | +## Development | |
78 | + | |
79 | +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. | |
80 | + | |
81 | +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). | |
82 | + | |
83 | +## Contributing | |
84 | + | |
85 | +Bug reports and pull requests are welcome on GitHub at https://github.com/fursich/exception_file_notifier. | |
86 | + | |
87 | +## Special Thanks To | |
88 | + | |
89 | +All the folks who have given supports, especially @motchang and @katsurak for great advises and reviews. | |
90 | + | |
91 | +## License | |
92 | + | |
93 | +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). | ... | ... |
1 | +++ a/bin/console | |
... | ... | @@ -0,0 +1,14 @@ |
1 | +#!/usr/bin/env ruby | |
2 | + | |
3 | +require "bundler/setup" | |
4 | +require "exception_file_notifier" | |
5 | + | |
6 | +# You can add fixtures and/or initialization code here to make experimenting | |
7 | +# with your gem easier. You can also use a different console, if you like. | |
8 | + | |
9 | +# (If you use this, don't forget to add pry to your Gemfile!) | |
10 | +# require "pry" | |
11 | +# Pry.start | |
12 | + | |
13 | +require "irb" | |
14 | +IRB.start(__FILE__) | ... | ... |
1 | +++ a/exception_file_notifier.gemspec | |
... | ... | @@ -0,0 +1,42 @@ |
1 | +# coding: utf-8 | |
2 | +lib = File.expand_path('../lib', __FILE__) | |
3 | +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | |
4 | +require 'exception_notifier/exception_file_notifier/version' | |
5 | + | |
6 | +Gem::Specification.new do |spec| | |
7 | + spec.name = "exception_file_notifier" | |
8 | + spec.version = ::ExceptionNotifier::ExceptionFileNotifier::VERSION | |
9 | + spec.authors = ["Koji Onishi"] | |
10 | + spec.email = ["fursich0@gmail.com"] | |
11 | + | |
12 | + spec.summary = %q{ A custom notifier for ExceptionNotification that generates exception log files in JSON format. } | |
13 | + spec.description = %q{ Exception File Notifier records exception logs in JSON format, helping you track errors in the production environment. } | |
14 | + spec.homepage = "https://github.com/fursich/exception_file_notifier" | |
15 | + spec.license = "MIT" | |
16 | + | |
17 | + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' | |
18 | + # to allow pushing to a single host or delete this section to allow pushing to any host. | |
19 | + # if spec.respond_to?(:metadata) | |
20 | + # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' | |
21 | + # else | |
22 | + # raise "RubyGems 2.0 or newer is required to protect against " \ | |
23 | + # "public gem pushes." | |
24 | + # end | |
25 | + | |
26 | + spec.files = `git ls-files -z`.split("\x0").reject do |f| | |
27 | + f.match(%r{^(test|spec|features)/}) | |
28 | + end | |
29 | + spec.bindir = "exe" | |
30 | + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } | |
31 | + spec.require_paths = ["lib"] | |
32 | + | |
33 | + spec.required_ruby_version = '>= 2.3' | |
34 | + spec.required_rubygems_version = '>= 1.8.11' | |
35 | + spec.add_dependency "exception_notification", ">= 4.0" | |
36 | + spec.add_dependency("activesupport", ">= 4.0", "<7") | |
37 | + | |
38 | + spec.add_development_dependency "bundler", ">= 2" | |
39 | + spec.add_development_dependency "rake", ">= 10.0" | |
40 | + spec.add_development_dependency "rspec", ">= 3.10" | |
41 | + spec.add_development_dependency "pry" | |
42 | +end | ... | ... |
lib/exception_notifier/exception_file_notifier/version.rb
0 → 100644
1 | +++ a/lib/exception_notifier/file_notifier.rb | |
... | ... | @@ -0,0 +1,155 @@ |
1 | +require "exception_notification" | |
2 | +require "action_dispatch" | |
3 | +require "active_support/core_ext/hash" | |
4 | +require "socket" | |
5 | +require 'logger' | |
6 | +require 'json' | |
7 | + | |
8 | +module ExceptionNotifier | |
9 | + class FileNotifier < BaseNotifier | |
10 | + include ExceptionNotifier::BacktraceCleaner | |
11 | + | |
12 | + def initialize(options={}) | |
13 | + filename = options[:filename] || "log/error.log" | |
14 | + shift_age = options[:shift_age] || 0 | |
15 | + shift_size = options[:shift_size] || nil | |
16 | + | |
17 | + @logger_options = [filename, shift_age, shift_size] | |
18 | + @formatter = -> (_, _, _, message) { message + "\n" } | |
19 | + end | |
20 | + | |
21 | + def call(exception=nil, options={}) | |
22 | + append_log exception_message(options[:env], exception, options) | |
23 | + end | |
24 | + | |
25 | + def append_log(message) | |
26 | + log = ::Logger.new(*@logger_options) | |
27 | + log.formatter = @formatter | |
28 | + error_log = generate_json(message) | |
29 | + log.error(error_log) | |
30 | + log.close | |
31 | + end | |
32 | + | |
33 | + def generate_json(message) | |
34 | + ::JSON.generate(deep_encode(message)) | |
35 | + end | |
36 | + | |
37 | + def exception_message(env, exception, options={}) | |
38 | + return error_message(exception) unless exception.is_a?(Exception) | |
39 | + | |
40 | + if env.present? | |
41 | + exception_notification(options[:env], exception, options) | |
42 | + else | |
43 | + background_exception_notification(exception, options) | |
44 | + end | |
45 | + end | |
46 | + | |
47 | + def exception_notification(env, exception, options) | |
48 | + @env = env | |
49 | + @exception = exception | |
50 | + @options = options.reverse_merge(@env['exception_notifier.options'] || {}).symbolize_keys | |
51 | + @kontroller = @env['action_controller.instance'] || MissingController.new | |
52 | + @request = ::ActionDispatch::Request.new(@env) | |
53 | + @backtrace = exception.backtrace ? clean_backtrace(exception) : [] | |
54 | + @timestamp = ::Time.current | |
55 | + @data = (@env['exception_notifier.exception_data'] || {}).merge(options[:data] || {}) | |
56 | + | |
57 | + render_notice | |
58 | + end | |
59 | + | |
60 | + def background_exception_notification(exception, options) | |
61 | + @exception = exception | |
62 | + @options = options.symbolize_keys | |
63 | + @backtrace = exception.backtrace || [] | |
64 | + @timestamp = ::Time.current | |
65 | + @data = options[:data] || {} | |
66 | + @env = @kontroller = nil | |
67 | + | |
68 | + render_notice | |
69 | + end | |
70 | + | |
71 | + def error_message(exception) | |
72 | + { | |
73 | + message: '[FATAL] NO EXCEPTION GIVEN: Please provide Exception as argument', | |
74 | + exception: exception.try(:to_s), | |
75 | + timestamp: ::Time.current, | |
76 | + } | |
77 | + end | |
78 | + | |
79 | + def render_notice | |
80 | + set_data_variables | |
81 | + | |
82 | + exception_details = { | |
83 | + exception: @exception.class, | |
84 | + controller: @kontroller.present? ? @kontroller.controller_name : '[BACKGROUND]', | |
85 | + action: @kontroller.present? ? @kontroller.action_name : '[BACKGROUND]', | |
86 | + exception_message: @exception.message, | |
87 | + | |
88 | + timestamp: @timestamp, | |
89 | + server: ::Socket.gethostname, | |
90 | + rails_root: (defined?(::Rails) && ::Rails.respond_to?(:root) ) ? ::Rails.root : nil, | |
91 | + process: $$, | |
92 | + backtrace: @backtrace, | |
93 | + } | |
94 | + | |
95 | + if @env.present? | |
96 | + exception_details.merge!({ | |
97 | + url: @request.url, | |
98 | + http_method: @request.request_method, | |
99 | + ip_address: @request.remote_ip, | |
100 | + parameters: (@request.filtered_parameters.inspect rescue "[NOT ENCODABLE]"), | |
101 | + session_id: (@request.ssl? ? "[FILTERED]" : @request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])), | |
102 | + session_data: @request.session.to_hash, | |
103 | + }) | |
104 | + filtered_env = @request.filtered_env | |
105 | + filtered_env.sort_by { |key, _| key.to_s }.each do |key, value| | |
106 | + exception_details[key] = value | |
107 | + end | |
108 | + end | |
109 | + | |
110 | + exception_details.merge!({ | |
111 | + data: @data, | |
112 | + }) | |
113 | + | |
114 | + exception_details | |
115 | + end | |
116 | + | |
117 | + class MissingController | |
118 | + def controller_name | |
119 | + '[NO CONTROLLER]' | |
120 | + end | |
121 | + def action_name | |
122 | + '[NO CONTROLLER]' | |
123 | + end | |
124 | + def method_missing(*args, &block) | |
125 | + end | |
126 | + end | |
127 | + | |
128 | + def set_data_variables | |
129 | + @data.each do |name, value| | |
130 | + instance_variable_set("@#{name}", value) | |
131 | + end | |
132 | + end | |
133 | + | |
134 | + def deep_encode(obj) | |
135 | + case obj | |
136 | + when Array | |
137 | + obj.map { |o| deep_encode(o) } | |
138 | + when Hash | |
139 | + deep_encode(obj.to_a).to_h | |
140 | + when String | |
141 | + encode_to_utf8(obj) | |
142 | + else | |
143 | + obj.respond_to?(:to_s) ? encode_to_utf8(obj.to_s) : '[NOT ENCODABLE]' | |
144 | + end | |
145 | + end | |
146 | + | |
147 | + def encode_to_utf8(str) | |
148 | + quick_sanitization(str).encode(::Encoding.find('UTF-8'), invalid: :replace, undef: :replace, replace: '?') | |
149 | + end | |
150 | + | |
151 | + def quick_sanitization(str) # stringify any random objects in a safe (and convenient) manner | |
152 | + str.inspect.gsub(/\A\"(.*)\"\z/,'\1') | |
153 | + end | |
154 | + end | |
155 | +end | ... | ... |
1 | +++ a/spec/exception_notifier/exception_file_notifier_spec.rb | |
... | ... | @@ -0,0 +1,7 @@ |
1 | +require "spec_helper" | |
2 | + | |
3 | +RSpec.describe ExceptionNotifier::ExceptionFileNotifier do | |
4 | + it "has a version number" do | |
5 | + expect(ExceptionNotifier::ExceptionFileNotifier::VERSION).not_to be nil | |
6 | + end | |
7 | +end | ... | ... |
1 | +++ a/spec/exception_notifier/file_notifier_spec.rb | |
... | ... | @@ -0,0 +1,161 @@ |
1 | +require "spec_helper" | |
2 | +require "exception_test_helper" | |
3 | + | |
4 | +RSpec.describe ExceptionNotifier::FileNotifier do | |
5 | + include ExceptionTestHelper | |
6 | + | |
7 | + let(:notifier) { | |
8 | + ExceptionNotifier::FileNotifier.new( | |
9 | + options | |
10 | + ) | |
11 | + } | |
12 | + | |
13 | + let(:options) { | |
14 | + { | |
15 | + filename: filename, | |
16 | + shift_age: shift_age, | |
17 | + shift_size: shift_size, | |
18 | + }.compact | |
19 | + } | |
20 | + | |
21 | + let(:filename) { 'log/exceptions.log' } | |
22 | + let(:shift_age) { 'daily' } | |
23 | + let(:shift_size) { nil } | |
24 | + | |
25 | + describe '#initialize' do | |
26 | + let(:logger_options) { | |
27 | + notifier.instance_eval {@logger_options} | |
28 | + } | |
29 | + it 'constructs the right file name based on given options' do | |
30 | + expect(logger_options).to contain_exactly(filename, shift_age, shift_size) | |
31 | + end | |
32 | + end | |
33 | + | |
34 | + describe '#generate_json' do | |
35 | + context 'in case broken strings are given' do | |
36 | + let (:hash_with_broken_codes) { | |
37 | + { | |
38 | + French: 'La langue française'.force_encoding('ASCII'), | |
39 | + Russian: 'Русский язык'.encode('WINDOWS-31J').force_encoding('UTF-16'), | |
40 | + Japanese: {'日本語'.encode('UTF-8').force_encoding('WINDOWS-31J') => 'にほんご'.force_encoding('ASCII') }, | |
41 | + } | |
42 | + } | |
43 | + | |
44 | + it 'jasonifies the strings properly without causing errors' do | |
45 | + expect( JSON.parse(notifier.generate_json(hash_with_broken_codes)) ).to eq( | |
46 | + { | |
47 | + "French"=>"La langue fran\\xC3\\xA7aise", | |
48 | + "Russian"=>"\\x84\\x51\\x84\\x85\\x84\\x83\\x84\\x83\\x84\\x7B\\x84\\x79\\x84\\x7A\\x20\\x84\\x91\\x84\\x78\\x84\\x8D\\x84\\x7B", | |
49 | + "Japanese" => {"\\x{E697}\\xA5\\x{E69C}\\xAC\\x{E8AA}\\x9E"=>"\\xE3\\x81\\xAB\\xE3\\x81\\xBB\\xE3\\x82\\x93\\xE3\\x81\\x94"}, | |
50 | + } | |
51 | + ) | |
52 | + end | |
53 | + end | |
54 | + end | |
55 | + | |
56 | + describe '#call' do | |
57 | + let (:backtrace) { | |
58 | + [ | |
59 | + %(/test/app/controllers/api/dummy_controller.rb:123:in `build_whatever_params'), | |
60 | + %(/test/app/controllers/api/dummy_controller.rb:321:in `any_random_loop'), | |
61 | + ] | |
62 | + } | |
63 | + | |
64 | + context 'with env options' do | |
65 | + let (:env) { | |
66 | + { "rack.version" => [1, 2], | |
67 | + "action_controller.instance" => ExceptionTestHelper::DummyController.new, | |
68 | + "REQUEST_METHOD" => "GET", | |
69 | + "SERVER_NAME" => "example.org", | |
70 | + "SERVER_PORT" => "80", | |
71 | + "PATH_INFO" => "/category", | |
72 | + "rack.url_scheme" => "http", | |
73 | + "REMOTE_ADDR" => "127.0.0.1", | |
74 | + "HTTP_HOST" => "example.org", | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + it 'indicates the exception' do | |
79 | + expect(notifier).to receive(:append_log).with( | |
80 | + hash_including( | |
81 | + exception: ExceptionTestHelper::DummyException, | |
82 | + exception_message: 'Dummy Exception Message' | |
83 | + ) | |
84 | + ).once | |
85 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | |
86 | + end | |
87 | + | |
88 | + it 'provides controller and action name' do | |
89 | + expect(notifier).to receive(:append_log).with( | |
90 | + hash_including( | |
91 | + controller: 'DummyController', | |
92 | + action: 'TestAction' | |
93 | + ) | |
94 | + ).once | |
95 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | |
96 | + end | |
97 | + | |
98 | + it 'provides url' do | |
99 | + expect(notifier).to receive(:append_log).with( | |
100 | + hash_including( | |
101 | + url: "http://example.org/category" | |
102 | + ) | |
103 | + ).once | |
104 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | |
105 | + end | |
106 | + | |
107 | + it 'provides backtrace' do | |
108 | + expect(notifier).to receive(:append_log).with( | |
109 | + hash_including(backtrace: backtrace) | |
110 | + ).once | |
111 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | |
112 | + end | |
113 | + end | |
114 | + | |
115 | + context 'without controller' do | |
116 | + let (:env) { | |
117 | + { "rack.version" => [1, 2], | |
118 | + "REQUEST_METHOD" => "GET", | |
119 | + "SERVER_NAME" => "example.org", | |
120 | + "SERVER_PORT" => "80", | |
121 | + "PATH_INFO" => "/category", | |
122 | + "rack.url_scheme" => "http", | |
123 | + "REMOTE_ADDR" => "127.0.0.1", | |
124 | + "HTTP_HOST" => "example.org", | |
125 | + } | |
126 | + } | |
127 | + it 'does not provide controller and action name' do | |
128 | + expect(notifier).to receive(:append_log).with( | |
129 | + hash_including( | |
130 | + controller: '[NO CONTROLLER]', | |
131 | + action: '[NO CONTROLLER]' | |
132 | + ) | |
133 | + ).once | |
134 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | |
135 | + end | |
136 | + end | |
137 | + | |
138 | + context 'without env options' do | |
139 | + it 'indicates the exception' do | |
140 | + expect(notifier).to receive(:append_log).with( | |
141 | + hash_including( | |
142 | + exception: ExceptionTestHelper::DummyException, | |
143 | + exception_message: 'Dummy Exception Message' | |
144 | + ) | |
145 | + ) | |
146 | + notifier.call(ExceptionTestHelper::DummyException.new) | |
147 | + end | |
148 | + | |
149 | + it 'does not provide controller and action name' do | |
150 | + expect(notifier).to receive(:append_log).with( | |
151 | + hash_including( | |
152 | + controller: '[BACKGROUND]', | |
153 | + action: '[BACKGROUND]' | |
154 | + ) | |
155 | + ) | |
156 | + notifier.call(ExceptionTestHelper::DummyException.new) | |
157 | + end | |
158 | + end | |
159 | + end | |
160 | + | |
161 | +end | ... | ... |
1 | +++ a/spec/exception_test_helper.rb | |
... | ... | @@ -0,0 +1,23 @@ |
1 | +module ExceptionTestHelper | |
2 | + | |
3 | + class DummyException < Exception | |
4 | + def initialize(backtrace = nil) | |
5 | + super | |
6 | + set_backtrace(backtrace) | |
7 | + end | |
8 | + | |
9 | + def message | |
10 | + 'Dummy Exception Message' | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + class DummyController | |
15 | + def controller_name | |
16 | + 'DummyController' | |
17 | + end | |
18 | + | |
19 | + def action_name | |
20 | + 'TestAction' | |
21 | + end | |
22 | + end | |
23 | +end | ... | ... |
1 | +++ a/spec/spec_helper.rb | |
... | ... | @@ -0,0 +1,20 @@ |
1 | +require "bundler/setup" | |
2 | +require "exception_file_notifier" | |
3 | +require 'exception_notification' | |
4 | + | |
5 | +if RUBY_VERSION >= '2.7.2' | |
6 | + # NOTE: https://bugs.ruby-lang.org/issues/17000 | |
7 | + # this will keep us informed of deprecation warnings after Ruby 2.7.2 | |
8 | + Warning[:deprecated] = true | |
9 | +end | |
10 | + | |
11 | +RSpec.configure do |config| | |
12 | + # Enable flags like --only-failures and --next-failure | |
13 | + config.example_status_persistence_file_path = ".rspec_status" | |
14 | + | |
15 | + config.expect_with :rspec do |c| | |
16 | + c.syntax = :expect | |
17 | + end | |
18 | +end | |
19 | + | |
20 | +# ENV["RAILS_ENV"] = "test" | ... | ... |