lib/tretry.rb in tretry-0.0.1 vs lib/tretry.rb in tretry-0.0.2
- old
+ new
@@ -1,93 +1,140 @@
#A library for doing retries in Ruby with timeouts, analysis of errors, waits between tries and more.
class Tretry
+ attr_reader :fails
+ attr_accessor :timeout, :tries, :wait
+
#Valid keys that can be given as argument for the method 'try'.
VALID_KEYS = [:tries, :timeout, :wait, :interrupt, :exit, :errors, :return_error]
-
+
#===Runs a block of code a given amount of times until it succeeds.
#===Examples
# res = Tretry.try(:tries => 3) do
# #something that often fails
# end
- #
+ #
# puts "Tries: '#{res[:tries]}'."
# puts "Result: '#{res[:result}'."
def self.try(args = {}, &block)
- #Validate given arguments and set various variables.
- raise "No block was given." if !block
- raise "Expected argument to be a hash." if !args.is_a?(Hash)
-
- args.each do |key, val|
- raise "Invalid key: '#{key}'." if !VALID_KEYS.include?(key)
- end
-
- args[:tries] = 3 if !args[:tries]
- tries = []
- error = nil
- res = nil
-
- args[:tries].to_i.downto(1) do |count|
- error = nil
- dobreak = false
-
+ Tretry.new(args).try(&block)
+ end
+
+ def initialize(args = {})
+ @args = args
+ @fails = []
+ @before_retry = []
+
+ parse_arguments
+ end
+
+ def before_retry(&block)
+ @before_retry << block
+ end
+
+ def try(&block)
+ raise "No block given." unless block
+ @block = block
+
+ @tries.times do |count|
+ @count = count
+
+ unless first_try?
+ # Sleep for a given amount of time if the 'wait'-argument is given.
+ sleep(@wait) if @wait
+
+ call_before_retry(error: @error)
+ @error = nil
+ end
+
begin
- if args[:timeout]
- #If a timeout-argument has been given, then run the code through the timeout.
- begin
- require "timeout"
- Timeout.timeout(args[:timeout]) do
- res = block.call
- dobreak = true
- break
- end
- rescue Timeout::Error => e
- doraise = e if count <= 1
- error = e
- sleep(args[:wait]) if args[:wait] and !doraise
- end
+ # If a timeout-argument has been given, then run the code through the timeout.
+ if @timeout
+ try_with_timeout
else
- #Else call block normally.
- res = block.call
- dobreak = true
- break
+ # Else call block normally.
+ @res = @block.call
+ @dobreak = true
end
rescue Exception => e
- if e.class == Interrupt
- raise e if !args.key?(:interrupt) or args[:interrupt]
- elsif e.class == SystemExit
- raise e if !args.key?(:exit) or args[:exit]
- elsif count <= 1 or (args.key?(:errors) and args[:errors].index(e.class) == nil)
- doraise = e
- elsif args.key?(:errors) and args[:errors].index(e.class) != nil
- #given error was in the :errors-array - do nothing. Maybe later it should be logged and returned in a stats-hash or something? - knj
- end
-
- error = e
-
- #Sleep for a given amount of time if the 'wait'-argument is given.
- sleep(args[:wait]) if args[:wait] and !doraise
+ handle_error(e)
end
-
- if doraise
- if args[:return_error]
- tries << {:error => error}
+
+ if @doraise
+ if @args[:return_error]
+ @fails << {error: @error}
return {
- :tries => tries,
- :error => true
+ fails: @fails,
+ error: true
}
else
- raise e
+ raise @error
end
- elsif error
- tries << {:error => error}
+ elsif @error
+ @fails << {error: @error}
end
-
- break if dobreak
+
+ break if @dobreak
end
-
+
return {
- :tries => tries,
- :result => res,
- :error => false
+ fails: @fails,
+ result: @res,
+ error: false
}
end
-end
\ No newline at end of file
+
+private
+
+ def parse_arguments
+ #Validate given arguments and set various variables.
+ raise "Expected argument to be a hash." unless @args.is_a?(Hash)
+
+ @args.each do |key, val|
+ raise "Invalid key: '#{key}'." unless VALID_KEYS.include?(key)
+ end
+
+ @args[:tries] ||= 3
+ @tries = @args[:tries].to_i
+ @wait = @args[:wait] ? @args[:wait] : nil
+ @timeout = @args[:timeout] ? @args[:timeout].to_f : nil
+ end
+
+ def try_with_timeout
+ begin
+ require "timeout"
+ Timeout.timeout(@timeout) do
+ @res = @block.call
+ @dobreak = true
+ end
+ rescue Timeout::Error => e
+ handle_error(e)
+ end
+ end
+
+ def handle_error(e)
+ if e.class == Interrupt
+ raise e if !@args.key?(:interrupt) || @args[:interrupt]
+ elsif e.class == SystemExit
+ raise e if !@args.key?(:exit) || @args[:exit]
+ elsif last_try? || (@args.key?(:errors) && !@args[:errors].include?(e.class))
+ @doraise = e
+ elsif @args.key?(:errors) && @args[:errors].index(e.class) != nil
+ #given error was in the :errors-array - do nothing. Maybe later it should be logged and returned in a stats-hash or something? - knj
+ end
+
+ @error = e
+ end
+
+ def call_before_retry(args)
+ @before_retry.each do |before_retry_block|
+ before_retry_block.call(args)
+ end
+ end
+
+ def last_try?
+ (@count + 1) >= @tries
+ end
+
+ def first_try?
+ @count == 0
+ end
+end