lib/take2.rb in take2-0.0.4 vs lib/take2.rb in take2-0.0.5
- old
+ new
@@ -1,23 +1,23 @@
-require 'net/http'
+# frozen_string_literal: true
+require 'net/http'
require 'take2/version'
require 'take2/configuration'
module Take2
-
def self.included(base)
- base.extend ClassMethods
- base.send :set_defaults
- base.send :include, InstanceMethods
+ base.extend(ClassMethods)
+ base.send(:set_defaults)
+ base.send(:include, InstanceMethods)
end
class << self
attr_accessor :configuration
end
- def self.configuration
+ def self.config
@configuration ||= Configuration.new
end
def self.reset(options = {})
@configuration = Configuration.new(options)
@@ -26,56 +26,72 @@
def self.local_defaults(options)
configuration.validate_options(options)
end
def self.configure
- yield(configuration) if block_given?
+ yield(config) if block_given?
end
module InstanceMethods
-
# Yields a block and retries on retriable errors n times.
# The raised error could be the defined retriable or it child.
#
# Example:
# class PizzaService
# include Take2
#
# number_of_retries 3
# retriable_errors Net::HTTPRetriableError
# retriable_condition proc { |error| response_status(error.response) < 500 }
- # on_retry proc { |error, tries| puts "#{self.name} - Retrying.. #{tries} of #{self.retriable_configuration[:retries]} (#{error})" }
- # sleep_before_retry 3
+ # on_retry proc { |error, tries|
+ # puts "#{self.name} - Retrying.. #{tries} of #{self.retriable_configuration[:retries]} (#{error})"
+ # }
+ # backoff_strategy type: :exponential, start: 3
#
# def give_me_food
# call_api_with_retry do
# # Some logic that might raise..
# # If it will raise retriable, magic happens.
# # If not the original error re raised
# end
# end
#
# end
- def call_api_with_retry(options = {})
+ def call_api_with_retry(options = {})
config = self.class.retriable_configuration
- config.merge! Take2.local_defaults(options) unless options.empty?
+ config.merge!(Take2.local_defaults(options)) unless options.empty?
tries ||= config[:retries]
begin
yield
rescue => e
- if config[:retriable].map {|klass| e.class <= klass }.any?
+ if config[:retriable].map { |klass| e.class <= klass }.any?
unless tries.zero? || config[:retry_condition_proc]&.call(e)
config[:retry_proc]&.call(e, tries)
- sleep(config[:time_to_sleep]) if config[:time_to_sleep]
+ rest(config, tries)
tries -= 1
retry
end
- end
+ end
raise e
end
end
-
+ alias_method :with_retry, :call_api_with_retry
+
+ private
+
+ def rest(config, tries)
+ seconds = if config[:time_to_sleep].to_f > 0
+ config[:time_to_sleep].to_f
+ else
+ next_interval(config[:backoff_intervals], config[:retries], tries)
+ end
+ sleep(seconds)
+ end
+
+ def next_interval(intervals, retries, current)
+ intervals[retries - current]
+ end
end
module ClassMethods
# Sets number of retries.
#
@@ -99,15 +115,16 @@
# retriable_errors Net::HTTPRetriableError, Errno::ECONNRESET
# end
# Arguments:
# errors: List of retiable errors
def retriable_errors(*errors)
- raise ArgumentError, 'All retriable errors must be StandardError decendants' unless errors.all? { |e| e <= StandardError }
+ message = 'All retriable errors must be StandardError decendants'
+ raise ArgumentError, message unless errors.all? { |e| e <= StandardError }
self.retriable = errors
end
- # Sets condition for retry attempt.
+ # Sets condition for retry attempt.
# If set, it MUST result to +false+ with number left retries greater that zero in order to retry.
#
# Example:
# class PizzaService
# include Take2
@@ -118,11 +135,11 @@
def retriable_condition(proc)
raise ArgumentError, 'Must be callable' unless proc.respond_to?(:call)
self.retry_condition_proc = proc
end
- # Defines a proc that is called *before* retry attempt.
+ # Defines a proc that is called *before* retry attempt.
#
# Example:
# class PizzaService
# include Take2
# on_retry proc { |error, tries| puts "Retrying.. #{tries} of #{self.class.retriable_configuration[:retries]}" }
@@ -132,22 +149,32 @@
def on_retry(proc)
raise ArgumentError, 'Must be callable' unless proc.respond_to?(:call)
self.retry_proc = proc
end
- # Sets number of seconds to sleep before next retry.
+ def sleep_before_retry(seconds)
+ unless (seconds.is_a?(Integer) || seconds.is_a?(Float)) && seconds.positive?
+ raise ArgumentError, 'Must be positive numer'
+ end
+ puts "DEPRECATION MESSAGE - The sleep_before_retry method is softly deprecated in favor of backoff_stategy \r
+ where the time to sleep is a starting point on the backoff intervals. Please implement it instead."
+ self.time_to_sleep = seconds
+ end
+
+ # Sets the backoff strategy
#
# Example:
# class PizzaService
# include Take2
- # sleep_before_retry 1.5
+ # backoff_strategy type: :exponential, start: 3
# end
# Arguments:
- # seconds: number
- def sleep_before_retry(seconds)
- raise ArgumentError, 'Must be positive numer' unless (seconds.is_a?(Integer) || seconds.is_a?(Float)) && seconds.positive?
- self.time_to_sleep = seconds
+ # hash: object
+ def backoff_strategy(options)
+ available_types = [:constant, :linear, :fibonacci, :exponential]
+ raise ArgumentError, 'Incorrect backoff type' unless available_types.include?(options[:type])
+ self.backoff_intervals = Backoff.new(options[:type], options[:start]).intervals
end
# Exposes current class configuration
def retriable_configuration
Take2::Configuration::CONFIG_ATTRS.each_with_object({}) do |key, hash|
@@ -165,12 +192,10 @@
instance_variable_set("@#{attr}", config[attr])
end
end
def response_status(response)
- return response.status if response.respond_to? :status
- response.status_code if response.respond_to? :status_code
+ return response.status if response.respond_to?(:status)
+ response.status_code if response.respond_to?(:status_code)
end
-
end
-
-end
\ No newline at end of file
+end