misfit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ = 0.0.1
2
+ * Initial Release
@@ -0,0 +1,24 @@
1
+ Copyright 2012 Ian White
2
+
3
+ This plugin was developed by Ian White (http://github.com/ianwhite)
4
+ while working at Distinctive Doors (http://distinctivedoors.co.uk)
5
+ who have kindly agreed to release this under the MIT-LICENSE.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,130 @@
1
+ misfit
2
+ ======
3
+
4
+ Flexible approach to handling exceptions in ruby (for library writers, or consumers). Inspired by [Avdi Grim](http://avdi.org/)'s excellent book [Exceptional Ruby](http://exceptionalruby.com/).
5
+
6
+ This was developed by [Ian White](http://github.com/ianwhite) while working at [Distinctive Doors](http://distinctivedoors.co.uk) who have kindly agreed to release this under the MIT-LICENSE.
7
+
8
+ Misfit allows any module to act like a ruby exception class and gives the ability to decorate any standard ruby exception, optionally adding data.
9
+
10
+ Misfit also provides a mechanism to change the error handling policy from raising to something else
11
+
12
+ Why?
13
+ ----
14
+
15
+ For a library writer, this means your library can raise exceptions that will all be caught by 'rescue YourLib::Error',
16
+ but also specific exceptions can be rescued (like rescuing IOErrors, for example) by client code.
17
+
18
+ # Your lib
19
+ module YourLib
20
+ module Error
21
+ include Misfit
22
+ end
23
+
24
+ def self.do_some_work
25
+ Error.wrap { some_calls_that_might_raise_errors_like_io_error }
26
+ if no_good then raise Error, "It's no good"
27
+ end
28
+ end
29
+
30
+ # Consumer of your lib can do this:
31
+ begin
32
+ YourLib.do_some_work
33
+ rescue YourLib::Error
34
+ log 'YourLib just errored'
35
+ end
36
+
37
+ # But they can also rescue specific errors they are interested in, say an IOError
38
+ begin
39
+ YourLib.do_some_work
40
+ rescue IOError
41
+ # open some tubes and retry
42
+ rescue YourLib::Error => e
43
+ # e is some non IOError raised by YourLib
44
+ end
45
+
46
+ For a library consumer, you can write a simple wrapper to isolate a library that raises all manner of errors (perhaps undocumented)
47
+
48
+ module ErrorByOtherLib
49
+ include Misfit
50
+ end
51
+
52
+ def call_out_to_other_lib
53
+ ErrorByOtherLib.wrap { OtherLib.so_some_stuff }
54
+ end
55
+
56
+ `call_out_to_other_lib` will now raise errors that are `ErrorByOtherLib` as well as retaining their original identity (so an IOError can still be
57
+ rescued, but you can now rescue all errors raised by OtherLib).
58
+
59
+ Quacks like an exception
60
+ ------------------------
61
+
62
+ When you create a misfit exception module, it can be used as if it was an exception class. For example (the `YourLib:Error` example above)
63
+
64
+ raise YourLib::Error, "an error"
65
+ raise YourLib::Error.new("an error")
66
+ YourLib::Error.exception(<message>, <backtrace>)
67
+
68
+ all work as if `YourLib::Error` was a ruby Exception class.
69
+
70
+ Niceties
71
+ --------
72
+
73
+ You can set the default basic Ruby exception class, and you can set up the equivalent of an inheritance hierarchy by simply including from the parent Exception:
74
+
75
+ module YourLib
76
+ module Error
77
+ include Misfit
78
+ end
79
+
80
+ module NotFoundError
81
+ include Error
82
+ exception_class IndexError
83
+ end
84
+ end
85
+
86
+ `raise YourLib::NotFoundError` will be rescued by `YourLib::NotFoundError`, `YourLib::Error`, and `IndexError`
87
+
88
+ You can also optionally wrap and add data to any Error:
89
+
90
+ e = YourLib::Error.wrap RuntimeError.new("Bad stuff"), {some: 'data'}
91
+ e.is_a?(RuntimeError) # => true
92
+ e.is_a?(YourLib::Error) # => true
93
+ e.data # => {some: 'data'}
94
+
95
+ `YourLib::Error.wrap &block` causes all raised exceptions to be wrapped as `YourLib::Error`
96
+
97
+ Other Examples of use:
98
+ ----------------------
99
+
100
+ module MyError
101
+ include Misfit
102
+ end
103
+
104
+ raise MyError, "foo"
105
+ # the above will be rescued as a StandardError, and a MyError
106
+
107
+ raise MyError.wrap ArgumentError.new('Bad args')
108
+ # the above will be rescued as an ArgumentError, and a MyError
109
+
110
+ MyError.wrap do
111
+ # some stuff that might raise errors
112
+ end
113
+ # any errors raised will also be MyErrors
114
+
115
+ Adding data to exceptions
116
+
117
+ MyError.new 'foo', {some: data}
118
+ # this will be a MyError with #data => {some: data}
119
+
120
+ MyError.wrap exception, {some: data}
121
+ # the resulting exception will be a MyError, and have #data attribute of {some: data}
122
+
123
+ # set up an error that has a different class as its base (like IOError for example)
124
+ module MyIOError
125
+ include MyError
126
+ exception_class IOError
127
+ end
128
+
129
+ # the resulting error will be an IOError, extended with MyIOError, and MyError, and will be rescued as such
130
+ raise MyIOError
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Misfit'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.md', 'CHANGELOG', 'MIT-LICENSE')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ require 'rspec/core/rake_task'
26
+
27
+ RSpec::Core::RakeTask.new(:spec)
28
+
29
+ desc "Run the specs with simplecov"
30
+ task :simplecov => [:simplecov_env, :spec]
31
+ task :simplecov_env do ENV['SIMPLECOV'] = '1' end
32
+
33
+ task :default => :spec
@@ -0,0 +1,61 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext'
3
+
4
+ module Misfit
5
+ extend ActiveSupport::Concern
6
+
7
+ included do inherit_from_misfit end
8
+
9
+ def self.exception_class
10
+ StandardError
11
+ end
12
+
13
+ attr_accessor :data
14
+
15
+ module ClassMethods
16
+ # when called with an exception, makes the exception a <self>
17
+ # when called with a block, any exceptions raised in the block will be wrapped as <self>
18
+ def wrap *args, &block
19
+ if block
20
+ wrap_block &block
21
+ else
22
+ wrap_exception *args
23
+ end
24
+ end
25
+
26
+ def new message, data = nil, *backtrace
27
+ wrap_exception exception_class.exception(message, *backtrace), data
28
+ end
29
+
30
+ def exception message, *args
31
+ new message, nil, *args
32
+ end
33
+
34
+ def exception_class klass = nil
35
+ @exception_class = klass if klass
36
+ @exception_class
37
+ end
38
+
39
+ private
40
+ def wrap_exception exception, data = nil
41
+ exception.tap do |e|
42
+ e.extend self unless e.is_a?(self)
43
+ e.data = data if data
44
+ end
45
+ end
46
+
47
+ def wrap_block
48
+ yield
49
+ rescue Exception => exception
50
+ raise wrap_exception(exception)
51
+ end
52
+
53
+ # when an exception module is included, set up this module
54
+ # as an exception module itself, and 'inherit' the exception_class
55
+ def inherit_from_misfit
56
+ extend ActiveSupport::Concern
57
+ exception_class ancestors[1].exception_class
58
+ included do inherit_from_misfit end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Misfit
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ module MisfitSpec
4
+ #
5
+ # Error
6
+ # +- FooError (RuntimeError)
7
+ # +- FazError (ArgumentError)
8
+ # +- BarError
9
+ module Error
10
+ include Misfit
11
+ end
12
+
13
+ module FooError
14
+ include Error
15
+ exception_class RuntimeError
16
+ end
17
+
18
+ module FazError
19
+ include FooError
20
+ exception_class IOError
21
+ end
22
+
23
+ module BarError
24
+ include FazError
25
+ end
26
+
27
+ describe Misfit do
28
+ describe 'Error (includes Misfit)' do
29
+ subject { exception_module }
30
+
31
+ let(:exception_module) { Error }
32
+ let(:message) { 'message' }
33
+ let(:data) { 'exception data' }
34
+
35
+ describe 'standard idioms' do
36
+ describe 'raise <exception_module>, <message>' do
37
+ subject { raise exception_module, message }
38
+
39
+ it { expect{ subject }.to raise_error(StandardError, message) }
40
+ it { expect{ subject }.to raise_error(exception_module, message) }
41
+ end
42
+
43
+ describe 'raise <exception_module>.new(<message>)' do
44
+ subject { raise exception_module.new(message) }
45
+
46
+ it { expect{ subject }.to raise_error(StandardError, message) }
47
+ it { expect{ subject }.to raise_error(exception_module, message) }
48
+ end
49
+
50
+ describe 'raise <exception_module>.exception(<message>)' do
51
+ subject { raise exception_module.exception(message) }
52
+
53
+ it { expect{ subject }.to raise_error(StandardError, message) }
54
+ it { expect{ subject }.to raise_error(exception_module, message) }
55
+ end
56
+
57
+ describe 'FooError (includes Error, exception_class RuntimeError)' do
58
+ let(:exception_module) { FooError }
59
+
60
+ describe 'raise <exception_module>, <message>' do
61
+ subject { raise exception_module, message }
62
+
63
+ it { expect{ subject }.to raise_error(Error, message) }
64
+ it { expect{ subject }.to raise_error(FooError, message) }
65
+ it { expect{ subject }.to raise_error(RuntimeError, message) }
66
+ end
67
+ end
68
+
69
+ describe 'FazError (includes FooError, exception_class IOError)' do
70
+ let(:exception_module) { FazError }
71
+
72
+ describe 'raise <exception_module>, <message>' do
73
+ subject { raise exception_module, message }
74
+
75
+ it { expect{ subject }.to raise_error(Misfit, message) }
76
+ it { expect{ subject }.to raise_error(Error, message) }
77
+ it { expect{ subject }.to raise_error(FooError, message) }
78
+ it { expect{ subject }.to raise_error(FazError, message) }
79
+ it { expect{ subject }.to raise_error(IOError, message) }
80
+ end
81
+ end
82
+
83
+ describe 'BarError (includes FazError)' do
84
+ let(:exception_module) { BarError }
85
+
86
+ describe 'raise <exception_module>, <message>' do
87
+ subject { raise exception_module, message }
88
+
89
+ it { expect{ subject }.to raise_error(Error, message) }
90
+ it { expect{ subject }.to raise_error(FooError, message) }
91
+ it { expect{ subject }.to raise_error(FazError, message) }
92
+ it { expect{ subject }.to raise_error(BarError, message) }
93
+ it { expect{ subject }.to raise_error(IOError, message) }
94
+ end
95
+ end
96
+ end
97
+
98
+ describe 'adding data' do
99
+ describe 'raise <exception_module>.new <message>, <data>' do
100
+ subject { raise exception_module.new(message, data) }
101
+
102
+ it "should have data added to the exception" do
103
+ begin
104
+ subject
105
+ rescue => e
106
+ e.data.should == data
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#wrap' do
113
+ describe 'RuntimeError.new(<message>)' do
114
+ subject { exception_module.wrap RuntimeError.new(message) }
115
+
116
+ it { should be_a RuntimeError }
117
+ it { should be_a exception_module }
118
+ its(:message) { should == message }
119
+ end
120
+
121
+ describe 'RuntimeError.new(<message>), <exception data>' do
122
+ subject { exception_module.wrap RuntimeError.new(message), data }
123
+
124
+ it { should be_a RuntimeError }
125
+ it { should be_a exception_module }
126
+ its(:message) { should == message }
127
+ its(:data) { should == data }
128
+ end
129
+
130
+ describe '{ some_error_ridden_code! }' do
131
+ subject { exception_module.wrap do some_error_ridden_code! end }
132
+
133
+ it { expect{ subject }.to raise_error(NoMethodError, /some_error_ridden_code!/) }
134
+ it { expect{ subject }.to raise_error(exception_module, /some_error_ridden_code!/) }
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+
@@ -0,0 +1,9 @@
1
+ if ENV['SIMPLECOV']
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "_spec.rb"
5
+ end
6
+ end
7
+
8
+ require 'rspec'
9
+ require_relative '../lib/misfit'
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: misfit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ian White
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &70193682798820 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70193682798820
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70193682798160 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70193682798160
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70193682797400 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '2'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70193682797400
47
+ description: Flexible approach to handling exceptions in ruby (for library writers,
48
+ or consumers). Ispired by Avdi Grimm's excellent book 'Exceptional Ruby'.
49
+ email:
50
+ - ian.w.white@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - lib/misfit/version.rb
56
+ - lib/misfit.rb
57
+ - MIT-LICENSE
58
+ - Rakefile
59
+ - README.md
60
+ - CHANGELOG
61
+ - spec/misfit_spec.rb
62
+ - spec/spec_helper.rb
63
+ homepage: http://github.com/i2w/misfit
64
+ licenses: []
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ segments:
76
+ - 0
77
+ hash: 1488499911703821850
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ segments:
85
+ - 0
86
+ hash: 1488499911703821850
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.15
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Flexible approach to handling exceptions in ruby (for library writers, or
93
+ consumers)
94
+ test_files:
95
+ - spec/misfit_spec.rb
96
+ - spec/spec_helper.rb