misfit 0.0.1
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.
- data/CHANGELOG +2 -0
- data/MIT-LICENSE +24 -0
- data/README.md +130 -0
- data/Rakefile +33 -0
- data/lib/misfit.rb +61 -0
- data/lib/misfit/version.rb +3 -0
- data/spec/misfit_spec.rb +140 -0
- data/spec/spec_helper.rb +9 -0
- metadata +96 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
data/lib/misfit.rb
ADDED
@@ -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
|
data/spec/misfit_spec.rb
ADDED
@@ -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
|
+
|
data/spec/spec_helper.rb
ADDED
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
|