execute_with_rescue 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 +18 -0
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/Appraisals +11 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +132 -0
- data/Rakefile +16 -0
- data/execute_with_rescue.gemspec +36 -0
- data/gemfiles/rails_3_2.gemfile +7 -0
- data/gemfiles/rails_4_0.gemfile +7 -0
- data/gemfiles/rails_4_1.gemfile +7 -0
- data/lib/execute_with_rescue.rb +6 -0
- data/lib/execute_with_rescue/errors.rb +6 -0
- data/lib/execute_with_rescue/mixins/core.rb +172 -0
- data/lib/execute_with_rescue/version.rb +3 -0
- data/spec/core_spec.rb +215 -0
- data/spec/fixtures/test_service_classes.rb +119 -0
- data/spec/spec_helper.rb +14 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6567bf06706ae0cc18b48601c95d7a8de4fdaf63
|
4
|
+
data.tar.gz: 70a5d62da3282a7851d3c07f5d12f88f29f66cb1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a787b094e8f6c46239b5f067e59bc27375484720c2ddda02beda5082bf1de00622832c9cb59ad0aa1a88cb50e0ae9d0cb62b363f2ed7a6e77581130229968be4
|
7
|
+
data.tar.gz: 5446a8050f2c55fcc4a91bfddc6fe50b073f39be067a6d22adf447ead5826888fc08f78fab257cb4db9dc0fd4f1dec2566c5a3abf14fe29245bc92e16a7bd200
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 PikachuEXE
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# ExecuteWithRescue
|
2
|
+
|
3
|
+
Saves your from writing `begin...rescue...ensure...end` everywhere.
|
4
|
+
This assumes you know how to use `rescue_from` not just within a controller.
|
5
|
+
|
6
|
+
I write this "gem" because I enocunter this pattern in many background worker and service classes.
|
7
|
+
(I use [`interactor`](https://github.com/collectiveidea/interactor) for service classes btw)
|
8
|
+
I use this gem, [`rescue_from`](http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html) and a custom [`airbrake`](https://github.com/airbrake/airbrake) adapter to add options to Airbrake when notifying, since otherwise you will have to call `Airbrake.notify_or_ignore` manually in `rescue` and pass the options.
|
9
|
+
Calling `airbrake` manually sometimes is the best option, but not all the time.
|
10
|
+
I might release another gem for that airbrake adapter.
|
11
|
+
|
12
|
+
|
13
|
+
## Support
|
14
|
+
===========
|
15
|
+
Tested against:
|
16
|
+
- Active Support of version `3.2`, `4.0` and `4.1` (pre-3.2 got wrong dependency & I don't want to support)
|
17
|
+
- MRI `1.9.3`, `2.0.0`, `2.1.0`, `2.1.1`
|
18
|
+
|
19
|
+
[](https://travis-ci.org/PikachuEXE/execute_with_rescue)
|
20
|
+
[](http://badge.fury.io/rb/execute_with_rescue)
|
21
|
+
[](https://gemnasium.com/PikachuEXE/execute_with_rescue)
|
22
|
+
[](https://coveralls.io/r/PikachuEXE/execute_with_rescue)
|
23
|
+
[](https://codeclimate.com/github/PikachuEXE/execute_with_rescue)
|
24
|
+
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'execute_with_rescue'
|
32
|
+
```
|
33
|
+
|
34
|
+
And then execute:
|
35
|
+
|
36
|
+
$ bundle
|
37
|
+
|
38
|
+
Or install it yourself as:
|
39
|
+
|
40
|
+
$ gem install execute_with_rescue
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
### `#execute_with_rescue`
|
45
|
+
**Private** method to be called with a block
|
46
|
+
You still have to call `rescue_from` at class level yourself (no magic here)
|
47
|
+
```ruby
|
48
|
+
class SomeServiceClass
|
49
|
+
include ExecuteWithRescue::Mixins::Core
|
50
|
+
|
51
|
+
# then run code with possible errors
|
52
|
+
def perform
|
53
|
+
execute_with_rescue do
|
54
|
+
# Something that might causes error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
## Class Methods for adding hooks
|
61
|
+
"Hooks" are just things to be run before the block and after the block (in `ensure`)
|
62
|
+
Beware that the "hooks" execution order are like [`ActiveSupport::Callbacks`](http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html)
|
63
|
+
|
64
|
+
### `.add_execute_with_rescue_before_hooks`
|
65
|
+
Alias: `.add_execute_with_rescue_before_hook`
|
66
|
+
Execution order: Add first, run first
|
67
|
+
```ruby
|
68
|
+
class SomeServiceClass
|
69
|
+
# Either add hooks by using method names in symbol
|
70
|
+
add_execute_with_rescue_before_hook :report_start_by_logging
|
71
|
+
|
72
|
+
# Or add more
|
73
|
+
add_execute_with_rescue_before_hooks :do_more, :do_even_more
|
74
|
+
|
75
|
+
# Or in block
|
76
|
+
add_execute_with_rescue_before_hook do
|
77
|
+
Rails.logger.debug("Some job started")
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def report_start_by_logging
|
83
|
+
Rails.logger.debug("Some job started")
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_more
|
87
|
+
# This execute earlier
|
88
|
+
end
|
89
|
+
def do_even_more
|
90
|
+
# This execute later
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
### `.add_execute_with_rescue_after_hooks`
|
96
|
+
Alias: `.add_execute_with_rescue_after_hook`
|
97
|
+
Execution order: Add first, run last
|
98
|
+
```ruby
|
99
|
+
class SomeServiceClass
|
100
|
+
# Either add hooks by using method names in symbol
|
101
|
+
add_execute_with_rescue_after_hook :report_end_by_logging
|
102
|
+
|
103
|
+
# Or add more
|
104
|
+
add_execute_with_rescue_after_hooks :clean_up_base, :clean_up_more
|
105
|
+
|
106
|
+
# Or in block
|
107
|
+
add_execute_with_rescue_after_hook do
|
108
|
+
Rails.logger.debug("Some job ended")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def report_end_by_logging
|
114
|
+
Rails.logger.debug("Some job ended")
|
115
|
+
end
|
116
|
+
|
117
|
+
def clean_up_base
|
118
|
+
# This execute later
|
119
|
+
end
|
120
|
+
def clean_up_more
|
121
|
+
# This execute earlier
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
## Contributing
|
127
|
+
|
128
|
+
1. Fork it
|
129
|
+
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
130
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
131
|
+
4. Push to the branch (`git push origin feature/my-new-feature`)
|
132
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
require "appraisal"
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
|
10
|
+
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
11
|
+
task :default do
|
12
|
+
sh "rake appraisal:install && rake appraisal spec"
|
13
|
+
end
|
14
|
+
else
|
15
|
+
task :default => :spec
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'execute_with_rescue/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "execute_with_rescue"
|
8
|
+
spec.version = ExecuteWithRescue::VERSION
|
9
|
+
spec.authors = ["PikachuEXE"]
|
10
|
+
spec.email = ["pikachuexe@gmail.com"]
|
11
|
+
spec.summary = %q{Execute code without writting rescue in methods with before and after hooks. You can also create some extensions yourself.}
|
12
|
+
spec.description = <<-DESC
|
13
|
+
Saves your from writing `begin...rescue...ensure...end` everywhere.
|
14
|
+
This assumes you know how to use `rescue_from` not just within a controller.
|
15
|
+
DESC
|
16
|
+
spec.homepage = "http://github.com/PikachuEXE/execute_with_rescue"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "activesupport", ">= 3.2.0", "< 5.0.0"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "appraisal", ">= 0.5.2"
|
29
|
+
spec.add_development_dependency "rspec", "~> 2.14.0"
|
30
|
+
spec.add_development_dependency "coveralls", ">= 0.7"
|
31
|
+
spec.add_development_dependency "gem-release", ">= 0.7"
|
32
|
+
|
33
|
+
spec.required_ruby_version = ">= 1.9.3"
|
34
|
+
|
35
|
+
spec.required_rubygems_version = ">= 1.4.0"
|
36
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/rescuable'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
|
5
|
+
require 'execute_with_rescue/errors'
|
6
|
+
|
7
|
+
module ExecuteWithRescue
|
8
|
+
module Mixins
|
9
|
+
module Core
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
include ActiveSupport::Rescuable
|
14
|
+
|
15
|
+
# Use active support or inheritance will be broken
|
16
|
+
class_attribute :_execute_with_rescue_before_hooks,
|
17
|
+
instance_reader: true,
|
18
|
+
instance_writer: false
|
19
|
+
self._execute_with_rescue_before_hooks = []
|
20
|
+
class_attribute :_execute_with_rescue_after_hooks,
|
21
|
+
instance_reader: true,
|
22
|
+
instance_writer: false
|
23
|
+
self._execute_with_rescue_after_hooks = []
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Pass method names or/and a block to be executed before yield
|
27
|
+
#
|
28
|
+
# @param method_names [Array<Symbol>]
|
29
|
+
# instance methods names to be run before yield
|
30
|
+
# @param block [Proc]
|
31
|
+
# a block to be executed with no argument
|
32
|
+
# in the instance before yield
|
33
|
+
# It will be appended after method_names if both given
|
34
|
+
#
|
35
|
+
# @note These hooks are inherited
|
36
|
+
#
|
37
|
+
# @example Add a hook to begin some logging
|
38
|
+
# add_execute_with_rescue_before_hooks(:log_start)
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError]
|
41
|
+
# if neither method_names and block is given
|
42
|
+
def add_execute_with_rescue_before_hooks(*method_names, &block)
|
43
|
+
_validate_execute_with_rescue_hook!(method_names, block)
|
44
|
+
|
45
|
+
# Must use setter to avoid changing parent setting
|
46
|
+
self._execute_with_rescue_before_hooks =
|
47
|
+
[
|
48
|
+
self._execute_with_rescue_before_hooks,
|
49
|
+
# Add method names first, block later
|
50
|
+
method_names,
|
51
|
+
block,
|
52
|
+
].flatten.compact
|
53
|
+
end
|
54
|
+
alias_method :add_execute_with_rescue_before_hook,
|
55
|
+
:add_execute_with_rescue_before_hooks
|
56
|
+
|
57
|
+
# Pass method names or/and a block to be executed after yield
|
58
|
+
# Similar to add_execute_with_rescue_before_hooks
|
59
|
+
#
|
60
|
+
# @see add_execute_with_rescue_before_hooks
|
61
|
+
def add_execute_with_rescue_after_hooks(*method_names, &block)
|
62
|
+
_validate_execute_with_rescue_hook!(method_names, block)
|
63
|
+
|
64
|
+
# Must use setter to avoid changing parent setting
|
65
|
+
self._execute_with_rescue_after_hooks =
|
66
|
+
[
|
67
|
+
self._execute_with_rescue_after_hooks,
|
68
|
+
# Add method names first, block later
|
69
|
+
method_names,
|
70
|
+
block,
|
71
|
+
].flatten.compact
|
72
|
+
end
|
73
|
+
alias_method :add_execute_with_rescue_after_hook,
|
74
|
+
:add_execute_with_rescue_after_hooks
|
75
|
+
|
76
|
+
# @private
|
77
|
+
# @discuss
|
78
|
+
# Should this moved into another module?
|
79
|
+
# (without being mixed in)
|
80
|
+
def _validate_execute_with_rescue_hook!(method_names, block)
|
81
|
+
raise ArgumentError if (method_names.empty? && block.nil?)
|
82
|
+
raise ExecuteWithRescue::Errors::UnsupportedHookValue unless
|
83
|
+
method_names.all?{|m| m.is_a?(Symbol)}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Wrapper method for rescuing known errors
|
91
|
+
# after you have call `rescue_from` at class level
|
92
|
+
# This saves you from typing:
|
93
|
+
# ```
|
94
|
+
# being
|
95
|
+
# # Some code that might cause exception
|
96
|
+
# rescue
|
97
|
+
# rescue_with_handler(exception) || raise
|
98
|
+
# end
|
99
|
+
# ````
|
100
|
+
# Remember to `next` instead of `return` if you want to terminate
|
101
|
+
#
|
102
|
+
# You can use `alias_method` to create a shorter alias, I use `execute`
|
103
|
+
# But some gem might use that name already, so be careful
|
104
|
+
#
|
105
|
+
# @param block [Proc]
|
106
|
+
# a block to be executed
|
107
|
+
#
|
108
|
+
# @note
|
109
|
+
# Use `next` for termination, since `return` in block does not work
|
110
|
+
# @note
|
111
|
+
# Although we rescue Exception here,
|
112
|
+
# but normally we should NOT handle them without re-raise
|
113
|
+
#
|
114
|
+
# @example Use with gem `interactor`
|
115
|
+
# class DoSomething
|
116
|
+
# include Interactor
|
117
|
+
# include ExecuteWithRescue::Mixins::Core
|
118
|
+
#
|
119
|
+
# def perform
|
120
|
+
# execute_with_rescue do
|
121
|
+
# # Do something
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# @raise [LocalJumpError]
|
127
|
+
# When you call return in block
|
128
|
+
def execute_with_rescue
|
129
|
+
_run_execute_with_rescue_before_hooks
|
130
|
+
yield
|
131
|
+
rescue Exception => exception
|
132
|
+
rescue_with_handler(exception) || raise
|
133
|
+
ensure
|
134
|
+
_run_execute_with_rescue_after_hooks
|
135
|
+
end
|
136
|
+
|
137
|
+
# @private
|
138
|
+
def _run_execute_with_rescue_before_hooks
|
139
|
+
_execute_with_rescue_before_hooks.each do |before_hook|
|
140
|
+
_run_execute_with_rescue_hook(before_hook)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
def _run_execute_with_rescue_after_hooks
|
146
|
+
_execute_with_rescue_after_hooks.reverse.each do |after_hook|
|
147
|
+
_run_execute_with_rescue_hook(after_hook)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @private
|
152
|
+
def _run_execute_with_rescue_hook(method_name_or_block)
|
153
|
+
case method_name_or_block
|
154
|
+
when Symbol
|
155
|
+
begin
|
156
|
+
self.send(method_name_or_block)
|
157
|
+
rescue NoMethodError
|
158
|
+
raise ExecuteWithRescue::Errors::NoHookMethod,
|
159
|
+
"method `#{method_name_or_block}` does not exists"
|
160
|
+
end
|
161
|
+
# block are converted to Proc as argument
|
162
|
+
when Proc
|
163
|
+
instance_eval(&method_name_or_block)
|
164
|
+
else
|
165
|
+
# This should not happen unless someone tamper the class attribute
|
166
|
+
# without using the provided methods
|
167
|
+
raise ExecuteWithRescue::Errors::UnsupportedHookValue
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/spec/core_spec.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ExecuteWithRescue::Mixins::Core do
|
4
|
+
describe '#execute_with_rescue' do
|
5
|
+
let!(:service_instance) { service_class.new }
|
6
|
+
def service_call
|
7
|
+
service_instance.call
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'without a block' do
|
11
|
+
let!(:service_class) { TestServiceWithoutBlockInExecute }
|
12
|
+
|
13
|
+
specify do
|
14
|
+
expect { service_call }
|
15
|
+
.to raise_error(LocalJumpError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with private method call' do
|
20
|
+
let!(:service_class) { TestServiceWithPrivateMethodCallInExecute }
|
21
|
+
|
22
|
+
specify do
|
23
|
+
expect { service_call }
|
24
|
+
.to_not raise_error
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'without calling rescue_from' do
|
29
|
+
let!(:service_class) { TestServiceWithError }
|
30
|
+
|
31
|
+
specify do
|
32
|
+
expect { service_call }
|
33
|
+
.to raise_error(StandardError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with calling rescue_from' do
|
38
|
+
let!(:service_class) { TestServiceWithRescue }
|
39
|
+
|
40
|
+
specify do
|
41
|
+
expect { service_call }
|
42
|
+
.to raise_error(TestServiceWithRescue::CustomError)
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with single hook' do
|
46
|
+
context 'with nothing' do
|
47
|
+
describe 'in before hook' do
|
48
|
+
specify do
|
49
|
+
expect do
|
50
|
+
TestService.class_eval do
|
51
|
+
add_execute_with_rescue_before_hook
|
52
|
+
end
|
53
|
+
end.to raise_error(ArgumentError)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
describe 'in after hook' do
|
57
|
+
specify do
|
58
|
+
expect do
|
59
|
+
TestService.class_eval do
|
60
|
+
add_execute_with_rescue_after_hook
|
61
|
+
end
|
62
|
+
end.to raise_error(ArgumentError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with symbol of a non-existing method name' do
|
68
|
+
let!(:temp_class) do
|
69
|
+
Class.new(TestServiceWithPrivateMethodCallInExecute).tap do |klass|
|
70
|
+
klass.class_eval do
|
71
|
+
add_execute_with_rescue_after_hook(:non_existing_method)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
let!(:service_class) { temp_class }
|
76
|
+
|
77
|
+
specify do
|
78
|
+
expect { service_call }
|
79
|
+
.to raise_error(ExecuteWithRescue::Errors::NoHookMethod)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with symbol of an existing method name' do
|
84
|
+
context 'with before hook' do
|
85
|
+
let!(:service_class) { TestServiceWithSymbolBeforeHook }
|
86
|
+
|
87
|
+
specify do
|
88
|
+
expect { service_call }
|
89
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(1)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with after hook' do
|
94
|
+
let!(:service_class) { TestServiceWithSymbolAfterHook }
|
95
|
+
|
96
|
+
specify do
|
97
|
+
expect { service_call }
|
98
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(1)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with before & after hook' do
|
103
|
+
let!(:service_class) { TestServiceWithSymbolBeforeAfterHook }
|
104
|
+
|
105
|
+
specify do
|
106
|
+
expect { service_call }
|
107
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(2)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'with block' do
|
113
|
+
context 'with before hook' do
|
114
|
+
let!(:service_class) { TestServiceWithBlockBeforeHook }
|
115
|
+
|
116
|
+
specify do
|
117
|
+
expect { service_call }
|
118
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(1)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with after hook' do
|
123
|
+
let!(:service_class) { TestServiceWithBlockAfterHook }
|
124
|
+
|
125
|
+
specify do
|
126
|
+
expect { service_call }
|
127
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(1)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'with before & after hook' do
|
132
|
+
let!(:service_class) { TestServiceWithBlockBeforeAfterHook }
|
133
|
+
|
134
|
+
specify do
|
135
|
+
expect { service_call }
|
136
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(2)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'with other type of things' do
|
142
|
+
let!(:temp_class){ Class.new(TestService) }
|
143
|
+
|
144
|
+
specify do
|
145
|
+
expect do
|
146
|
+
temp_class.add_execute_with_rescue_before_hook(1)
|
147
|
+
end.to raise_error(ExecuteWithRescue::Errors::UnsupportedHookValue)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'with multiple hooks' do
|
153
|
+
context 'without inheritance' do
|
154
|
+
let!(:service_class) { TestServiceWithManySymbolBeforeHook }
|
155
|
+
|
156
|
+
specify do
|
157
|
+
expect { service_call }
|
158
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(3)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
context 'with inheritance' do
|
162
|
+
let!(:service_class) { TestServiceWithManySymbolBeforeHookInherited }
|
163
|
+
|
164
|
+
specify do
|
165
|
+
expect { service_call }
|
166
|
+
.to change{ service_instance.hook_exec_count }.from(0).to(5)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'with tampered internal class attribuite' do
|
172
|
+
let!(:temp_class) do
|
173
|
+
Class.new(TestServiceWithPrivateMethodCallInExecute).tap do |klass|
|
174
|
+
klass.class_eval do
|
175
|
+
_execute_with_rescue_before_hooks << Hash.new
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
let!(:service_class) { temp_class }
|
180
|
+
|
181
|
+
specify 'after hooks run in reverse order of the define order' do
|
182
|
+
expect { service_call }
|
183
|
+
.to raise_error(ExecuteWithRescue::Errors::UnsupportedHookValue)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
|
189
|
+
describe 'after hook execution' do
|
190
|
+
describe 'after an error is raised in block' do
|
191
|
+
|
192
|
+
let!(:service_class) { TestServiceWithErrorAndAfterHook }
|
193
|
+
|
194
|
+
specify 'after hooks are run after exception is raised' do
|
195
|
+
expect(service_instance.hook_exec_count).to eq(0)
|
196
|
+
|
197
|
+
expect { service_call }
|
198
|
+
.to raise_error(RuntimeError)
|
199
|
+
|
200
|
+
expect(service_instance.hook_exec_count).to eq(1)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'order' do
|
205
|
+
let!(:service_class) { TestServiceWithManyAfterHooks }
|
206
|
+
|
207
|
+
specify 'after hooks run in reverse order of the define order' do
|
208
|
+
expect { service_call }
|
209
|
+
.to change{ service_instance.some_data_array }.from([]).to([2,1])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
class TestService
|
2
|
+
include ExecuteWithRescue::Mixins::Core
|
3
|
+
|
4
|
+
def call
|
5
|
+
# To be overidden
|
6
|
+
end
|
7
|
+
end
|
8
|
+
class TestServiceWithoutBlockInExecute < TestService
|
9
|
+
def call
|
10
|
+
execute_with_rescue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
class TestServiceWithPrivateMethodCallInExecute < TestService
|
14
|
+
def call
|
15
|
+
execute_with_rescue do
|
16
|
+
do_something_privately
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def do_something_privately
|
23
|
+
# do nothing
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class TestServiceWithError < TestService
|
28
|
+
include ExecuteWithRescue::Mixins::Core
|
29
|
+
|
30
|
+
def call
|
31
|
+
execute_with_rescue do
|
32
|
+
raise StandardError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class TestServiceWithRescue < TestServiceWithError
|
37
|
+
rescue_from StandardError, with: :handle_error
|
38
|
+
|
39
|
+
CustomError = Class.new(StandardError)
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def handle_error
|
44
|
+
raise CustomError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
class TestServiceWithHook < TestServiceWithRescue
|
48
|
+
def initialize
|
49
|
+
@hook_exec_count = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :hook_exec_count
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def handle_error
|
57
|
+
# do nothing
|
58
|
+
end
|
59
|
+
def inc_hook_exec_count
|
60
|
+
@hook_exec_count += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class TestServiceWithSymbolBeforeHook < TestServiceWithHook
|
64
|
+
add_execute_with_rescue_before_hook(:inc_hook_exec_count)
|
65
|
+
end
|
66
|
+
class TestServiceWithSymbolAfterHook < TestServiceWithHook
|
67
|
+
add_execute_with_rescue_after_hook(:inc_hook_exec_count)
|
68
|
+
end
|
69
|
+
class TestServiceWithSymbolBeforeAfterHook < TestServiceWithHook
|
70
|
+
add_execute_with_rescue_before_hook(:inc_hook_exec_count)
|
71
|
+
add_execute_with_rescue_after_hook(:inc_hook_exec_count)
|
72
|
+
end
|
73
|
+
class TestServiceWithBlockBeforeHook < TestServiceWithHook
|
74
|
+
add_execute_with_rescue_before_hook { inc_hook_exec_count }
|
75
|
+
end
|
76
|
+
class TestServiceWithBlockAfterHook < TestServiceWithHook
|
77
|
+
add_execute_with_rescue_after_hook { inc_hook_exec_count }
|
78
|
+
end
|
79
|
+
class TestServiceWithBlockBeforeAfterHook < TestServiceWithHook
|
80
|
+
add_execute_with_rescue_before_hook { inc_hook_exec_count }
|
81
|
+
add_execute_with_rescue_after_hook { inc_hook_exec_count }
|
82
|
+
end
|
83
|
+
|
84
|
+
class TestServiceWithManySymbolBeforeHook < TestServiceWithHook
|
85
|
+
add_execute_with_rescue_before_hooks(:inc_hook_exec_count,
|
86
|
+
:inc_hook_exec_count,
|
87
|
+
:inc_hook_exec_count)
|
88
|
+
end
|
89
|
+
class TestServiceWithManySymbolBeforeHookInherited <
|
90
|
+
TestServiceWithManySymbolBeforeHook
|
91
|
+
add_execute_with_rescue_before_hook(:inc_hook_exec_count)
|
92
|
+
add_execute_with_rescue_before_hook(:inc_hook_exec_count)
|
93
|
+
end
|
94
|
+
|
95
|
+
class TestServiceWithManyAfterHooks < TestServiceWithHook
|
96
|
+
add_execute_with_rescue_after_hooks do
|
97
|
+
push_some_data(1)
|
98
|
+
end
|
99
|
+
add_execute_with_rescue_after_hooks do
|
100
|
+
push_some_data(2)
|
101
|
+
end
|
102
|
+
|
103
|
+
def some_data_array
|
104
|
+
@some_data_array ||= []
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def push_some_data(data)
|
110
|
+
some_data_array << data
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
class TestServiceWithErrorAndAfterHook < TestServiceWithBlockAfterHook
|
116
|
+
def handle_error
|
117
|
+
raise RuntimeError
|
118
|
+
end
|
119
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: execute_with_rescue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- PikachuEXE
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.0
|
20
|
+
- - <
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.5'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.5'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: appraisal
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.5.2
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.5.2
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ~>
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.14.0
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.14.0
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: coveralls
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.7'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0.7'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: gem-release
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.7'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.7'
|
117
|
+
description: |2
|
118
|
+
Saves your from writing `begin...rescue...ensure...end` everywhere.
|
119
|
+
This assumes you know how to use `rescue_from` not just within a controller.
|
120
|
+
email:
|
121
|
+
- pikachuexe@gmail.com
|
122
|
+
executables: []
|
123
|
+
extensions: []
|
124
|
+
extra_rdoc_files: []
|
125
|
+
files:
|
126
|
+
- .gitignore
|
127
|
+
- .rspec
|
128
|
+
- .travis.yml
|
129
|
+
- Appraisals
|
130
|
+
- CHANGELOG.md
|
131
|
+
- Gemfile
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.md
|
134
|
+
- Rakefile
|
135
|
+
- execute_with_rescue.gemspec
|
136
|
+
- gemfiles/rails_3_2.gemfile
|
137
|
+
- gemfiles/rails_4_0.gemfile
|
138
|
+
- gemfiles/rails_4_1.gemfile
|
139
|
+
- lib/execute_with_rescue.rb
|
140
|
+
- lib/execute_with_rescue/errors.rb
|
141
|
+
- lib/execute_with_rescue/mixins/core.rb
|
142
|
+
- lib/execute_with_rescue/version.rb
|
143
|
+
- spec/core_spec.rb
|
144
|
+
- spec/fixtures/test_service_classes.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
homepage: http://github.com/PikachuEXE/execute_with_rescue
|
147
|
+
licenses:
|
148
|
+
- MIT
|
149
|
+
metadata: {}
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 1.9.3
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: 1.4.0
|
164
|
+
requirements: []
|
165
|
+
rubyforge_project:
|
166
|
+
rubygems_version: 2.2.2
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: Execute code without writting rescue in methods with before and after hooks.
|
170
|
+
You can also create some extensions yourself.
|
171
|
+
test_files:
|
172
|
+
- spec/core_spec.rb
|
173
|
+
- spec/fixtures/test_service_classes.rb
|
174
|
+
- spec/spec_helper.rb
|
175
|
+
has_rdoc:
|