eh 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .idea
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in eh.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ernst van Graan, Wynand van Dyk, Hetzner (Pty) Ltd
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,104 @@
1
+ # Eh
2
+
3
+ The Error handler gem provids the following major functional support for error handling:
4
+
5
+ o Wrapping of code blocks in exception handling logic, supporting retry, logging and exception consumption
6
+ o Provision of fault tolerant logging that logs to configured logger(s), or to stderr should loggers be unavailable
7
+ o Capability to handle unhandled exceptions, log them, email them and either raise them again or convert them to system exit codes (listing of standard linux exit codes provided as constants)
8
+
9
+ The Error Handler gem allows the wrapping of arbitrary blocks of code in exception handling logic. Specifically, once an exception occurs:
10
+
11
+ o The block can be retried 'threshold' times, with a delay of 'delay' seconds in-between retries (EH::retry and EH::retry!)
12
+ o The exception can be logged to a single logger, or an array of loggers
13
+ o The excepion can be re-raised (suppressed during retry, but logged, raised again after retry failure)
14
+ o The same functionality is available, with retry disabled (EH::run and EH::run!)
15
+ o The list of Linux system exit codes is also provided as EH::EX_<exit>
16
+ o Unhandled exceptions can be logged and emailed in main() using EH::report_unhandled()
17
+
18
+ This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'eh'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install eh
33
+
34
+ ## Usage
35
+
36
+ -- Run code block with retry, logging and exception re-raise, specifying logger, number of
37
+ times to retry, retry delay, and block arguments
38
+ def load_configuration(configuration_path, logger = nil)
39
+ EH::retry!(:logger => logger,
40
+ :message => "Could not parse YAML configuration",
41
+ :args => [configuration_path],
42
+ :threshold => 5,
43
+ :delay => 0.5) do
44
+ YAML.load(File.open(configuration_path))
45
+ end
46
+ end
47
+
48
+ -- Run code block with retry only on the exceptions listed, with the default threshold and delay
49
+
50
+ def load_configuration(configuration_path, logger = nil)
51
+ EH::retry!(:args => [configuration_path], :exceptions => [IOError, RuntimeError]) do
52
+ YAML.load(File.open(configuration_path))
53
+ end
54
+ end
55
+
56
+ -- Run code block without retry, and swallowing all exceptions
57
+
58
+ def load_configuration(configuration_path, logger = nil)
59
+ EH::run(:args => [configuration_path]) do
60
+ YAML.load(File.open(configuration_path))
61
+ end
62
+ end
63
+
64
+ Any combination of the following options are allowed, both with both flavours of run() and retry().
65
+
66
+ :logger - log using the logger's error() method
67
+ :message - log this message, with the exception appended
68
+ :args - code block arguments
69
+ :threshold - the number of execution attempts
70
+ :delay - the number of seconds to delay between retries
71
+
72
+
73
+ Function descriptions:
74
+
75
+ run - Execute block, swallow all exceptions, no retry
76
+ run! - Execute block, re-raise exceptions, no retry
77
+ retry - Execute block, swallow all exceptions, retry
78
+ retry! - Execute block, re-raise exceptions, retry
79
+
80
+ Exception filters:
81
+
82
+ When :exception_filter is provided to retry!(), only the exceptions in the filter are retried. If retry fails, all exceptions are re-raised.
83
+ When :exception_filter is provided to retry(), only the exceptions in the filter are retried. If retry fails. Exceptions are not re-raised.
84
+ When :exception_filter is provided to run!(), only the exceptions in the filter are logged, if a logger is provided. All exceptions are re-raised.
85
+ When :exception_filter is provided to run(), only the exceptions in the filter are logged, if a logger is provided. Exceptions are not re-raised.
86
+
87
+ Unhandled exceptions can be logged and emailed as follows:
88
+
89
+ at_exit do
90
+ EH::report_unhandled(logfile = "/var/log/myapp/crash.log", email = "ernst.van.graan@hetzner.co.za")
91
+ exit EH::EX_CONFIG
92
+ end
93
+
94
+ Please send feedback and comments to the authors at:
95
+ Ernst van Graan <ernst.van.graan@hetzner.co.za>
96
+ Wynand van Dyk <wynand.van.dyk@hetzner.co.za>
97
+
98
+ ## Contributing
99
+
100
+ 1. Fork it
101
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch (`git push origin my-new-feature`)
104
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/eh.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'eh/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "eh"
8
+ spec.version = ErrorHandler::VERSION
9
+ spec.authors = ["Ernst ban Graan"]
10
+ spec.email = ["ernst.van.graan@hetzner.co.za"]
11
+ spec.description = %q{Error handler gem that allows wrapping of code blocks with support for block retry, logging, exception filtering and re-raise}
12
+ spec.summary = %q{Error handler gem that allows wrapping of code blocks with support for block retry, logging, exception filtering and re-raise}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "simplecov"
25
+ end
data/lib/eh/eh.rb ADDED
@@ -0,0 +1,171 @@
1
+ module ErrorHandler
2
+ class EH
3
+ EX_OK = 0 ; EX_OK.freeze
4
+ EX_BASE = 64; EX_BASE.freeze # base value for error messages
5
+ EX_USAGE = 64; EX_USAGE.freeze # command line usage error
6
+ EX_DATAERR = 65; EX_DATAERR.freeze # data format error
7
+ EX_NOINPUT = 66; EX_NOINPUT.freeze # cannot open input
8
+ EX_NOUSER = 67; EX_NOUSER.freeze # addressee unknown
9
+ EX_NOHOST = 68; EX_NOHOST.freeze # host name unknown
10
+ EX_UNAVAILABLE = 69; EX_UNAVAILABLE.freeze # service unavailable
11
+ EX_SOFTWARE = 70; EX_SOFTWARE.freeze # internal software error
12
+ EX_OSERR = 71; EX_OSERR.freeze # system error (e.g., can't fork)
13
+ EX_OSFILE = 72; EX_OSFILE.freeze # critical OS file missing
14
+ EX_CANTCREAT = 73; EX_CANTCREAT.freeze # /* can't create (user) output file
15
+ EX_IOERR = 74; EX_IOERR.freeze # input/output error
16
+ EX_TEMPFAIL = 75; EX_TEMPFAIL.freeze # temp failure; user is invited to retry
17
+ EX_PROTOCOL = 76; EX_PROTOCOL.freeze # remote error in protocol
18
+ EX_NOPERM = 77; EX_NOPERM.freeze # permission denied
19
+
20
+ ERROR = 'error'
21
+ DEBUG = 'debug'
22
+ INFO = 'info'
23
+ WARN = 'warn'
24
+ FATAL = 'fatal'
25
+
26
+ # Coming soon...
27
+ #def self.report_unhandled(logfile = nil, email = nil)
28
+ # if $!
29
+ # message = "Unhandled exception: #{$!}"
30
+ # warn message
31
+ # if not logfile.nil?
32
+ # open(logfile, 'a') { |f|
33
+ # f.puts message
34
+ # }
35
+ # end
36
+ #
37
+ # if not email.nil?
38
+ # send_email(email, message)
39
+ # end
40
+ # end
41
+ #end
42
+
43
+ def self.retry!(options, &block)
44
+ opts = options || {}
45
+ EH::retry_with_raise(opts, block)
46
+
47
+ rescue => e
48
+ raise e if opts.nil? == false and opts[:exception_filter] and not opts[:exception_filter].include? e.class
49
+
50
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts.nil? == false and not opts[:logger].nil? and not opts[:message].nil?
51
+ raise e
52
+ end
53
+
54
+ def self.retry(options, &block)
55
+ opts = options || {}
56
+ begin
57
+ EH::retry_with_raise(opts, block)
58
+ return true
59
+ rescue => e
60
+ if not opts[:logger].nil?
61
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
62
+ end
63
+ return false
64
+ end
65
+ end
66
+
67
+ def self.run!(options, &block)
68
+ opts = options || {}
69
+ block.call(EH::construct_args(opts))
70
+
71
+ rescue => e
72
+ if not opts[:logger].nil?
73
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
74
+ end
75
+
76
+ raise e if opts.nil? == false and opts[:exception_filter] and not opts[:exception_filter].include? e.class
77
+ raise e
78
+ end
79
+
80
+ def self.run(options, &block)
81
+ opts = options || {}
82
+ block.call(EH::construct_args(opts))
83
+
84
+ rescue => e
85
+ if not opts[:logger].nil?
86
+ EH::log(opts[:logger], "#{opts[:message]}: #{e.message}", EH::log_level(opts)) if opts[:exception_filter].nil? or opts[:exception_filter].include? e.class
87
+ end
88
+ end
89
+
90
+ def self.log(facilities, msg, msg_type)
91
+ if facilities.is_a? Array
92
+ EH::log_multiple_loggers(facilities, msg, msg_type)
93
+ else
94
+ EH::log_single_logger(facilities, msg, msg_type)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def self.construct_args(opts)
101
+ return nil if opts.nil?
102
+ return nil if opts[:args].nil?
103
+ opts[:args].join(' ')
104
+ end
105
+
106
+ def self.retry_with_raise(opts, block)
107
+ retry_threshold = opts[:threshold] || 3
108
+ delay = opts[:delay] || 0.2
109
+ attempts = 0
110
+ begin
111
+ block.call(EH::construct_args(opts))
112
+ rescue => e
113
+ raise e if opts[:exception_filter] and not opts[:exception_filter].include? e.class
114
+
115
+ attempts += 1
116
+ sleep delay
117
+ retry if attempts < retry_threshold
118
+ raise e
119
+ end
120
+ end
121
+
122
+ # Coming soon...
123
+ # def send_email(to, message, opts={})
124
+ # require 'net/smtp'
125
+ #
126
+ # opts[:server] ||= Socket.gethostbyname(Socket.gethostname).first
127
+ # me = "database-agent@#{opts[:server]}"
128
+ # opts[:from] ||= me
129
+ # opts[:from_alias] ||= me
130
+ # opts[:subject] ||= "Uncaught exception"
131
+ # opts[:body] ||= message
132
+ #
133
+ # msg = <<END_OF_MESSAGE
134
+ #From: #{opts[:from_alias]} <#{opts[:from]}>
135
+ #To: <#{to}>
136
+ #Subject: #{opts[:subject]}
137
+ #
138
+ # #{opts[:body]}
139
+ #END_OF_MESSAGE
140
+ #
141
+ # Net::SMTP.start(opts[:server]) do |smtp|
142
+ # smtp.send_message msg, opts[:from], to
143
+ # end
144
+ # end
145
+
146
+ def self.log_single_logger(logger, msg, msg_type)
147
+ if logger.nil?
148
+ warn msg_type + ': ' + msg
149
+ else
150
+ self.log([logger], msg, msg_type)
151
+ end
152
+ end
153
+
154
+ def self.log_multiple_loggers(loggers, msg, msg_type)
155
+ loggers.each do |logger|
156
+ next if logger.nil?
157
+ logger.warn msg if msg_type == WARN
158
+ logger.debug msg if msg_type == DEBUG
159
+ logger.error msg if msg_type == ERROR
160
+ logger.info msg if msg_type == INFO
161
+ logger.fatal msg if msg_type == FATAL
162
+ end
163
+ end
164
+
165
+ def self.log_level(opts)
166
+ opts[:level] || EH::ERROR
167
+ end
168
+ end
169
+ end
170
+
171
+ EH = ErrorHandler::EH
data/lib/eh/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module ErrorHandler
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "eh/version"
2
+
3
+ module ErrorHandler
4
+ end
data/spec/eh_spec.rb ADDED
@@ -0,0 +1,380 @@
1
+ require "spec_helper"
2
+ require "eh/eh"
3
+ require "mock_logger"
4
+
5
+ describe ErrorHandler do
6
+ before :each do
7
+ @logger = MockLogger.new
8
+ end
9
+
10
+ context "When executing a block of code without retry" do
11
+ describe "run" do
12
+ it "should swallow all exceptions if asked to" do
13
+ begin
14
+ exception = nil
15
+ EH::run(nil) do
16
+ raise RuntimeError
17
+ end
18
+ rescue => e
19
+ exception = e
20
+ end
21
+ exception.nil?.should == true
22
+ end
23
+
24
+ it "should not retry" do
25
+ begin
26
+ count = 0
27
+ EH::run(:exception_filter => [RuntimeError], :args => [count]) do
28
+ count += 1
29
+ raise RuntimeError
30
+ end
31
+ rescue => e
32
+ end
33
+ count.should == 1
34
+ end
35
+
36
+ it "should log the message specified with the exception appended, using the logger specified" do
37
+ @logger.should_receive(:error).with("the message: RuntimeError")
38
+ begin
39
+ EH::run(:logger => @logger, :message => "the message") do
40
+ raise RuntimeError
41
+ end
42
+ end
43
+ end
44
+
45
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
46
+ @logger.should_receive(:error).with("the message: RuntimeError")
47
+ begin
48
+ EH::run(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
49
+ raise RuntimeError
50
+ end
51
+ end
52
+ end
53
+
54
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
55
+ @logger.should_not_receive(:error)
56
+ begin
57
+ EH::run(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
58
+ raise IOError
59
+ end
60
+ end
61
+ end
62
+
63
+ it "should log using the level specified" do
64
+ @logger.should_receive(:warn).with("the message: RuntimeError")
65
+ begin
66
+ EH::run(:logger => @logger, :message => "the message", :level => EH::WARN) do
67
+ raise RuntimeError
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "run!" do
74
+ it "should re-raise all exceptions if asked to" do
75
+ begin
76
+ exception = nil
77
+ EH::run!(nil) do
78
+ raise RuntimeError
79
+ end
80
+ rescue => e
81
+ exception = e
82
+ end
83
+ exception.class.should == RuntimeError
84
+ end
85
+
86
+ it "should not retry" do
87
+ begin
88
+ count = 0
89
+ EH::run!(:exception_filter => [RuntimeError], :args => [count]) do
90
+ count += 1
91
+ raise RuntimeError
92
+ end
93
+ rescue => e
94
+ end
95
+ count.should == 1
96
+ end
97
+
98
+ it "should log the message specified with the exception appended, using the logger specified" do
99
+ @logger.should_receive(:error).with("the message: RuntimeError")
100
+ begin
101
+ EH::run!(:logger => @logger, :message => "the message") do
102
+ raise RuntimeError
103
+ end
104
+ rescue => e
105
+ end
106
+ end
107
+
108
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
109
+ @logger.should_receive(:error).with("the message: RuntimeError")
110
+ begin
111
+ EH::run!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
112
+ raise RuntimeError
113
+ end
114
+ rescue => e
115
+ end
116
+ end
117
+
118
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
119
+ @logger.should_not_receive(:error)
120
+ begin
121
+ EH::run!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
122
+ raise IOError
123
+ end
124
+ rescue => e
125
+ end
126
+ end
127
+
128
+ it "should log using the level specified" do
129
+ @logger.should_receive(:warn).with("the message: RuntimeError")
130
+ begin
131
+ EH::run!(:logger => @logger, :message => "the message", :level => EH::WARN) do
132
+ raise RuntimeError
133
+ end
134
+ rescue => e
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context "When executing a block of code with retry" do
141
+ describe "retry" do
142
+ it "should swallow all exceptions if asked to" do
143
+ begin
144
+ exception = nil
145
+ EH::retry(nil) do
146
+ raise RuntimeError
147
+ end
148
+ rescue => e
149
+ exception = e
150
+ end
151
+ exception.nil?.should == true
152
+ end
153
+
154
+ it "should retry" do
155
+ begin
156
+ count = 0
157
+ EH::retry(:exception_filter => [RuntimeError], :args => [count]) do
158
+ count += 1
159
+ raise RuntimeError
160
+ end
161
+ end
162
+ count.should == 3
163
+ end
164
+
165
+ it "should return true if the code succeeds after retry" do
166
+ begin
167
+ count = 0
168
+ result = EH::retry(:exception_filter => [RuntimeError], :args => [count]) do
169
+ count += 1
170
+ raise RuntimeError if count < 2
171
+ end
172
+ end
173
+ result.should == true
174
+ end
175
+
176
+ it "should return false if the code does not succeed after retry" do
177
+ begin
178
+ result = EH::retry(:exception_filter => [RuntimeError]) do
179
+ raise RuntimeError
180
+ end
181
+ end
182
+ result.should == false
183
+ end
184
+
185
+ it "should attempt the number of retries specified in :threshold" do
186
+ begin
187
+ count = 0
188
+ EH::retry(:exception_filter => [RuntimeError], :args => [count], :threshold => 5) do
189
+ count += 1
190
+ raise RuntimeError
191
+ end
192
+ end
193
+ count.should == 5
194
+ end
195
+
196
+ it "should delay between intervals as specified in :delay" do
197
+ begin
198
+ pre = Time.now
199
+ EH::retry(:exception_filter => [RuntimeError], :delay => 0.1, :threshold => 6) do
200
+ raise RuntimeError
201
+ end
202
+ end
203
+ post = Time.now
204
+ (post - pre > 0.6).should == true
205
+ end
206
+
207
+ it "should log the message specified with the exception appended, using the logger specified" do
208
+ @logger.should_receive(:error).with("the message: RuntimeError")
209
+ begin
210
+ EH::retry(:logger => @logger, :message => "the message") do
211
+ raise RuntimeError
212
+ end
213
+ end
214
+ end
215
+
216
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
217
+ @logger.should_receive(:error).with("the message: RuntimeError")
218
+ begin
219
+ EH::retry(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
220
+ raise RuntimeError
221
+ end
222
+ end
223
+ end
224
+
225
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
226
+ @logger.should_not_receive(:error)
227
+ begin
228
+ EH::retry(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
229
+ raise IOError
230
+ end
231
+ end
232
+ end
233
+
234
+ it "should log using the level specified" do
235
+ @logger.should_receive(:warn).with("the message: RuntimeError")
236
+ begin
237
+ EH::retry(:logger => @logger, :message => "the message", :level => EH::WARN) do
238
+ raise RuntimeError
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ describe "retry!" do
245
+ it "should re-raise all exceptions if asked to" do
246
+ begin
247
+ exception = nil
248
+ EH::retry!(nil) do
249
+ raise RuntimeError
250
+ end
251
+ rescue => e
252
+ exception = e
253
+ end
254
+ exception.class.should == RuntimeError
255
+ end
256
+
257
+ it "should retry" do
258
+ begin
259
+ count = 0
260
+ EH::retry!(:exception_filter => [RuntimeError], :args => [count]) do
261
+ count += 1
262
+ raise RuntimeError
263
+ end
264
+ rescue => e
265
+ end
266
+ count.should == 3
267
+ end
268
+
269
+ it "should attempt the number of retries specified in :threshold" do
270
+ begin
271
+ count = 0
272
+ EH::retry(:exception_filter => [RuntimeError], :args => [count], :threshold => 5) do
273
+ count += 1
274
+ raise RuntimeError
275
+ end
276
+ rescue => e
277
+ end
278
+ count.should == 5
279
+ end
280
+
281
+ it "should delay between intervals as specified in :delay" do
282
+ begin
283
+ pre = Time.now
284
+ EH::retry(:exception_filter => [RuntimeError], :delay => 0.1, :threshold => 6) do
285
+ raise RuntimeError
286
+ end
287
+ rescue => e
288
+ end
289
+ post = Time.now
290
+ (post - pre > 0.6).should == true
291
+ end
292
+
293
+ it "should log the message specified with the exception appended, using the logger specified" do
294
+ @logger.should_receive(:error).with("the message: RuntimeError")
295
+ begin
296
+ EH::retry!(:logger => @logger, :message => "the message") do
297
+ raise RuntimeError
298
+ end
299
+ rescue => e
300
+ end
301
+ end
302
+
303
+ it "should log the message specified with the exception appended, if the exception is in :exception_filter, using the logger specified" do
304
+ @logger.should_receive(:error).with("the message: RuntimeError")
305
+ begin
306
+ EH::retry!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
307
+ raise RuntimeError
308
+ end
309
+ rescue => e
310
+ end
311
+ end
312
+
313
+ it "should not log, if :exception_filter is specified and the exception is not in :exception_filter" do
314
+ @logger.should_not_receive(:error)
315
+ begin
316
+ EH::retry!(:logger => @logger, :message => "the message", :exception_filter => [RuntimeError]) do
317
+ raise IOError
318
+ end
319
+ rescue => e
320
+ end
321
+ end
322
+
323
+ it "should log using the level specified" do
324
+ @logger.should_receive(:warn).with("the message: RuntimeError")
325
+ begin
326
+ EH::retry!(:logger => @logger, :message => "the message", :level => EH::WARN) do
327
+ raise RuntimeError
328
+ end
329
+ rescue => e
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+ context "when asked to log" do
336
+ it "should log to a single logger provided, with the message specified, at the level specified" do
337
+ @logger.should_receive(:info).with("testing single logging")
338
+ EH::log(@logger, "testing single logging", EH::INFO)
339
+ end
340
+
341
+ it "should log to a single logger provided, with the message specified, at the level specified" do
342
+ @logger2 = MockLogger.new
343
+
344
+ @logger.should_receive(:debug).with("testing single logging")
345
+ @logger2.should_receive(:debug).with("testing single logging")
346
+ EH::log([@logger, @logger2], "testing single logging", EH::DEBUG)
347
+ end
348
+
349
+ it "should log at all log levels" do
350
+ @logger.should_receive(:info)
351
+ EH::log(@logger, "info", EH::INFO)
352
+
353
+ @logger.should_receive(:debug)
354
+ EH::log(@logger, "debug", EH::DEBUG)
355
+
356
+ @logger.should_receive(:error)
357
+ EH::log(@logger, "error", EH::ERROR)
358
+
359
+ @logger.should_receive(:warn)
360
+ EH::log(@logger, "warn", EH::WARN)
361
+
362
+ @logger.should_receive(:fatal)
363
+ EH::log(@logger, "fatal", EH::FATAL)
364
+ end
365
+
366
+ it "should log using 'warn' if a nil single logger is provided" do
367
+ EH::should_receive(:warn).with "fatal: fatal"
368
+
369
+ EH::log(nil, "fatal", EH::FATAL)
370
+ end
371
+
372
+ it "should not log if nil is provided in a list of loggers, and 'warn' should not be called" do
373
+ @logger::should_receive(:fatal).once().with "fatal"
374
+ EH::should_not_receive(:warn)
375
+
376
+ EH::log([nil, @logger], "fatal", EH::FATAL)
377
+ end
378
+ end
379
+
380
+ end
@@ -0,0 +1,16 @@
1
+ class MockLogger
2
+ def error(message)
3
+ end
4
+
5
+ def info(message)
6
+ end
7
+
8
+ def debug(message)
9
+ end
10
+
11
+ def warn(message)
12
+ end
13
+
14
+ def fatal(message)
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ernst ban Graan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
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
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Error handler gem that allows wrapping of code blocks with support for
79
+ block retry, logging, exception filtering and re-raise
80
+ email:
81
+ - ernst.van.graan@hetzner.co.za
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - eh.gemspec
92
+ - lib/eh/eh.rb
93
+ - lib/eh/version.rb
94
+ - lib/error_handler.rb
95
+ - spec/eh_spec.rb
96
+ - spec/mock_logger.rb
97
+ homepage: ''
98
+ licenses:
99
+ - MIT
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: 798203344795618863
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ segments:
120
+ - 0
121
+ hash: 798203344795618863
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 1.8.24
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Error handler gem that allows wrapping of code blocks with support for block
128
+ retry, logging, exception filtering and re-raise
129
+ test_files:
130
+ - spec/eh_spec.rb
131
+ - spec/mock_logger.rb