remotely_exceptional 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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +6 -0
- data/Gemfile +16 -0
- data/Guardfile +7 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +9 -0
- data/lib/remotely_exceptional.rb +11 -0
- data/lib/remotely_exceptional/exceptions.rb +12 -0
- data/lib/remotely_exceptional/handler.rb +91 -0
- data/lib/remotely_exceptional/handlers/prioritized_handler.rb +178 -0
- data/lib/remotely_exceptional/remote_handling.rb +42 -0
- data/lib/remotely_exceptional/version.rb +4 -0
- data/remotely_exceptional.gemspec +23 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/exceptions_test.rb +22 -0
- data/test/unit/handler_test.rb +135 -0
- data/test/unit/handlers/prioritized_handler_test.rb +297 -0
- data/test/unit/remote_handling_test.rb +110 -0
- data/test/unit/remotely_exceptional_test.rb +13 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5db0afaa461aa92d936cae11184f651757c23e50
|
4
|
+
data.tar.gz: 6b8dfd4d4a3ff0c8602e435126de50e8ccc5dc70
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5697edf8df114e0a6c5c1953f76ae740e23dd69e6fc552dda3198e03e14a3a198f57d2e43f70be73fc5410afe2fc969333a20162d4190e333eb8160ac47fd084
|
7
|
+
data.tar.gz: 2a3ed81301611e4cff979f3a20b7ad7335d749b7751e7066a0cb95c70493334350f9d32b30b6f6245eb088a82ec7185229240d008071f72fe78a81b60622602b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :doc do
|
6
|
+
gem "yard"
|
7
|
+
end
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem "coveralls", :require => false
|
11
|
+
gem "guard"
|
12
|
+
gem "guard-minitest"
|
13
|
+
gem "minitest", ">= 3.0"
|
14
|
+
gem "mocha"
|
15
|
+
gem "simplecov", :require => false
|
16
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
gem_name = File.basename(Dir[File.expand_path("../*.gemspec", __FILE__)].first)[0..-9]
|
2
|
+
|
3
|
+
guard(:minitest, :all_after_pass => false, :all_on_start => false) do
|
4
|
+
watch(%r{^lib/#{gem_name}/(.+)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
|
5
|
+
watch(%r{^test/.+_test\.rb$})
|
6
|
+
watch(%r{^(?:test/test_helper(.*)|lib/#{gem_name})\.rb$}) { "test" }
|
7
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Danny Guinther
|
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 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,
|
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 THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# RemotelyExceptional
|
2
|
+
[](http://badge.fury.io/rb/remotely_exceptional)
|
3
|
+
[](http://www.rubydoc.info/gems/remotely_exceptional)
|
4
|
+
[](https://travis-ci.org/tdg5/remotely_exceptional)
|
5
|
+
[](https://coveralls.io/r/tdg5/remotely_exceptional)
|
6
|
+
[](https://codeclimate.com/github/tdg5/remotely_exceptional)
|
7
|
+
[](https://gemnasium.com/tdg5/remotely_exceptional)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "remotely_exceptional"
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$ bundle
|
21
|
+
```
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ gem install remotely_exceptional
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it ( https://github.com/tdg5/remotely_exceptional/fork )
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "remotely_exceptional/version"
|
2
|
+
require "remotely_exceptional/exceptions"
|
3
|
+
require "remotely_exceptional/handler"
|
4
|
+
require "remotely_exceptional/remote_handling"
|
5
|
+
|
6
|
+
# The namespace for the RemotelyExceptional gem.
|
7
|
+
module RemotelyExceptional
|
8
|
+
# The namespace that specialized handlers live in.
|
9
|
+
module Handlers
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Mixin providing basic functionality required for matching and handling
|
2
|
+
# exceptions.
|
3
|
+
module RemotelyExceptional::Handler
|
4
|
+
# Actions that will be taken on any object that includes this module.
|
5
|
+
#
|
6
|
+
# @param includer [Class,Module] The class or module that has included this
|
7
|
+
# module.
|
8
|
+
def self.included(includer)
|
9
|
+
includer.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Factory function for creating classes with Handler behaviors. Creates a new
|
13
|
+
# class with Handler behaviors from the given super class and block. By
|
14
|
+
# default the super class of the new class will be Object. The given block
|
15
|
+
# will be used as the matcher of the generated class.
|
16
|
+
#
|
17
|
+
# @param super_class [Class] An optional super class to use when creating a
|
18
|
+
# new class with Handler behaviors.
|
19
|
+
# @yieldparam [Exception] exception_instance The exception instance that
|
20
|
+
# should be evaluated for a match.
|
21
|
+
# @yieldreturn [Boolean] A boolean value indicating whether or not the
|
22
|
+
# exception instance was matched.
|
23
|
+
# @return [Class] Returns a new class extended with Handler behaviors.
|
24
|
+
def self.new(super_class = Object, &block)
|
25
|
+
raise ArgumentError, "Block required" unless block_given?
|
26
|
+
handler_class = Class.new(super_class)
|
27
|
+
handler_class.send(:include, self)
|
28
|
+
handler_class.instance_variable_set(:@matcher, block)
|
29
|
+
handler_class
|
30
|
+
end
|
31
|
+
|
32
|
+
# Class-level handler behaviors that will be added to any object that includes
|
33
|
+
# this module.
|
34
|
+
module ClassMethods
|
35
|
+
# Used by Ruby's rescue keyword to evaluate if an exception instance can be
|
36
|
+
# caught by this Class or Module. Delegates to {#matcher}.
|
37
|
+
#
|
38
|
+
# @param exception [Exception] The exception instance that should be evaluated
|
39
|
+
# for a match.
|
40
|
+
# @return [Boolean] Returns a Boolean value indicating whether or not the
|
41
|
+
# exception instance matches this handler.
|
42
|
+
def ===(exception)
|
43
|
+
matcher.call(exception)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Factory method that takes in an exception and an optional Hash of
|
47
|
+
# additional contextual information and creates a new Handler instance from
|
48
|
+
# that data. The generated Handler instance is then used to handle the the
|
49
|
+
# exception.
|
50
|
+
#
|
51
|
+
# @param exception [Exception] The exception to handle. Defaults to $!.
|
52
|
+
# @param context [Hash{Symbol=>Object}] An optional Hash of additional
|
53
|
+
# contextual information about the exception.
|
54
|
+
# @return [Symbol] Returns a symbol indicating what action should be taken
|
55
|
+
# to continue execution. Depending on the situation, valid values include:
|
56
|
+
# [:continue, :raise, :retry]
|
57
|
+
def handle(exception = $!, context = {})
|
58
|
+
instance = new
|
59
|
+
context, exception = exception, $! if exception.is_a?(Hash)
|
60
|
+
instance.instance_variable_set(:@exception, exception)
|
61
|
+
instance.instance_variable_set(:@context, context)
|
62
|
+
instance.handle
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# The block used by the class to evaluate matching exceptions.
|
68
|
+
def matcher
|
69
|
+
@matcher
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :context, :exception
|
74
|
+
|
75
|
+
# Placeholder method, must be implemented by including class. Should
|
76
|
+
# encapsulate the logic required to handle an exception matced by the class.
|
77
|
+
# Should take no arguments.
|
78
|
+
#
|
79
|
+
# @raise [NotImplementedError] Raised when the including class does not
|
80
|
+
# provide it's own #handle instance method.
|
81
|
+
# @return [Symbol] Returns a symbol indicating what action should be taken
|
82
|
+
# to continue execution. Depending on the situation, valid values include:
|
83
|
+
# [:continue, :raise, :retry]
|
84
|
+
# @return [Array<(Symbol, Object)>] Returns a symbol indicating what action
|
85
|
+
# should be taken to continue execution and an object that should be used as
|
86
|
+
# the result of the rescue operation. Depending on the situation, valid
|
87
|
+
# action values include: [:continue, :raise, :retry]
|
88
|
+
def handle
|
89
|
+
raise NotImplementedError, "#{__method__} must be implemented by including class!"
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module RemotelyExceptional::Handlers::PrioritizedHandler
|
2
|
+
# The default priority that should be used when registering handlers.
|
3
|
+
DEFAULT_PRIORITY = 1000.freeze
|
4
|
+
|
5
|
+
# Hash#default_proc for hashes that should return a new Set if a given key is
|
6
|
+
# not defined.
|
7
|
+
HASH_BUILDER = lambda { |hash, key| hash[key] = Set.new }.freeze
|
8
|
+
|
9
|
+
def self.included(includer)
|
10
|
+
includer.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# Determines if any of the available handlers match the provided exception.
|
15
|
+
#
|
16
|
+
# @param exception [Exception] An exception.
|
17
|
+
# @return [Boolean] Returns true if a handler is available that matches the
|
18
|
+
# given exception. Returns false if none of the available handlers match the
|
19
|
+
# given exception.
|
20
|
+
def ===(exception)
|
21
|
+
!!handler_for_exception(exception)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the Hash of block handlers by priority.
|
25
|
+
#
|
26
|
+
# @return [Hash{Integer,Set}] The Hash of priorities and which block handlers
|
27
|
+
# belong to those priorities.
|
28
|
+
def block_handlers
|
29
|
+
Thread.current["#{name}.block_handlers"] ||= Hash.new(&HASH_BUILDER)
|
30
|
+
end
|
31
|
+
|
32
|
+
# The default priority level that should be used for handlers when no priority
|
33
|
+
# is provided.
|
34
|
+
#
|
35
|
+
# @return [Integer] The default priority level.
|
36
|
+
def default_priority
|
37
|
+
const_get(:DEFAULT_PRIORITY)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Finds the handler with the highest priority that matches the exception and
|
41
|
+
# uses this handler to handle the exception. If no handlers are available that
|
42
|
+
# match the given exception, the exception is re-raised.
|
43
|
+
#
|
44
|
+
# @param exception [Exception] The exception to handle.
|
45
|
+
# @param context [Hash{Symbol=>Object}] An optional Hash of additional
|
46
|
+
# contextual information about the exception.
|
47
|
+
# @raise [exception] The given exception is reraised if no handler is found
|
48
|
+
# that matches the given exception.
|
49
|
+
# @return [Symbol] Returns a symbol indicating what action should be taken
|
50
|
+
# to continue execution. Depending on the situation, valid values include:
|
51
|
+
# [:continue, :raise, :retry]
|
52
|
+
def handle(exception = $!, context = {})
|
53
|
+
context, exception = exception, $! if exception.is_a?(Hash)
|
54
|
+
priority_handler = handler_for_exception(exception)
|
55
|
+
raise exception if !priority_handler
|
56
|
+
priority_handler.handle(exception, context)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Finds the handler with the highest priority that matches the given
|
60
|
+
# exception. Returns nil if no matching handler can be found.
|
61
|
+
#
|
62
|
+
# @param exception [Exception] The exception to find a matching handler for.
|
63
|
+
# @return [RemotelyExceptional::Handler] Returns the handler with the
|
64
|
+
# highest priority that matches the given exception. If no handler is found,
|
65
|
+
# returns nil.
|
66
|
+
# @return [nil] Returns nil if no matching handler could be found.
|
67
|
+
def handler_for_exception(exception)
|
68
|
+
prioritized_handlers.detect { |handler| handler === exception }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns an enumerator that yields block handlers and registered handlers in
|
72
|
+
# priority ASC, name ASC order. The collection is lazily generated, so changes
|
73
|
+
# to the sets of handlers may appear during traversal. If consistent state is
|
74
|
+
# necessary, force the returned enumerator to eagerly generate the full
|
75
|
+
# collection using #to_a or similar.
|
76
|
+
#
|
77
|
+
# @return [Enumerator<RemotelyExceptional::Handler>] An enumerator of all
|
78
|
+
# known block handlers and registered handlers in priority ASC, name ASC
|
79
|
+
# order.
|
80
|
+
def prioritized_handlers
|
81
|
+
Enumerator.new do |yielder|
|
82
|
+
priorities = (registered_handlers.keys | block_handlers.keys).sort!
|
83
|
+
priorities.uniq!
|
84
|
+
priorities.each do |priority|
|
85
|
+
if registered_handlers.key?(priority)
|
86
|
+
collected_handlers = registered_handlers[priority].to_a
|
87
|
+
end
|
88
|
+
if block_handlers.key?(priority)
|
89
|
+
temp_handlers = block_handlers[priority].to_a
|
90
|
+
collected_handlers &&= collected_handlers.concat(temp_handlers)
|
91
|
+
collected_handlers ||= temp_handlers
|
92
|
+
end
|
93
|
+
collected_handlers.sort_by!(&:name)
|
94
|
+
collected_handlers.uniq!
|
95
|
+
collected_handlers.each { |handler| yielder << handler }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Adds the given handler to the set of registered handlers. Optionally, a
|
101
|
+
# priority may be supplied. If no priority is supplied the {::default_priority
|
102
|
+
# default priority} is used.
|
103
|
+
#
|
104
|
+
# @param handler [RemotelyExceptional::Handler] The handler that should be
|
105
|
+
# registered.
|
106
|
+
# @param options [Hash{Symbol=>Object}] A Hash of optional arguments.
|
107
|
+
# @option options [Integer] :priority ({::default_priority}) The priority of
|
108
|
+
# the handler.
|
109
|
+
# @return [Boolean] Returns true if the handler was successfully registered
|
110
|
+
# for the given priority. Returns false if the handler was already registered
|
111
|
+
# for the given priority.
|
112
|
+
def register_handler(handler, options = {})
|
113
|
+
priority = options[:priority] || default_priority
|
114
|
+
!!registered_handlers[priority].add?(handler)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the Hash of registered handlers by priority.
|
118
|
+
#
|
119
|
+
# @return [Hash{Integer,Set<RemotelyExceptional::Handler>}] The Hash of
|
120
|
+
# priorities and which handlers belong to those priorities.
|
121
|
+
def registered_handlers
|
122
|
+
Thread.current["#{name}.registered_handlers"] ||= Hash.new(&HASH_BUILDER)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Removes the given handler. By default removes the handler from the
|
126
|
+
# {::default_priority default_priority}, but a :priority option may be
|
127
|
+
# supplied to remove the handler from a specified priority.
|
128
|
+
#
|
129
|
+
# @param handler [RemotelyExceptional::Handler] The handler that should be
|
130
|
+
# removed.
|
131
|
+
# @param options [Hash{Symbol=>Object}] A Hash of optional arguments.
|
132
|
+
# @option options [Integer] :priority ({::default_priority}) The priority that
|
133
|
+
# should be searched for the given handler.
|
134
|
+
# @return [Boolean] Returns true if the handler was successfully removed for
|
135
|
+
# the given priority. Returns false if the handler was not registered for
|
136
|
+
# the given priority.
|
137
|
+
def remove_handler(handler, options = {})
|
138
|
+
priority = options[:priority] || default_priority
|
139
|
+
registered_handlers.key?(priority) &&
|
140
|
+
!!registered_handlers[priority].delete(handler)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Clears all {::block_handlers block handlers} and {::registered_handlers
|
144
|
+
# registered handlers}.
|
145
|
+
#
|
146
|
+
# @return [true]
|
147
|
+
def reset_handlers!
|
148
|
+
Thread.current["#{name}.registered_handlers"] = nil
|
149
|
+
Thread.current["#{name}.block_handlers"] = nil
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
# Registers a handler for the duration of the given block. By default
|
154
|
+
# registers the block at the {::default_priority default priority}, but a
|
155
|
+
# specific priority may be supplied as an option.
|
156
|
+
#
|
157
|
+
# @param handler [RemotelyExceptional::Handler] The handler that should be
|
158
|
+
# registered.
|
159
|
+
# @param options [Hash{Symbol=>Object}] A Hash of optional arguments.
|
160
|
+
# @option options [Integer] :priority ({::default_priority}) The priority that
|
161
|
+
# should be used to register the handler.
|
162
|
+
# @raise [ArgumentError] if a block is not provided.
|
163
|
+
# @return [Boolean] Returns true if the block handler was successfully
|
164
|
+
# registered for the given priority. Returns false if a matching block
|
165
|
+
# handler was already registered for the given priority.
|
166
|
+
def with_handler(handler, options = {})
|
167
|
+
raise ArgumentError, "Block required!" unless block_given?
|
168
|
+
|
169
|
+
priority = options[:priority] || default_priority
|
170
|
+
if block_handlers[priority].add?(handler)
|
171
|
+
added_handler = true
|
172
|
+
end
|
173
|
+
yield
|
174
|
+
ensure
|
175
|
+
block_handlers[priority].delete(handler) if added_handler
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RemotelyExceptional::RemoteHandling
|
2
|
+
# Executes the given block of code in a context that allows for remote
|
3
|
+
# handling of exceptions using the specified handler class. Optionally,
|
4
|
+
# additional contextual information may be provided in case of an exception.
|
5
|
+
#
|
6
|
+
# @param handler [RemotelyExceptional::Handler] The handler that should be
|
7
|
+
# used to match and handle any exceptions that occur.
|
8
|
+
# @param context [Hash{Symbol=>Object}] Optional contextual information that
|
9
|
+
# will be made available to the handler if an exception occurs.
|
10
|
+
# @raise [ArgumentError] Raised if the provided handler is not a valid
|
11
|
+
# RemotelyExceptional::Handler.
|
12
|
+
# @raise [RemotelyExceptional::InvalidHandlerResponse] Raised if an exception
|
13
|
+
# is raised but the handler does not return a valid action symbol.
|
14
|
+
# @raise [Exception] Depending on the Handler used, could raise any error
|
15
|
+
# returned by the handler's handle method.
|
16
|
+
# @return [Object] Returns the result of the given block if no exception
|
17
|
+
# occurs.
|
18
|
+
# @return [Object, nil] If an exception occurs may return a result value
|
19
|
+
# provided by the exception handler's handle method. If the handler's handle
|
20
|
+
# method does not specify a result, nil will be returned instead.
|
21
|
+
def remotely_exceptional(handler, context = {})
|
22
|
+
raise ArgumentError, "Invalid Handler! Got #{handler.inspect}" unless handler &&
|
23
|
+
handler.respond_to?(:ancestors) &&
|
24
|
+
handler.ancestors.include?(RemotelyExceptional::Handler)
|
25
|
+
|
26
|
+
# Must explicitly use begin otherwise TypeError will occur if handler is not
|
27
|
+
# a Class or Module. We can raise a more specific error if begin is used.
|
28
|
+
begin
|
29
|
+
yield
|
30
|
+
rescue handler
|
31
|
+
response_code, result = handler.handle(context)
|
32
|
+
case response_code
|
33
|
+
when :raise then result ? raise(result) : raise
|
34
|
+
when :retry then retry
|
35
|
+
when :continue then result
|
36
|
+
else
|
37
|
+
msg = "Handler did not return an expected response code!"
|
38
|
+
raise RemotelyExceptional::InvalidHandlerResponse, msg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "remotely_exceptional/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "remotely_exceptional"
|
8
|
+
spec.version = RemotelyExceptional::VERSION
|
9
|
+
spec.authors = ["Danny Guinther"]
|
10
|
+
spec.email = ["dannyguinther@gmail.com"]
|
11
|
+
spec.summary = %q{Remote control of exceptions raised in distant contexts.}
|
12
|
+
spec.description = %q{Remote control of exceptions raised in distant contexts.}
|
13
|
+
spec.homepage = "https://github.com/tdg5/remotely_exceptional"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake", "~> 0"
|
23
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
if ENV["CI"]
|
2
|
+
require "simplecov"
|
3
|
+
require "coveralls"
|
4
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
5
|
+
SimpleCov.root(File.expand_path("../..", __FILE__))
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter "test"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require "minitest/autorun"
|
12
|
+
require "mocha/setup"
|
13
|
+
require "remotely_exceptional"
|
14
|
+
|
15
|
+
# Use alternate shoulda-style DSL for tests
|
16
|
+
class RemotelyExceptional::TestCase < Minitest::Spec
|
17
|
+
class << self
|
18
|
+
alias :setup :before
|
19
|
+
alias :teardown :after
|
20
|
+
alias :context :describe
|
21
|
+
alias :should :it
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RemotelyExceptional::ExceptionsTest < RemotelyExceptional::TestCase
|
4
|
+
exception = RemotelyExceptional::InvalidHandlerResponse
|
5
|
+
context exception.name do
|
6
|
+
subject { exception }
|
7
|
+
context "#original_exception" do
|
8
|
+
should "capture the original exception if one exists" do
|
9
|
+
assert_nil subject.new.original_exception
|
10
|
+
exception = ArgumentError
|
11
|
+
rescued = false
|
12
|
+
begin
|
13
|
+
raise exception
|
14
|
+
rescue exception
|
15
|
+
rescued = true
|
16
|
+
assert_kind_of exception, subject.new.original_exception
|
17
|
+
end
|
18
|
+
assert_equal true, rescued
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RemotelyExceptional::HandlerTest < RemotelyExceptional::TestCase
|
4
|
+
Subject = RemotelyExceptional::Handler
|
5
|
+
class TestMixer
|
6
|
+
include Subject
|
7
|
+
end
|
8
|
+
|
9
|
+
context Subject.name do
|
10
|
+
subject { Subject }
|
11
|
+
|
12
|
+
context "::new" do
|
13
|
+
should "raise ArgumentError if no block was given" do
|
14
|
+
assert_raises(ArgumentError) do
|
15
|
+
subject.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
should "return a new class that includes #{Subject.name}" do
|
20
|
+
test_class = subject.new {|other| true }
|
21
|
+
assert_equal true, test_class.ancestors.include?(subject)
|
22
|
+
assert_kind_of subject, test_class.new
|
23
|
+
end
|
24
|
+
|
25
|
+
should "set the matcher to the given block" do
|
26
|
+
block = proc {|other| true }
|
27
|
+
test_class = subject.new(&block)
|
28
|
+
assert_equal block, test_class.send(:matcher)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "take an optional super_class argument" do
|
32
|
+
test_super_class = Class.new
|
33
|
+
test_class = subject.new(test_super_class) {|other| true }
|
34
|
+
assert_kind_of test_super_class, test_class.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context TestMixer.name do
|
40
|
+
subject { TestMixer }
|
41
|
+
|
42
|
+
context "included behaviors" do
|
43
|
+
context "::new" do
|
44
|
+
should "not be overriden by the module" do
|
45
|
+
assert_kind_of subject, subject.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "::===" do
|
50
|
+
should "call the matcher with the given argument" do
|
51
|
+
expected_argument = ArgumentError
|
52
|
+
subject.expects(:matcher).returns(lambda { |other| other == expected_argument })
|
53
|
+
assert_equal true, subject === expected_argument
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "::handle" do
|
58
|
+
setup do
|
59
|
+
@exception = ArgumentError.new
|
60
|
+
@context = { :context => true }
|
61
|
+
@instance = subject.new
|
62
|
+
subject.expects(:new).at_least(1).returns(@instance)
|
63
|
+
end
|
64
|
+
|
65
|
+
should "create a new instance and invoke #handle" do
|
66
|
+
@instance.expects(:handle)
|
67
|
+
subject.handle(@exception, @context)
|
68
|
+
end
|
69
|
+
|
70
|
+
should "set the exception and context of the instance correctly" do
|
71
|
+
@instance.stubs(:handle)
|
72
|
+
subject.handle(@exception, @context)
|
73
|
+
assert_equal @exception, @instance.exception
|
74
|
+
assert_equal @context, @instance.context
|
75
|
+
end
|
76
|
+
|
77
|
+
should "should automatically detect exception if not provided in an exception context" do
|
78
|
+
@instance.stubs(:handle)
|
79
|
+
begin
|
80
|
+
raise @exception
|
81
|
+
rescue
|
82
|
+
subject.handle(@context)
|
83
|
+
end
|
84
|
+
# Should be the exception in an exception context
|
85
|
+
assert_equal @context, @instance.context
|
86
|
+
assert_equal @exception, @instance.exception
|
87
|
+
end
|
88
|
+
|
89
|
+
should "have a nil exception when not provided an exception outside of an exception context" do
|
90
|
+
@instance.stubs(:handle)
|
91
|
+
subject.handle(@context)
|
92
|
+
assert_equal @context, @instance.context
|
93
|
+
# Should be nil outside of an exception context
|
94
|
+
assert_nil @instance.exception
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "rescue" do
|
99
|
+
should "call the matcher with an exception class when used to rescue" do
|
100
|
+
expected_argument = ArgumentError
|
101
|
+
subject.expects(:matcher).returns(lambda { |other| !other.is_a?(expected_argument) })
|
102
|
+
|
103
|
+
rescued = false
|
104
|
+
assert_raises(ArgumentError) do
|
105
|
+
begin
|
106
|
+
raise ArgumentError
|
107
|
+
rescue subject
|
108
|
+
rescued = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
assert_equal false, rescued
|
112
|
+
end
|
113
|
+
|
114
|
+
should "invoke rescue code when the matcher matches the exception" do
|
115
|
+
expected_argument = ArgumentError
|
116
|
+
subject.expects(:matcher).returns(lambda { |other| other.is_a?(expected_argument) })
|
117
|
+
|
118
|
+
rescued = false
|
119
|
+
begin
|
120
|
+
raise ArgumentError
|
121
|
+
rescue subject
|
122
|
+
rescued = true
|
123
|
+
end
|
124
|
+
assert_equal true, rescued
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "#handle" do
|
129
|
+
should "raise NotImplementedError if not overriden" do
|
130
|
+
assert_raises(NotImplementedError) { subject.new.handle }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "remotely_exceptional/handlers/prioritized_handler"
|
3
|
+
|
4
|
+
class RemotelyExceptional::Handlers::PrioritizedHandlerTest < RemotelyExceptional::TestCase
|
5
|
+
Subject = RemotelyExceptional::Handlers::PrioritizedHandler
|
6
|
+
|
7
|
+
AlphaHandler = RemotelyExceptional::Handler.new { |ex| ArgumentError === ex }
|
8
|
+
BetaHandler = RemotelyExceptional::Handler.new { |ex| RuntimeError === ex }
|
9
|
+
OmegaHandler = RemotelyExceptional::Handler.new { |ex| Exception === ex }
|
10
|
+
|
11
|
+
class TestSubject
|
12
|
+
include Subject
|
13
|
+
end
|
14
|
+
|
15
|
+
context "module that includes #{Subject.name}" do
|
16
|
+
subject { TestSubject }
|
17
|
+
|
18
|
+
setup { subject.reset_handlers! }
|
19
|
+
|
20
|
+
context "::===" do
|
21
|
+
should "return true if a handler is registered that matches the exception" do
|
22
|
+
subject.register_handler(AlphaHandler)
|
23
|
+
assert_equal true, subject === ArgumentError.new
|
24
|
+
end
|
25
|
+
|
26
|
+
should "return false if none of the handlers match the exception" do
|
27
|
+
subject.register_handler(AlphaHandler)
|
28
|
+
assert_equal false, subject === RuntimeError.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "::default_priority" do
|
33
|
+
should "return 1000" do
|
34
|
+
assert_equal 1000, subject.default_priority
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "::handle" do
|
39
|
+
should "delegate handling to the matching handler with the highest priority" do
|
40
|
+
subject.register_handler(AlphaHandler)
|
41
|
+
subject.register_handler(OmegaHandler, :priority => 10)
|
42
|
+
ex = ArgumentError.new
|
43
|
+
result = :continue
|
44
|
+
context = { :context => :foo }
|
45
|
+
OmegaHandler.expects(:handle).with(ex, context).returns(result)
|
46
|
+
assert_equal result, subject.handle(ex, context)
|
47
|
+
end
|
48
|
+
|
49
|
+
should "re-raise the exception if no matching handler is found" do
|
50
|
+
ex = ArgumentError.new
|
51
|
+
assert_raises(ex.class) { subject.handle(ex) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "::handler_for_exception" do
|
56
|
+
should "return the matching handler" do
|
57
|
+
subject.register_handler(AlphaHandler)
|
58
|
+
assert_equal AlphaHandler, subject.handler_for_exception(ArgumentError.new)
|
59
|
+
end
|
60
|
+
|
61
|
+
should "return the matching handler with the highest priority" do
|
62
|
+
subject.register_handler(AlphaHandler)
|
63
|
+
subject.register_handler(OmegaHandler, :priority => 10)
|
64
|
+
assert_equal OmegaHandler, subject.handler_for_exception(ArgumentError.new)
|
65
|
+
end
|
66
|
+
|
67
|
+
should "return nil if no matching handler is found" do
|
68
|
+
subject.register_handler(AlphaHandler)
|
69
|
+
subject.register_handler(BetaHandler)
|
70
|
+
assert_nil subject.handler_for_exception(SystemStackError.new)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "::prioritized_handlers" do
|
75
|
+
should "yield handlers in priority ASC, name ASC order" do
|
76
|
+
setup_registered_handlers(subject)
|
77
|
+
found_handlers = nil
|
78
|
+
# Should be yielded first
|
79
|
+
subject.with_handler(BetaHandler, :priority => 5) do
|
80
|
+
# Should be yielded last
|
81
|
+
subject.with_handler(OmegaHandler, :priority => 2500) do
|
82
|
+
# Should only be yielded once for this priority
|
83
|
+
subject.with_handler(AlphaHandler) do
|
84
|
+
found_handlers = subject.prioritized_handlers.to_a
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@expected_handlers.unshift(BetaHandler)
|
89
|
+
@expected_handlers.push(OmegaHandler)
|
90
|
+
assert_equal @expected_handlers, found_handlers
|
91
|
+
end
|
92
|
+
|
93
|
+
should "work when there are only registered handlers" do
|
94
|
+
setup_registered_handlers(subject)
|
95
|
+
subject.instance_variable_set(:@block_handlers, nil)
|
96
|
+
assert_equal @expected_handlers, subject.prioritized_handlers.to_a
|
97
|
+
end
|
98
|
+
|
99
|
+
should "work when there are only block handlers" do
|
100
|
+
found_handlers = nil
|
101
|
+
subject.with_handler(BetaHandler, :priority => 5) do
|
102
|
+
subject.with_handler(OmegaHandler, :priority => 2500) do
|
103
|
+
subject.with_handler(AlphaHandler) do
|
104
|
+
found_handlers = subject.prioritized_handlers.to_a
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
expected_handlers = [
|
109
|
+
BetaHandler,
|
110
|
+
AlphaHandler,
|
111
|
+
OmegaHandler,
|
112
|
+
]
|
113
|
+
assert_equal expected_handlers, found_handlers
|
114
|
+
end
|
115
|
+
|
116
|
+
should "yield a handler only once per priority level" do
|
117
|
+
subject.register_handler(AlphaHandler)
|
118
|
+
found_handlers = nil
|
119
|
+
subject.with_handler(AlphaHandler) do
|
120
|
+
found_handlers = subject.prioritized_handlers.to_a
|
121
|
+
end
|
122
|
+
assert_equal 1, found_handlers.length
|
123
|
+
assert_equal AlphaHandler, found_handlers.first
|
124
|
+
end
|
125
|
+
|
126
|
+
context "set scan" do
|
127
|
+
should "not create empty sets when no keys exist" do
|
128
|
+
subject.prioritized_handlers.to_a
|
129
|
+
assert_empty subject.send(:registered_handlers)
|
130
|
+
assert_empty subject.send(:block_handlers)
|
131
|
+
end
|
132
|
+
|
133
|
+
should "not create empty sets when a key exists in one set" do
|
134
|
+
priority = 1000
|
135
|
+
subject.with_handler(AlphaHandler, :priority => priority) do
|
136
|
+
subject.prioritized_handlers.to_a
|
137
|
+
end
|
138
|
+
assert_equal false, subject.send(:registered_handlers).key?(priority)
|
139
|
+
|
140
|
+
priority = 10
|
141
|
+
subject.register_handler(AlphaHandler, :priority => priority)
|
142
|
+
assert_equal false, subject.send(:block_handlers).key?(priority)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "::register_handler" do
|
148
|
+
should "return false if the handler was not registered" do
|
149
|
+
priority = 10
|
150
|
+
assert_equal true, subject.register_handler(AlphaHandler, :priority => priority)
|
151
|
+
assert_equal false, subject.register_handler(AlphaHandler, :priority => priority)
|
152
|
+
end
|
153
|
+
|
154
|
+
should "return true if the handler was registered" do
|
155
|
+
priority = 10
|
156
|
+
assert_equal true, subject.register_handler(AlphaHandler, :priority => priority)
|
157
|
+
end
|
158
|
+
|
159
|
+
should "register the handler with the provided priority" do
|
160
|
+
priority = 10
|
161
|
+
registered_handlers = subject.send(:registered_handlers)
|
162
|
+
registered_handlers.expects(:[]).with(priority).returns(Set.new)
|
163
|
+
assert_equal true, subject.register_handler(AlphaHandler, :priority => priority)
|
164
|
+
end
|
165
|
+
|
166
|
+
should "register the handler with the default priority if no priority given" do
|
167
|
+
priority = subject.default_priority
|
168
|
+
registered_handlers = subject.send(:registered_handlers)
|
169
|
+
registered_handlers.expects(:[]).with(priority).returns(Set.new)
|
170
|
+
assert_equal true, subject.register_handler(AlphaHandler)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "::remove_handler" do
|
175
|
+
should "return false if the handler was not removed" do
|
176
|
+
assert_equal false, subject.remove_handler(AlphaHandler)
|
177
|
+
end
|
178
|
+
|
179
|
+
should "return true if the handler was removed" do
|
180
|
+
assert_equal true, subject.register_handler(AlphaHandler)
|
181
|
+
assert_equal true, subject.remove_handler(AlphaHandler)
|
182
|
+
end
|
183
|
+
|
184
|
+
should "remove a handler with a matching priority" do
|
185
|
+
priority = 10
|
186
|
+
assert_equal true, subject.register_handler(AlphaHandler, :priority => priority)
|
187
|
+
assert_equal true, subject.remove_handler(AlphaHandler, :priority => priority)
|
188
|
+
end
|
189
|
+
|
190
|
+
should "not remove a handler with a non-matching priority" do
|
191
|
+
priority = 10
|
192
|
+
assert_equal true, subject.register_handler(AlphaHandler, :priority => priority)
|
193
|
+
assert_equal false, subject.remove_handler(AlphaHandler)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "::reset_handlers!" do
|
198
|
+
should "clear all handlers" do
|
199
|
+
subject.register_handler(AlphaHandler, :priority => 10)
|
200
|
+
initial_found_handlers = final_found_handlers = nil
|
201
|
+
subject.with_handler(AlphaHandler) do
|
202
|
+
initial_found_handlers = subject.prioritized_handlers.to_a
|
203
|
+
subject.reset_handlers!
|
204
|
+
final_found_handlers = subject.prioritized_handlers.to_a
|
205
|
+
end
|
206
|
+
assert_equal 2, initial_found_handlers.length
|
207
|
+
assert_equal 0, final_found_handlers.length
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "::with_handler" do
|
212
|
+
should "raise ArgumentError if no block is given" do
|
213
|
+
assert_raises(ArgumentError) do
|
214
|
+
subject.with_handler(AlphaHandler)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
should "register the handler if it is not already registered" do
|
219
|
+
assert_equal true, subject.prioritized_handlers.none?
|
220
|
+
subject.with_handler(AlphaHandler) do
|
221
|
+
found_handlers = subject.prioritized_handlers.to_a
|
222
|
+
assert_equal 1, found_handlers.length
|
223
|
+
assert_equal AlphaHandler, subject.prioritized_handlers.first
|
224
|
+
|
225
|
+
# Should not add the handler again.
|
226
|
+
subject.with_handler(AlphaHandler) do
|
227
|
+
found_handlers = subject.prioritized_handlers.to_a
|
228
|
+
assert_equal 1, found_handlers.length
|
229
|
+
assert_equal AlphaHandler, subject.prioritized_handlers.first
|
230
|
+
end
|
231
|
+
end
|
232
|
+
assert_equal true, subject.prioritized_handlers.none?
|
233
|
+
end
|
234
|
+
|
235
|
+
should "register the handler with the given priority" do
|
236
|
+
priority = 10
|
237
|
+
assert_equal true, subject.prioritized_handlers.none?
|
238
|
+
subject.with_handler(AlphaHandler, :priority => priority) do
|
239
|
+
found_handler = subject.send(:block_handlers)[priority].first
|
240
|
+
assert_equal AlphaHandler, found_handler
|
241
|
+
end
|
242
|
+
assert_equal true, subject.prioritized_handlers.none?
|
243
|
+
end
|
244
|
+
|
245
|
+
should "register the handler with the default priority if no priority given" do
|
246
|
+
priority = subject.default_priority
|
247
|
+
assert_equal true, subject.prioritized_handlers.none?
|
248
|
+
subject.with_handler(AlphaHandler, :priority => priority) do
|
249
|
+
found_handler = subject.send(:block_handlers)[priority].first
|
250
|
+
assert_equal AlphaHandler, found_handler
|
251
|
+
end
|
252
|
+
assert_equal true, subject.prioritized_handlers.none?
|
253
|
+
end
|
254
|
+
|
255
|
+
should "remove the handler after yielding" do
|
256
|
+
assert_equal true, subject.prioritized_handlers.none?
|
257
|
+
subject.with_handler(AlphaHandler) do
|
258
|
+
assert_equal AlphaHandler, subject.prioritized_handlers.first
|
259
|
+
end
|
260
|
+
assert_equal true, subject.prioritized_handlers.none?
|
261
|
+
end
|
262
|
+
|
263
|
+
should "remove the handler even if an error occurs" do
|
264
|
+
assert_equal true, subject.prioritized_handlers.none?
|
265
|
+
assert_raises(Exception) do
|
266
|
+
subject.with_handler(AlphaHandler) do
|
267
|
+
assert_equal AlphaHandler, subject.prioritized_handlers.first
|
268
|
+
raise Exception
|
269
|
+
end
|
270
|
+
end
|
271
|
+
assert_equal true, subject.prioritized_handlers.none?
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
def setup_registered_handlers(handler)
|
278
|
+
# 5
|
279
|
+
assert_equal true, handler.register_handler(AlphaHandler)
|
280
|
+
# 2
|
281
|
+
assert_equal true, handler.register_handler(AlphaHandler, :priority => 500)
|
282
|
+
# 3
|
283
|
+
assert_equal true, handler.register_handler(BetaHandler, :priority => 500)
|
284
|
+
# 1
|
285
|
+
assert_equal true, handler.register_handler(OmegaHandler, :priority => 50)
|
286
|
+
# 4
|
287
|
+
assert_equal true, handler.register_handler(OmegaHandler, :priority => 500)
|
288
|
+
|
289
|
+
@expected_handlers = [
|
290
|
+
OmegaHandler,
|
291
|
+
AlphaHandler,
|
292
|
+
BetaHandler,
|
293
|
+
OmegaHandler,
|
294
|
+
AlphaHandler,
|
295
|
+
]
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RemotelyExceptional::RemoteHandlingTest < RemotelyExceptional::TestCase
|
4
|
+
Subject = RemotelyExceptional::RemoteHandling
|
5
|
+
|
6
|
+
class TestMixer
|
7
|
+
include Subject
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestHandler
|
11
|
+
include RemotelyExceptional::Handler
|
12
|
+
def self.matcher
|
13
|
+
lambda { |ex| ex.is_a?(exception_class) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.exception_class
|
17
|
+
RuntimeError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "class that includes #{Subject.name}" do
|
22
|
+
|
23
|
+
context "#remotely_exceptional" do
|
24
|
+
subject { TestMixer.new }
|
25
|
+
|
26
|
+
setup do
|
27
|
+
@handler = TestHandler
|
28
|
+
@instance = TestHandler.new
|
29
|
+
@handler.stubs(:new).returns(@instance)
|
30
|
+
end
|
31
|
+
|
32
|
+
should "raise ArgumentError unless a Handler is given" do
|
33
|
+
[nil, Class.new, Module.new, :not_a_handler].each do |handler|
|
34
|
+
assert_raises(ArgumentError) { subject.remotely_exceptional(handler) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should "yield to the provided block" do
|
39
|
+
block_called = false
|
40
|
+
subject.remotely_exceptional(@handler) do
|
41
|
+
block_called = true
|
42
|
+
end
|
43
|
+
assert_equal true, block_called
|
44
|
+
end
|
45
|
+
|
46
|
+
context "response codes" do
|
47
|
+
should "raise InvalidHandlerResponse if unrecognized response code" do
|
48
|
+
@instance.expects(:handle).returns(:not_a_thing)
|
49
|
+
exception = assert_raises(RemotelyExceptional::InvalidHandlerResponse) do
|
50
|
+
subject.remotely_exceptional(@handler) do
|
51
|
+
raise @handler.exception_class
|
52
|
+
end
|
53
|
+
end
|
54
|
+
assert_kind_of @handler.exception_class, exception.original_exception
|
55
|
+
end
|
56
|
+
|
57
|
+
should "retry if retry code is given" do
|
58
|
+
@instance.expects(:handle).returns(:retry)
|
59
|
+
already_called = false
|
60
|
+
retried = false
|
61
|
+
subject.remotely_exceptional(@handler) do
|
62
|
+
if already_called
|
63
|
+
retried = true
|
64
|
+
else
|
65
|
+
already_called = true
|
66
|
+
raise @handler.exception_class
|
67
|
+
end
|
68
|
+
end
|
69
|
+
assert_equal true, retried
|
70
|
+
end
|
71
|
+
|
72
|
+
should "raise if raise code is given" do
|
73
|
+
@instance.expects(:handle).returns(:raise)
|
74
|
+
assert_raises(@handler.exception_class) do
|
75
|
+
subject.remotely_exceptional(@handler) do
|
76
|
+
raise @handler.exception_class
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
should "raise given exception if raise code is given with exception" do
|
82
|
+
exception_class = ArgumentError
|
83
|
+
@instance.expects(:handle).returns([:raise, exception_class])
|
84
|
+
assert_raises(exception_class) do
|
85
|
+
subject.remotely_exceptional(@handler) do
|
86
|
+
raise @handler.exception_class
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
should "continue if continue code is given" do
|
92
|
+
@instance.expects(:handle).returns(:continue)
|
93
|
+
result = subject.remotely_exceptional(@handler) do
|
94
|
+
raise @handler.exception_class
|
95
|
+
end
|
96
|
+
assert_nil result
|
97
|
+
end
|
98
|
+
|
99
|
+
should "continue and return given value if continue code is given with value" do
|
100
|
+
expected_result = 42
|
101
|
+
@instance.expects(:handle).returns([:continue, expected_result])
|
102
|
+
result = subject.remotely_exceptional(@handler) do
|
103
|
+
raise @handler.exception_class
|
104
|
+
end
|
105
|
+
assert_equal expected_result, result
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RemotelyExceptionalTest < RemotelyExceptional::TestCase
|
4
|
+
Subject = RemotelyExceptional
|
5
|
+
|
6
|
+
subject { Subject }
|
7
|
+
|
8
|
+
context Subject.name do
|
9
|
+
should "be defined" do
|
10
|
+
assert defined?(subject), "Expected #{subject.name} to be defined!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: remotely_exceptional
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danny Guinther
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Remote control of exceptions raised in distant contexts.
|
42
|
+
email:
|
43
|
+
- dannyguinther@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- ".travis.yml"
|
50
|
+
- Gemfile
|
51
|
+
- Guardfile
|
52
|
+
- LICENSE
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- lib/remotely_exceptional.rb
|
56
|
+
- lib/remotely_exceptional/exceptions.rb
|
57
|
+
- lib/remotely_exceptional/handler.rb
|
58
|
+
- lib/remotely_exceptional/handlers/prioritized_handler.rb
|
59
|
+
- lib/remotely_exceptional/remote_handling.rb
|
60
|
+
- lib/remotely_exceptional/version.rb
|
61
|
+
- remotely_exceptional.gemspec
|
62
|
+
- test/test_helper.rb
|
63
|
+
- test/unit/exceptions_test.rb
|
64
|
+
- test/unit/handler_test.rb
|
65
|
+
- test/unit/handlers/prioritized_handler_test.rb
|
66
|
+
- test/unit/remote_handling_test.rb
|
67
|
+
- test/unit/remotely_exceptional_test.rb
|
68
|
+
homepage: https://github.com/tdg5/remotely_exceptional
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.2.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Remote control of exceptions raised in distant contexts.
|
92
|
+
test_files:
|
93
|
+
- test/test_helper.rb
|
94
|
+
- test/unit/exceptions_test.rb
|
95
|
+
- test/unit/handler_test.rb
|
96
|
+
- test/unit/handlers/prioritized_handler_test.rb
|
97
|
+
- test/unit/remote_handling_test.rb
|
98
|
+
- test/unit/remotely_exceptional_test.rb
|
99
|
+
has_rdoc:
|