Skip to content

Commit

Permalink
Fixes a "NoMethodError: private method `warn' called for nil:NilClass…
Browse files Browse the repository at this point in the history
…" error

Use Rails.logger if available, otherwise use Std Lib Logger.
  • Loading branch information
rymai committed Feb 7, 2012
1 parent b1ebcb1 commit a64a01d
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
39 changes: 33 additions & 6 deletions lib/rescue_me.rb
Expand Up @@ -10,21 +10,48 @@ module Kernel
# strongly encouraged to provide arguments that capture temporary
# exceptional conditions that are likely to work upon a retry.
def rescue_and_retry(max_attempts=7, *temporary_exceptions)
retry_interval = 2 # A good initial start value. Tweak as needed.
retry_interval = 2 # A good initial start value. Tweak as needed.
temporary_exceptions << Exception if temporary_exceptions.empty?
begin
begin
yield
rescue *temporary_exceptions => e
attempt = (attempt || 0) + 1
message = "rescue and retry (attempt #{attempt}/#{max_attempts}): " +
"#{caller.first}, <#{e.class}: #{e.message}>"
# TODO AS: Is there a better way to access and use a logger?
(defined? logger) ? logger.warn(message) : puts(message)
RescueMe::Logger.warn(attempt, max_attempts, caller, e)

raise(e) if attempt >= max_attempts

# Retry immediately before exponential waits (1, 2, 4, 16, ... sec)
sleep retry_interval**(attempt - 2) if attempt >= 2
retry
end
end

end

module RescueMe
require "logger"

class << self
def logger
@logger ||= defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : ::Logger.new($stdout)
end
end

module Logger

class << self
def warn(attempt, max_attempts, kaller, exception)
if RescueMe.logger && RescueMe.logger.respond_to?(:warn)
RescueMe.logger.warn(rescued_message(attempt, max_attempts, kaller, exception))
end
end

def rescued_message(attempt, max_attempts, kaller, exception)
"rescue and retry (attempt #{attempt}/#{max_attempts}): " +
"#{kaller.first}, <#{exception.class}: #{exception.message}>"
end
end

end

end
24 changes: 12 additions & 12 deletions test/test_rescue_me.rb
Expand Up @@ -8,7 +8,7 @@ class ExceptionCounter
attr_reader :method_called_count

def initialize
@method_called_count = 0;
@method_called_count = 0
end

def exception_free
Expand All @@ -26,15 +26,14 @@ def raise_zero_division_error
def raise_smtp_exception_until_call(times)
@method_called_count += 1
@smtp_exception_count = (@smtp_exception_count ||= 0) + 1
raise Net::SMTPServerBusy until times > @smtp_exception_count
raise Net::SMTPServerBusy if times > @smtp_exception_count
end

end # ExceptionCounter

context "rescue_and_retry of code that might raise temporary exceptions" do
setup do
@exception_counter = ExceptionCounter.new
@previous_call_count = @exception_counter.method_called_count
end

should "run an exception-free block of code once" do
Expand All @@ -43,33 +42,34 @@ def raise_smtp_exception_until_call(times)
@exception_counter.exception_free
}
end
assert 1, @exception_counter.method_called_count
assert_equal 1, @exception_counter.method_called_count
end

should "attempt to run a block that raises an unexpected exception " +
"only once" do
should "attempt to run a block that raises an unexpected exception only once" do
assert_raise(ZeroDivisionError) do
rescue_and_retry(5, IOError) {
@exception_counter.raise_zero_division_error
}
end
assert 1, @exception_counter.method_called_count
assert_equal 1, @exception_counter.method_called_count
end

should "re-run the block of code for exactly max_attempt number of times" do
assert_raise(ZeroDivisionError) do
rescue_and_retry(3, IOError) {
rescue_and_retry(3, ZeroDivisionError) {
@exception_counter.raise_zero_division_error
}
end
assert 3, @exception_counter.method_called_count - @previous_call_count
assert_equal 3, @exception_counter.method_called_count

@exception_counter = ExceptionCounter.new

assert_raise(ZeroDivisionError) do
rescue_and_retry(1, IOError) {
rescue_and_retry(1, ZeroDivisionError) {
@exception_counter.raise_zero_division_error
}
end
assert 1, @exception_counter.method_called_count - @previous_call_count
assert_equal 1, @exception_counter.method_called_count
end

should "not re-run the block of code after it has run successfully" do
Expand All @@ -78,7 +78,7 @@ def raise_smtp_exception_until_call(times)
@exception_counter.raise_smtp_exception_until_call(3)
}
end
assert 3, @exception_counter.method_called_count
assert_equal 3, @exception_counter.method_called_count
end

end
Expand Down

0 comments on commit a64a01d

Please sign in to comment.