Class: BlockRepeater::Repeater

Inherits:
Object
  • Object
show all
Includes:
RepeaterMethods
Defined in:
lib/block_repeater/repeater.rb

Overview

The class which governs when to stop repeating based on condition or timeout

Constant Summary collapse

@@default_exceptions =
[]

Constants included from RepeaterMethods

RepeaterMethods::UNTIL_METHOD_REGEX

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RepeaterMethods

#call_if_method_responsive, #method_missing, #respond_to_missing?

Constructor Details

#initialize(manual_repeat: true, **kwargs, &block) ⇒ Repeater

Prepare the Repeater to take the initial block to be repeated

Parameters:

  • manual_repeat (defaults to: true)
    • Determines whether the repeat method is called manually, used by the Repeatable module

  • **kwargs
    • Capture other arguments in the situation where repeat isn’t being called manually

  • &block
    • The block of code to be repeated



21
22
23
24
25
26
# File 'lib/block_repeater/repeater.rb', line 21

def initialize(manual_repeat: true, **kwargs, &block)
  @manual_repeat = manual_repeat
  @repeater_arguments = kwargs
  @repeat_block = block
  @anticipated_exceptions = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class RepeaterMethods

Class Method Details

.default_catch(exceptions: [], behaviour: :defer, &block) ⇒ Object

Same as #catch but defines default behaviours shared by all BlockRepeater instances except that there is no default exception type, it must be defined



92
93
94
# File 'lib/block_repeater/repeater.rb', line 92

def self.default_catch(exceptions: [], behaviour: :defer, &block)
  @@default_exceptions << ExceptionResponse.new(types: exceptions, behaviour: behaviour, &block)
end

Instance Method Details

#catch(exceptions: [StandardError], behaviour: :defer, &block) ⇒ Object

Determine how to respond to exceptions raised while repeating, must be called before #until

Parameters:

  • &block
    • Code to execute when an exception is encountered

  • exceptions (defaults to: [StandardError])
    • Which exceptions are being handled by this block, defaults to StandardError

  • behaviour (defaults to: :defer)
    • After encountering the exception how should the repeater behave:

    :stop - cease repeating, execute the given block :continue - execute the given block but keep repeating :defer - execute the block only if the exception still occurs after all repeat attempts



84
85
86
87
# File 'lib/block_repeater/repeater.rb', line 84

def catch(exceptions: [StandardError], behaviour: :defer, &block)
  @anticipated_exceptions << ExceptionResponse.new(types: exceptions, behaviour: behaviour, &block)
  self
end

#repeat(times: 25, delay: 0.2, **_) ⇒ Object

Repeat a block until either the defined timeout is met or the condition block returns true

Parameters:

  • times (defaults to: 25)
    • How many times to attempt to execute the main block

  • delay (defaults to: 0.2)
    • The amount of time to wait between each attempt

  • **_
    • Capture any extra keyword arguments and discard them

Returns:

  • The result of calling the main block the final time



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/block_repeater/repeater.rb', line 35

def repeat(times: 25, delay: 0.2, **_)
  result, @condition_met, deferred_exception = nil
  anticipated_exception_types = @anticipated_exceptions.map(&:types).flatten
  default_exception_types = @@default_exceptions.map(&:types).flatten
  exception_types = anticipated_exception_types + default_exception_types

  times.times do
    begin
      result = @repeat_block.call
      @condition_met = @condition_block.call(result) if @condition_block
      deferred_exception = nil
    rescue *exception_types => e
      exceptions = if anticipated_exception_types.any? do |ex|
                        e.class <= ex
                      end
                     @anticipated_exceptions
                   else
                     @@default_exceptions
                   end
      matched_response = exceptions.detect { |expected| expected.types.any? { |ex| e.class <= ex } }
      if matched_response.behaviour == :defer
        deferred_exception = matched_response
        deferred_exception.actual = e
      else
        matched_response.execute(e)
      end

      break if matched_response.behaviour == :stop
    end

    break if @condition_met

    sleep delay
  end

  deferred_exception&.execute

  result
end

#until(&block) ⇒ Object

Set the block which determines if the main block should stop being executed

Parameters:

  • &block
    • The block of code which determines the target condition of the main block

Returns:

  • Either return the repeater object if the repeat method is being called manually or return the result of calling the repeat method



102
103
104
105
106
107
108
109
# File 'lib/block_repeater/repeater.rb', line 102

def until(&block)
  @condition_block = block
  if @manual_repeat
    self
  else
    repeat(**@repeater_arguments)
  end
end