failsafe 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ .rspec
2
+ Gemfile.lock
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - ree
4
+ - ruby-head
5
+ - rbx
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+ gem 'rake'
5
+
6
+ group :test do
7
+ gem 'rspec', ">= 2.7"
8
+ gem 'mocha'
9
+ end
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Alex Sharp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,64 @@
1
+ [![Build Status](https://secure.travis-ci.org/zaarly/failsafe.png?branch=master)](http://travis-ci.org/zaarly/failsafe)
2
+
3
+ ## What
4
+
5
+ Failsafe is a tiny library that makes it easy to make sure non-critical exceptions
6
+ don't interrupt program flow. It uses a failure handling system heavily inspired
7
+ by that of [Resque](http://github.com/defunkt/resque).
8
+
9
+ You can use failsafe to suppress exceptions from your users, but continue to deliver
10
+ them to your errors backends (log files, airbrake, etc).
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ Failsafe.error_backends << Failsafe::Backends::Stderr
16
+
17
+ class MyApp < Sinatra::Base
18
+ get '/' do
19
+ Failsafe.failsafe { track_some_metrics; raise("boom!") }
20
+ status(200)
21
+ end
22
+ end
23
+ ```
24
+
25
+ ## Disabling Failsafe
26
+
27
+ In production, you want your app to keep ticking along when certain exceptions
28
+ occur without missing a beat. However, in development and test environments,
29
+ you probably want to know that exceptions are occurring.
30
+ Use the `disabled` property for that:
31
+
32
+ ```ruby
33
+ if Rails.env.test?
34
+ Failsafe.disabled = true
35
+ else
36
+ # not necessary, this is the default setting
37
+ Failsafe.disabled = false
38
+ end
39
+ ```
40
+
41
+ ## Configuration
42
+
43
+ Failsafe comes with 3 error backends by default: Airbrake, File, and Stderr.
44
+ You must add them to the failsafe configuration in order to log exceptions
45
+ to them:
46
+
47
+ ```ruby
48
+ Failsafe.error_backends << Failsafe::Backends::Airbrake
49
+ Failsafe.error_backends << Failsafe::Backends::Stderr
50
+ ```
51
+
52
+ ## Backends
53
+
54
+ "Backends" are what your exceptions get handled by instead of letting them
55
+ bubble up to the user. When an exception occurs within a piece of code wrapped
56
+ by failsafe, the exception object is handed to each error backend.
57
+
58
+ Failsafe ships with three error backends by default:
59
+
60
+ * Airbrake - Send errors to airbrake
61
+ * Stderr - Send errors to stderr
62
+ * File - Send errors to a log file
63
+
64
+ Note: The File backend logs to a log file in the log directory called failsafe_errors.log.
@@ -0,0 +1,5 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ task :default => :spec
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "failsafe/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "failsafe"
7
+ s.version = Failsafe::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Alex Sharp"]
10
+ s.email = ["ajsharp@gmail.com"]
11
+ s.homepage = "https://github.com/zaarly/failsafe"
12
+ s.summary = %q{Tiny little library for silently handling errors so they don't interrupt program flow.}
13
+ s.description = %q{}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'logger'
2
+
3
+ require 'failsafe/failure'
4
+
5
+ module Failsafe
6
+
7
+ # Attach error backends to be fire when errors occur.
8
+ #
9
+ # @example Adding multiple backends
10
+ # Failsafe::Config.error_backends << Failsafe::Backends::Airbrake
11
+ # Failsafe::Config.error_backends << Failsafe::Backends::Stderr
12
+ def self.error_backends
13
+ @backends ||= []
14
+ end
15
+
16
+ @@disabled = false
17
+
18
+ # Determines if failsafe should rescue errors or let them bubble up.
19
+ #
20
+ # @default false
21
+ # @return [Boolean]
22
+ def self.disabled; @@disabled; end
23
+ def self.disabled?; disabled; end
24
+
25
+ def self.disabled=(val)
26
+ @@disabled = val
27
+ end
28
+
29
+ # Wraps code in a begin..rescue and delivers exceptions
30
+ # to the configured error backends.
31
+ #
32
+ # @todo make this threadsafe
33
+ def self.failsafe
34
+ # Let errors bubble up if failsafe has been disabled
35
+ if disabled?
36
+ return yield
37
+ end
38
+
39
+ begin
40
+ yield
41
+ rescue => e
42
+ error_backends.each do |backend|
43
+ backend.new(e).save
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ module Failsafe
2
+ module Backends
3
+
4
+ # Failure backend to send errors to Airbrake
5
+ class Airbrake < Base
6
+ def save
7
+ ::Airbrake.notify_or_ignore(exception)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Failsafe
2
+ module Backends
3
+ class Base
4
+ attr_accessor :exception
5
+
6
+ def initialize(exception)
7
+ @exception = exception
8
+ end
9
+
10
+ # Implement in subclasses
11
+ def save
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Failsafe
2
+ module Backends
3
+
4
+ # File failure backend. Writes exception backtraces to a logfile.
5
+ class File < Base
6
+ def self.logger
7
+ @logger ||= ::Logger.new(::File.join(Rails.root, 'log', 'failsafe_errors.log')).tap { |l| l.formatter = Logger::Formatter.new }
8
+ end
9
+
10
+ def save
11
+ msg = []
12
+ msg << exception.message
13
+ msg << exception.backtrace.join("\n")
14
+ self.class.logger.error(msg.join)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+
2
+ module Failsafe
3
+ module Backends
4
+
5
+ # Failure backend to log errors to stderr
6
+ class Stderr < Base
7
+ def self.logger
8
+ @logger ||= ::Logger.new($stderr).tap { |l| l.formatter = Logger::Formatter.new }
9
+ end
10
+
11
+ def save
12
+ msg = []
13
+ msg << exception.message
14
+ msg << exception.backtrace.join("\n")
15
+ self.class.logger.error(msg.join)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Failsafe
2
+ module Backends
3
+ autoload :Base, 'failsafe/backends/base'
4
+
5
+ autoload :Airbrake, 'failsafe/backends/airbrake'
6
+ autoload :Stderr, 'failsafe/backends/stderr'
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Failsafe
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Failsafe::Backends::Base do
4
+ subject { Failsafe::Backends::Base.new(Exception.new) }
5
+
6
+ it { should respond_to :save }
7
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Failsafe::Backends::Stderr do
4
+ let!(:stderr_bucket) { $stderr }
5
+ let(:exception) { RuntimeError.new("Oh noez!") }
6
+
7
+ subject { Failsafe::Backends::Stderr.new(exception) }
8
+
9
+ before do
10
+ $stderr = StringIO.new
11
+ Failsafe::Backends::Stderr.stubs(:logger => ::Logger.new($stderr))
12
+ exception.set_backtrace(["#{__FILE__}:6"])
13
+ subject.save
14
+ $stderr.rewind
15
+ end
16
+
17
+ after { $stderr = stderr_bucket }
18
+
19
+ it "logs something to stderr when a exception is raised" do
20
+ $stderr.read.should =~ /Oh noez!/
21
+ end
22
+
23
+ it "logs the backtrace" do
24
+ $stderr.read.should include "stderr_spec.rb:6"
25
+ end
26
+ end
27
+
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe "configuring error backends" do
4
+ it "adds the error backend class to the config" do
5
+ some_backend_class = Struct.new(:save)
6
+ Failsafe.error_backends << some_backend_class
7
+ Failsafe.error_backends.should include some_backend_class
8
+ end
9
+ end
10
+
11
+ describe Failsafe, ".failsafe" do
12
+ class DangerousClass
13
+ def boom_boom
14
+ raise RuntimeError.new "room!"
15
+ end
16
+ end
17
+
18
+ before { Failsafe.error_backends << MockFailureBackend }
19
+ let(:danger) { DangerousClass.new }
20
+
21
+ subject { Failsafe.failsafe { danger.boom_boom } }
22
+
23
+ it "does not raise an error when wrapped with failsafe" do
24
+ expect {
25
+ subject
26
+ }.not_to raise_error
27
+ end
28
+
29
+ it "notifies backends of the exception" do
30
+ MockFailureBackend.any_instance.expects(:save)
31
+ subject
32
+ end
33
+ end
34
+
35
+ describe "disabling failsafe" do
36
+ it "is not disabled by default" do
37
+ Failsafe.should_not be_disabled
38
+ end
39
+
40
+ context "when false" do
41
+ before { Failsafe.disabled = false }
42
+
43
+ it "allows exception to bubble up" do
44
+ expect {
45
+ Failsafe.failsafe { raise 'boom' }
46
+ }.not_to raise_error
47
+ end
48
+ end
49
+
50
+ context "when true" do
51
+ before { Failsafe.disabled = true }
52
+
53
+ it "allows errors to bubble up" do
54
+ expect {
55
+ Failsafe.failsafe { raise 'boom' }
56
+ }.to raise_error
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,12 @@
1
+ $:.push(File.expand_path(File.dirname(__FILE__)))
2
+ $:.push(File.expand_path(File.dirname(__FILE__)) + '/../lib')
3
+
4
+ require 'failsafe'
5
+
6
+ class MockFailureBackend < Failsafe::Backends::Base
7
+ end
8
+
9
+ RSpec.configure do |config|
10
+ config.mock_with :mocha
11
+ config.after { Failsafe.error_backends.clear }
12
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: failsafe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alex Sharp
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ''
15
+ email:
16
+ - ajsharp@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rvmrc
23
+ - .travis.yml
24
+ - Gemfile
25
+ - MIT-LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - failsafe.gemspec
29
+ - lib/failsafe.rb
30
+ - lib/failsafe/backends/airbrake.rb
31
+ - lib/failsafe/backends/base.rb
32
+ - lib/failsafe/backends/file.rb
33
+ - lib/failsafe/backends/stderr.rb
34
+ - lib/failsafe/failure.rb
35
+ - lib/failsafe/version.rb
36
+ - spec/failsafe/backends/base_spec.rb
37
+ - spec/failsafe/backends/stderr_spec.rb
38
+ - spec/failsafe_spec.rb
39
+ - spec/spec_helper.rb
40
+ homepage: https://github.com/zaarly/failsafe
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.15
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Tiny little library for silently handling errors so they don't interrupt
64
+ program flow.
65
+ test_files:
66
+ - spec/failsafe/backends/base_spec.rb
67
+ - spec/failsafe/backends/stderr_spec.rb
68
+ - spec/failsafe_spec.rb
69
+ - spec/spec_helper.rb