block_repeater 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f94ed1fef785fdbd5a8f8f6810406a6a610be84fd551886b3e8b4784b7f47402
4
- data.tar.gz: 4bace96bec87f17aacbc235907af48e5dfd641f1ff2f0c4126b95dff79f04fd1
3
+ metadata.gz: 338c684e8ff15a822ae560f39d1de4df380f16213bb8f5fb4d1099f98b06f627
4
+ data.tar.gz: f02a5d355851bf5b1b089bb2a86ff5f4325a76ca260969385aee159fcc0f6e86
5
5
  SHA512:
6
- metadata.gz: be8d84eacb5b9397e6c47757fb68a06bb4c039cf6632230b68e44abffbb22b6d040b45f5b238220f12d25969e028a80228479f4afb0ba2a9ddebc71bf0ccaa43
7
- data.tar.gz: 61437b317664440b00db72f3b8cb3e20d4d3da18281b9c9e6a87252a29200c622036e3bd824b3d077063208b7d6ff07ca4683498bd442fae913a36ce63530afc
6
+ metadata.gz: 9013a8cb077a212b9896bc30d5a1d6bfc7be81f3655b25562307e369a0a6d125b8aae352a5ff0a15046b8899304d0c79c3bf5bc9e8179681bd38bb8345d32ac8
7
+ data.tar.gz: 1b53289cec53d69c0b3a9cce35b685567e8780c350a900e58f1abc9c3e17779d3a3528befce6583b506fe20c95a12ae4036a11b20516454a8e756141235fe6b0
@@ -3,15 +3,13 @@ name: Ruby Gem
3
3
  on:
4
4
  push:
5
5
  branches: [ "master" ]
6
- pull_request:
7
- branches: [ "master" ]
8
6
 
9
7
  jobs:
10
8
  test:
11
9
  runs-on: ubuntu-latest
12
10
  strategy:
13
11
  matrix:
14
- ruby-version: [ '3.0', '3.1' ]
12
+ ruby-version: [ '3.0', '3.1', '3.2' ]
15
13
 
16
14
  steps:
17
15
  - uses: actions/checkout@v2
data/.gitignore CHANGED
@@ -9,3 +9,7 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # jetbrains directories
14
+ /.idea
15
+ /.DS_Store
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- block_repeater (1.0.0)
4
+ block_repeater (1.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -50,6 +50,22 @@ The repeater also takes two parameters:
50
50
  repeat (delay: 0.5, times: 10){ call_database_method }.until{ |result| result.count.positive? }
51
51
  ```
52
52
 
53
+ ### Backoff method
54
+ This is a slightly different take on the preferred `repeat` method. It retries a call whilst exponentially increasing the wait time between each iteration until a timeout is reached.
55
+
56
+ ```ruby
57
+ repeat do
58
+ call_method
59
+ end.until do |result|
60
+ result
61
+ end.backoff(timeout: 10, initial_wait: 0.5, multiplier: 2)
62
+ ```
63
+
64
+ `backoff` takes three paramaters:
65
+ - `timeout:` is how long you want your repeater to run
66
+ - `initial_wait:` is how long you want to pause before your first retry
67
+ - `multiplier:` is the rate at which you increase the wait time between each iteration
68
+
53
69
  ### Exception Handling
54
70
  Using the `catch` method you can define how the repeater should respond to specific exception types. To do this you need to provide a list of exceptions to catch, a block of code which will be performed, and an option for how to trigger than block of code.
55
71
 
@@ -82,13 +98,13 @@ You can also define default exception handling behaviour which all repeaters in
82
98
  ```ruby
83
99
  BlockRepeater::Repeater.default_catch(exceptions: [IOError], behaviour: :defer) do |e|
84
100
  puts 'An IOError occurred'
85
- e.raise
101
+ raise e
86
102
  end
87
103
  ```
88
104
  A common use-case for default exception handling is if using a gem such as RSpec, where you may want to handle failed expectations in a uniform manner. To do so you need define the default behaviour first, in a place such as a `env.rb` file or similar:
89
105
  ```ruby
90
106
  BlockRepeater::Repeater.default_catch(exceptions: [RSpec::Expectations::ExpectationNotMetError], behaviour: :defer) do |e|
91
- e.raise
107
+ raise e
92
108
  end
93
109
  ```
94
110
  Then an RSpec expectation can be used in the block for the `until` method. The expectation will be attempted each try, but the exception will only be raised if it has still failed once the number or attempts has been reached.
@@ -45,8 +45,8 @@ module BlockRepeater
45
45
  deferred_exception = nil
46
46
  rescue *exception_types => e
47
47
  exceptions = if anticipated_exception_types.any? do |ex|
48
- e.class <= ex
49
- end
48
+ e.class <= ex
49
+ end
50
50
  @anticipated_exceptions
51
51
  else
52
52
  @@default_exceptions
@@ -72,6 +72,39 @@ module BlockRepeater
72
72
  result
73
73
  end
74
74
 
75
+ # Retry a call whilst exponentially increasing the wait time between each iteration until a timeout is reached
76
+ # @param [Integer] `timeout` is the max amount wait of time you wish to try the action
77
+ # @param [Integer] `initial_wait` is the initial wait time to retry the action
78
+ # @param [Integer] `multiplier` is the rate at which you increase the wait time between each iteration
79
+ def backoff(timeout: 10, initial_wait: 0.1, multiplier: 2)
80
+ raise StandardError, 'Multiplier cannot be less than 1.1' if multiplier < 1.1
81
+
82
+ condition_met = nil
83
+ result = nil
84
+
85
+ current_sleep_seconds = initial_wait
86
+ start_time = Time.now
87
+
88
+ until current_sleep_seconds >= timeout
89
+ result = @repeat_block.call
90
+ condition_met = @condition_block.call(result) if @condition_block
91
+
92
+ duration = Time.now - start_time
93
+
94
+ break if condition_met || duration > timeout
95
+
96
+ # calculating exponential increase
97
+ current_sleep_seconds *= multiplier
98
+ # how long the total duration will be after the next sleep
99
+ projected_duration = current_sleep_seconds + duration
100
+ # if projected duration exceeds the timeout, reduce time for next sleep to allow one final call
101
+ current_sleep_seconds = (timeout - duration) if projected_duration > timeout
102
+
103
+ sleep(current_sleep_seconds)
104
+ end
105
+ result
106
+ end
107
+
75
108
  ##
76
109
  # Determine how to respond to exceptions raised while repeating, must be called _before_ #until
77
110
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BlockRepeater
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: block_repeater
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Bray
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-04 00:00:00.000000000 Z
11
+ date: 2026-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bump
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
143
  requirements: []
144
- rubygems_version: 3.4.10
144
+ rubygems_version: 3.2.15
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: Conditionally repeat a block of code