Module: Retryable

Extended by:
Forwardable
Defined in:
lib/retryable.rb,
lib/retryable/version.rb,
lib/retryable/configuration.rb

Overview

Runs a code block, and retries it when an exception occurs. It’s great when working with flakey webservices (for example).

Defined Under Namespace

Modules: Version Classes: Configuration

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configurationObject

The configuration object.

See Also:



35
36
37
# File 'lib/retryable.rb', line 35

def configuration
  @configuration ||= Configuration.new
end

Class Method Details

.configure {|configuration| ... } ⇒ Object

Call this method to modify defaults in your initializers.

Examples:

Retryable.configure do |config|
  config.contexts     = {}
  config.ensure       = proc {}
  config.exception_cb = proc {}
  config.log_method   = proc {}
  config.matching     = /.*/
  config.not          = []
  config.on           = StandardError
  config.sleep        = 1
  config.sleep_method = ->(seconds) { Kernel.sleep(seconds) }
  config.tries        = 2
end

Yields:



29
30
31
# File 'lib/retryable.rb', line 29

def configure
  yield(configuration)
end

.retryable(options = {}) ⇒ Object

rubocop:disable Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/retryable.rb', line 52

def retryable(options = {})
  opts = configuration.to_hash

  check_for_invalid_options(options, opts)
  opts.merge!(options)

  # rubocop:disable Style/NumericPredicate
  return if opts[:tries] == 0
  # rubocop:enable Style/NumericPredicate

  on_exception = opts[:on].is_a?(Array) ? opts[:on] : [opts[:on]]
  not_exception = opts[:not].is_a?(Array) ? opts[:not] : [opts[:not]]

  matching = opts[:matching].is_a?(Array) ? opts[:matching] : [opts[:matching]]
  tries = opts[:tries]
  retries = 0
  retry_exception = nil

  begin
    opts[:log_method].call(retries, retry_exception) if retries > 0
    return yield retries, retry_exception
  rescue *not_exception
    raise
  rescue *on_exception => exception
    raise unless configuration.enabled?
    raise unless matches?(exception.message, matching)

    infinite_retries = :infinite || tries.respond_to?(:infinite?) && tries.infinite?
    raise if tries != infinite_retries && retries + 1 >= tries

    # Interrupt Exception could be raised while sleeping
    begin
      seconds = opts[:sleep].respond_to?(:call) ? opts[:sleep].call(retries) : opts[:sleep]
      opts[:sleep_method].call(seconds)
    rescue *not_exception
      raise
    rescue *on_exception
    end

    retries += 1
    retry_exception = exception
    opts[:exception_cb].call(retry_exception)
    retry
  ensure
    opts[:ensure].call(retries)
  end
end

.with_context(context_key, options = {}, &block) ⇒ Object Also known as: retryable_with_context



41
42
43
44
45
46
# File 'lib/retryable.rb', line 41

def with_context(context_key, options = {}, &block)
  unless configuration.contexts.key?(context_key)
    raise ArgumentError, "#{context_key} not found in Retryable.configuration.contexts. Available contexts: #{configuration.contexts.keys}"
  end
  retryable(configuration.contexts[context_key].merge(options), &block) if block
end