rescue-any 0.1.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/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - jruby-19mode
5
+ - rbx-19mode
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.1.1 (09/10/2013)
2
+
3
+ * First, experimental release - [@erik-eide](https://github.com/erik-eide).
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'rspec'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Erik Eide
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ RescueAny
2
+ ==========
3
+
4
+ [![Build Status](https://travis-ci.org/erik-eide/rescue-any.png)](https://travis-ci.org/erik-eide/rescue-any) [![Code Climate](https://codeclimate.com/github/erik-eide/rescue-any.png)](https://codeclimate.com/github/erik-eide/rescue-any)
5
+
6
+ An experimental library for declarative exception handling in Ruby.
7
+
8
+ Inspired by Rails's declarative [rescue_from](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/rescuable.rb)
9
+ method, this is a bit of an experimental attempt to try something similar that can be used with any Ruby class, as well as to just mess
10
+ around with a little meta-programming.
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ class RemoteUser
16
+ include RescueAny
17
+
18
+ rescue_any RuntimeError, on: [:create, :update, :delete], with: lambda {|ex| puts 'ohoh: ' + ex.message }
19
+
20
+ def create
21
+ raise RuntimeError.new("raised by #create")
22
+ end
23
+
24
+ def update
25
+ raise RuntimeError.new("raised by #update")
26
+ end
27
+
28
+ def delete
29
+ raise RuntimeError.new("raised by #delete")
30
+ end
31
+ end
32
+ ```
33
+
34
+ ## Contributing
35
+
36
+ Feel free to contribute or correct, just:
37
+
38
+ * Fork this project.
39
+ * Write specs, make them pass.
40
+ * Updated [CHANGELOG](CHANGELOG.md).
41
+ * Make a pull request, bonus for topic branches.
42
+
43
+ ## Copyright and License
44
+
45
+ Copyright (c) 2013, Erik Eide and [Contributors](CHANGELOG.md).
46
+
47
+ This project is licensed under the [MIT License](LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/gem_tasks'
3
+
4
+ Bundler.setup(:default, :development)
5
+
6
+ require 'rspec/core'
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |spec|
10
+ spec.pattern = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ module RescueAny
2
+ VERSION = '0.1.1'
3
+ end
data/lib/rescue_any.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'rescue_any/version'
2
+
3
+ module RescueAny
4
+
5
+ def self.included(base)
6
+ class << base
7
+ attr_accessor :rescue_statements
8
+ end
9
+
10
+ base.rescue_statements = {}
11
+ base.extend ClassMethods
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ # Rescue exceptions raised by an object's instance methods.
17
+ #
18
+ # <tt>rescue_any</tt> receives an exception class, and a trailing hash
19
+ # containing a <tt>on:</tt> option listing one or more method names to
20
+ # apply the rescue to, and a <tt>with:</tt> option containing a code block
21
+ # for handling the exception.
22
+ #
23
+ # The handling block can receive either zero or one argument, in the latter,
24
+ # it will be passed the rescued exception.
25
+ #
26
+ # class RemoteUser
27
+ # include RescueAny
28
+ #
29
+ # rescue_any RuntimeError, on: [:create, :update, :delete], with: lambda {|ex| puts 'ohoh: ' + ex.message }
30
+ #
31
+ # def create
32
+ # raise RuntimeError.new("raised by #create")
33
+ # end
34
+ #
35
+ # def update
36
+ # raise RuntimeError.new("raised by #update")
37
+ # end
38
+ #
39
+ # def delete
40
+ # raise RuntimeError.new("raised by #delete")
41
+ # end
42
+ # end
43
+ #
44
+ # Exceptions raised inside the handler are not caught and will propogate
45
+ # up to the client class.
46
+ def rescue_any(exception_klass, options = {})
47
+
48
+ unless exception_klass.is_a?(Class) && exception_klass <= Exception
49
+ raise ArgumentError.new('First argument must be an Exception or sub-class of Exception')
50
+ end
51
+
52
+ unless options[:with]
53
+ raise ArgumentError.new('Expected a handler, supply an option hash that as :with key as an argument')
54
+ end
55
+
56
+ method_block = options[:with]
57
+ method_names = [options.fetch(:on, [])].flatten
58
+
59
+ method_names.each do |method_name|
60
+ rescue_statements[method_name] ||= {}
61
+ rescue_statements[method_name][exception_klass] = method_block
62
+ end
63
+ end
64
+
65
+ def method_added(method_name)
66
+ if rescue_statements.keys.include?(method_name)
67
+ alias_method_name = "#{method_name}_without_rescue_any".to_sym
68
+
69
+ unless self.instance_methods.include?(alias_method_name)
70
+ self.send(:alias_method, alias_method_name, method_name)
71
+ self.send(:define_method, method_name) do
72
+ begin
73
+ self.send(alias_method_name)
74
+ rescue Exception => ex
75
+ exception_handled = false
76
+
77
+ exceptions_and_rescue_blocks = self.class.instance_eval { rescue_statements[method_name] }
78
+ exceptions_and_rescue_blocks.keys.each do |rescue_exception|
79
+ if ex.instance_of? rescue_exception
80
+ self.class.instance_eval do
81
+ rescue_handler = rescue_statements[method_name][rescue_exception]
82
+ rescue_handler.arity != 0 ? rescue_handler.call(ex) : rescue_handler.call
83
+ end
84
+ exception_handled = true
85
+ break
86
+ end
87
+ end
88
+
89
+ raise ex unless exception_handled
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,16 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "rescue_any/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rescue-any"
6
+ s.version = RescueAny::VERSION
7
+ s.authors = [ "Erik Eide" ]
8
+ s.email = "gdayerik@facebook.com"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.required_rubygems_version = '>= 1.3.6'
11
+ s.files = `git ls-files`.split("\n")
12
+ s.require_paths = [ "lib" ]
13
+ s.homepage = "http://github.com/erik-eide/rescue-any"
14
+ s.licenses = [ "MIT" ]
15
+ s.summary = "Declarative exception handling for Ruby"
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe RescueAny do
4
+ it "has a version" do
5
+ RescueAny::VERSION.should_not be_nil
6
+ end
7
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ describe RescueAny do
4
+
5
+ let(:subject_class) { Class.new }
6
+ let(:subject) { subject_class.new }
7
+
8
+ before :each do
9
+ subject_class.class_eval do
10
+ include RescueAny
11
+ end
12
+ end
13
+
14
+ describe '.rescue_any' do
15
+ context 'without an exception' do
16
+ it 'raises an ArgumentError for nil' do
17
+ expect {
18
+ subject_class.class_eval do
19
+ rescue_any nil, :on => :create, :with => lambda { |ex| "#{ex} caught by rescue_any" }
20
+ end
21
+ }.to raise_error ArgumentError
22
+ end
23
+
24
+ it 'raises an ArgumentError for something other than a class' do
25
+ expect {
26
+ subject_class.class_eval do
27
+ rescue_any 'StandardError', :on => :create, :with => lambda { |ex| "#{ex} caught by rescue_any" }
28
+ end
29
+ }.to raise_error ArgumentError
30
+ end
31
+
32
+ it 'raises an ArgumentError for something other than an Exception or sub-class' do
33
+ expect {
34
+ subject_class.class_eval do
35
+ rescue_any Object, :on => :create, :with => lambda { |ex| "#{ex} caught by rescue_any" }
36
+ end
37
+ }.to raise_error ArgumentError
38
+ end
39
+ end
40
+
41
+ context 'for one method' do
42
+ before :each do
43
+ subject_class.class_eval do
44
+ rescue_any StandardError, :on => :create, :with => lambda { |ex| "#{ex} caught by rescue_any" }
45
+ end
46
+ end
47
+
48
+ it 'will rescue the given exception' do
49
+ subject_class.class_eval do
50
+ def create
51
+ raise StandardError.new("Raised by #create")
52
+ end
53
+ end
54
+
55
+ expect { subject.create }.to_not raise_error
56
+ end
57
+
58
+ it 'will not rescue other exception types' do
59
+ subject_class.class_eval do
60
+ def create
61
+ raise ArgumentError.new("ArgumentError raised by #create")
62
+ end
63
+ end
64
+
65
+ expect { subject.create }.to raise_error
66
+ end
67
+ end
68
+
69
+ context 'for a list of methods' do
70
+ before :each do
71
+ subject_class.class_eval do
72
+ rescue_any StandardError, :on => [:create, :update], :with => lambda { |ex| "#{ex} caught by rescue_any" }
73
+ end
74
+ end
75
+
76
+ it 'will rescue the given exception' do
77
+ subject_class.class_eval do
78
+ def create
79
+ raise StandardError.new("Raised by #create")
80
+ end
81
+
82
+ def update
83
+ raise StandardError.new("Raised by #update")
84
+ end
85
+ end
86
+
87
+ expect { subject.create }.to_not raise_error
88
+ expect { subject.update }.to_not raise_error
89
+ end
90
+ end
91
+
92
+ context 'when you forget to specify any methods' do
93
+ before :each do
94
+ subject_class.class_eval do
95
+ rescue_any StandardError, :with => lambda { |ex| "#{ex} caught by rescue_any" }
96
+
97
+ def create
98
+ raise StandardError.new("Raised by #create")
99
+ end
100
+
101
+ def update
102
+ raise StandardError.new("Raised by #update")
103
+ end
104
+ end
105
+ end
106
+
107
+ it "just won't rescue anything" do
108
+ expect { subject.create }.to raise_error
109
+ expect { subject.update }.to raise_error
110
+ end
111
+ end
112
+
113
+ context "with a lambda handler" do
114
+ it "it receives an invocation" do
115
+ expect(Kernel).to receive(:puts)
116
+
117
+ subject_class.class_eval do
118
+ rescue_any StandardError, :on => :create, :with => lambda { |ex| Kernel.puts(ex.message) }
119
+
120
+ def create
121
+ raise StandardError.new("Raised by #create")
122
+ end
123
+ end
124
+
125
+ expect { subject.create }.to_not raise_error
126
+ end
127
+
128
+ it 'propagates exceptions from within the handler' do
129
+ subject_class.class_eval do
130
+ rescue_any StandardError, :on => :create, :with => lambda { raise Exception.new("Raised by .rescue_any handler") }
131
+
132
+ def create
133
+ raise StandardError.new("Raised by #create")
134
+ end
135
+ end
136
+
137
+ expect { subject.create }.to raise_error Exception
138
+ end
139
+ end
140
+
141
+ context 'without a handler' do
142
+ it 'raises an argument error' do
143
+ expect {
144
+ subject_class.class_eval do
145
+ rescue_any StandardError, :on => :create
146
+ end
147
+ }.to raise_error ArgumentError
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'rescue_any'
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rescue-any
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Erik Eide
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-09-14 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email: gdayerik@facebook.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - .gitignore
31
+ - .rspec
32
+ - .travis.yml
33
+ - CHANGELOG.md
34
+ - Gemfile
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - lib/rescue_any.rb
39
+ - lib/rescue_any/version.rb
40
+ - rescue_any.gemspec
41
+ - spec/rescue_any/version_spec.rb
42
+ - spec/rescue_any_spec.rb
43
+ - spec/spec_helper.rb
44
+ homepage: http://github.com/erik-eide/rescue-any
45
+ licenses:
46
+ - MIT
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 23
67
+ segments:
68
+ - 1
69
+ - 3
70
+ - 6
71
+ version: 1.3.6
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.26
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Declarative exception handling for Ruby
79
+ test_files: []
80
+