block_repeater 1.1.0 → 1.2.0
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 +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +28 -24
- data/README.md +39 -2
- data/block_repeater.gemspec +3 -2
- data/lib/block_repeater/replay.rb +36 -0
- data/lib/block_repeater/version.rb +1 -1
- data/lib/block_repeater.rb +2 -0
- metadata +26 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea41f6457e3cf7aa83b6e1f5989f2a92b24174ebb6d12f48b564033661af1477
|
|
4
|
+
data.tar.gz: 7df266b49f3b8c32aff6a8d2826d18864b5d1885f41e3888ce80896bb715f73a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75426f7a71bd028729e26a82424e142a9ffa40c2d88cc8704bf95f22051ddd2fc4c2677896d30d690d6c819a17c2e81d89013ff681d1511c224d56bb92eefd53
|
|
7
|
+
data.tar.gz: 414d3d2a92040b4e143d1fc4103f82ddb403fa9e154f0d2a688ef303b4b444c465d787260646bd0e875c41466484c7338c03941cadad06ba966289a33481998d
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,40 +1,43 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
block_repeater (1.
|
|
4
|
+
block_repeater (1.2.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
-
ast (2.4.
|
|
9
|
+
ast (2.4.3)
|
|
10
10
|
bump (0.10.0)
|
|
11
11
|
coderay (1.1.3)
|
|
12
|
-
diff-lcs (1.
|
|
13
|
-
json (2.
|
|
14
|
-
method_source (1.
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
diff-lcs (1.6.2)
|
|
13
|
+
json (2.21.1)
|
|
14
|
+
method_source (1.1.0)
|
|
15
|
+
ostruct (0.6.3)
|
|
16
|
+
parallel (1.28.0)
|
|
17
|
+
parser (3.3.12.0)
|
|
17
18
|
ast (~> 2.4.1)
|
|
18
|
-
|
|
19
|
+
racc
|
|
20
|
+
pry (0.14.2)
|
|
19
21
|
coderay (~> 1.1)
|
|
20
22
|
method_source (~> 1.0)
|
|
23
|
+
racc (1.8.1)
|
|
21
24
|
rainbow (3.1.1)
|
|
22
|
-
rake (
|
|
23
|
-
regexp_parser (2.
|
|
24
|
-
rexml (3.
|
|
25
|
-
rspec (3.
|
|
26
|
-
rspec-core (~> 3.
|
|
27
|
-
rspec-expectations (~> 3.
|
|
28
|
-
rspec-mocks (~> 3.
|
|
29
|
-
rspec-core (3.
|
|
30
|
-
rspec-support (~> 3.
|
|
31
|
-
rspec-expectations (3.
|
|
25
|
+
rake (13.4.2)
|
|
26
|
+
regexp_parser (2.12.0)
|
|
27
|
+
rexml (3.4.4)
|
|
28
|
+
rspec (3.13.2)
|
|
29
|
+
rspec-core (~> 3.13.0)
|
|
30
|
+
rspec-expectations (~> 3.13.0)
|
|
31
|
+
rspec-mocks (~> 3.13.0)
|
|
32
|
+
rspec-core (3.13.6)
|
|
33
|
+
rspec-support (~> 3.13.0)
|
|
34
|
+
rspec-expectations (3.13.5)
|
|
32
35
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
33
|
-
rspec-support (~> 3.
|
|
34
|
-
rspec-mocks (3.
|
|
36
|
+
rspec-support (~> 3.13.0)
|
|
37
|
+
rspec-mocks (3.13.8)
|
|
35
38
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
36
|
-
rspec-support (~> 3.
|
|
37
|
-
rspec-support (3.
|
|
39
|
+
rspec-support (~> 3.13.0)
|
|
40
|
+
rspec-support (3.13.7)
|
|
38
41
|
rubocop (1.50.2)
|
|
39
42
|
json (~> 2.3)
|
|
40
43
|
parallel (~> 1.10)
|
|
@@ -56,9 +59,10 @@ PLATFORMS
|
|
|
56
59
|
DEPENDENCIES
|
|
57
60
|
block_repeater!
|
|
58
61
|
bump (~> 0.6)
|
|
59
|
-
bundler (
|
|
62
|
+
bundler (>= 2.4)
|
|
63
|
+
ostruct
|
|
60
64
|
pry
|
|
61
|
-
rake (~>
|
|
65
|
+
rake (~> 13.0)
|
|
62
66
|
rspec (~> 3.8)
|
|
63
67
|
rubocop (~> 1.50.0)
|
|
64
68
|
|
data/README.md
CHANGED
|
@@ -98,13 +98,13 @@ You can also define default exception handling behaviour which all repeaters in
|
|
|
98
98
|
```ruby
|
|
99
99
|
BlockRepeater::Repeater.default_catch(exceptions: [IOError], behaviour: :defer) do |e|
|
|
100
100
|
puts 'An IOError occurred'
|
|
101
|
-
e
|
|
101
|
+
raise e
|
|
102
102
|
end
|
|
103
103
|
```
|
|
104
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:
|
|
105
105
|
```ruby
|
|
106
106
|
BlockRepeater::Repeater.default_catch(exceptions: [RSpec::Expectations::ExpectationNotMetError], behaviour: :defer) do |e|
|
|
107
|
-
e
|
|
107
|
+
raise e
|
|
108
108
|
end
|
|
109
109
|
```
|
|
110
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.
|
|
@@ -115,6 +115,43 @@ Then an RSpec expectation can be used in the block for the `until` method. The e
|
|
|
115
115
|
expect(result.count).to be_positive, raise 'No result returned from databased'
|
|
116
116
|
end
|
|
117
117
|
```
|
|
118
|
+
### Replay method
|
|
119
|
+
The `replay` method provides a retry-with-refresh mechanism for UI actions that may fail due to transient errors. It is designed for use with browser automation tools like Capybara.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
replay(exceptions: [Capybara::ElementNotFound], refresh: -> { page.refresh }) do
|
|
123
|
+
find('#submit-button').click
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`replay` takes five parameters:
|
|
128
|
+
- `times:` maximum retry attempts (defaults to 3)
|
|
129
|
+
- `delay:` seconds between retries (defaults to 0.5)
|
|
130
|
+
- `exceptions:` array of exception types to catch and retry on (defaults to [])
|
|
131
|
+
- `refresh:` a callable (Proc/lambda) to execute between retries for page refresh (defaults to nil)
|
|
132
|
+
- `logger:` any object responding to `#error` for error messages, or nil to silence logging (defaults to nil)
|
|
133
|
+
|
|
134
|
+
The block is executed and if a configured exception is raised, the error is logged, the refresh callable is invoked (if provided), and the block is retried. If the exception persists after all attempts, it is re-raised. The refresh is not called on the final attempt.
|
|
135
|
+
|
|
136
|
+
A typical setup with Capybara and locale handling:
|
|
137
|
+
```ruby
|
|
138
|
+
UI_ERRORS = [Capybara::ElementNotFound, Selenium::WebDriver::Error::ElementClickInterceptedError]
|
|
139
|
+
|
|
140
|
+
replay(exceptions: UI_ERRORS, refresh: -> { visit("#{current_path}?locale=#{I18n.locale}") }) do
|
|
141
|
+
click_link_or_button('Continue')
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
To use a custom logger:
|
|
146
|
+
```ruby
|
|
147
|
+
replay(exceptions: UI_ERRORS, logger: Rails.logger) { perform_action }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
To silence logging:
|
|
151
|
+
```ruby
|
|
152
|
+
replay(exceptions: UI_ERRORS, logger: nil) { perform_action }
|
|
153
|
+
```
|
|
154
|
+
|
|
118
155
|
### Non predefined condition methods
|
|
119
156
|
Very simple conditions can be utilised without using a block. This expects either one or two method names which will be called against the result of repeating the main block.
|
|
120
157
|
The required format is `until_<method name>` or `until_<method name>_becomes_<method name>`.
|
data/block_repeater.gemspec
CHANGED
|
@@ -27,9 +27,10 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.require_paths = ['lib']
|
|
28
28
|
|
|
29
29
|
spec.add_development_dependency 'bump', '~> 0.6'
|
|
30
|
-
spec.add_development_dependency 'bundler', '
|
|
30
|
+
spec.add_development_dependency 'bundler', '>= 2.4'
|
|
31
|
+
spec.add_development_dependency 'ostruct'
|
|
31
32
|
spec.add_development_dependency 'pry'
|
|
32
|
-
spec.add_development_dependency 'rake', '~>
|
|
33
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
33
34
|
spec.add_development_dependency 'rspec', '~> 3.8'
|
|
34
35
|
spec.add_development_dependency 'rubocop'
|
|
35
36
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BlockRepeater
|
|
4
|
+
##
|
|
5
|
+
# Provides a retry-with-refresh mechanism for UI actions
|
|
6
|
+
# that may fail due to transient driver/page errors.
|
|
7
|
+
#
|
|
8
|
+
# Consumers configure which exceptions to catch and how to refresh the page.
|
|
9
|
+
module Replay
|
|
10
|
+
##
|
|
11
|
+
# Execute a block, retrying on configured exceptions with optional page refresh.
|
|
12
|
+
#
|
|
13
|
+
# @param exceptions [Array<Class>] Exception types to catch and retry on
|
|
14
|
+
# @param times [Integer] Maximum retry attempts (default: 3)
|
|
15
|
+
# @param delay [Float] Seconds between retries (default: 0.5)
|
|
16
|
+
# @param refresh [Proc, nil] Callable to execute between retries for page refresh (default: nil)
|
|
17
|
+
# @param logger [#error, nil] Logger for error messages; nil to silence (default: nil)
|
|
18
|
+
# @param &block - The block of code to retry
|
|
19
|
+
# @return The result of the block
|
|
20
|
+
def replay(exceptions:, times: 3, delay: 0.5, refresh: nil, logger: nil, &block)
|
|
21
|
+
raise ArgumentError, 'replay requires a block' unless block
|
|
22
|
+
|
|
23
|
+
attempt = 0
|
|
24
|
+
|
|
25
|
+
repeat(times: times, delay: delay, &block).catch(exceptions: exceptions, behaviour: :continue) do |e|
|
|
26
|
+
attempt += 1
|
|
27
|
+
logger&.error { e.message }
|
|
28
|
+
raise(e) if attempt >= times
|
|
29
|
+
|
|
30
|
+
refresh&.call
|
|
31
|
+
end.until do |result|
|
|
32
|
+
!result.is_a?(Exception)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/block_repeater.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'block_repeater/version'
|
|
4
4
|
require 'block_repeater/repeatable'
|
|
5
|
+
require 'block_repeater/replay'
|
|
5
6
|
require 'block_repeater/repeater'
|
|
6
7
|
|
|
7
8
|
##
|
|
@@ -11,4 +12,5 @@ require 'block_repeater/repeater'
|
|
|
11
12
|
#
|
|
12
13
|
module BlockRepeater
|
|
13
14
|
include Repeatable
|
|
15
|
+
include Replay
|
|
14
16
|
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.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- William Bray
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bump
|
|
@@ -28,16 +28,30 @@ dependencies:
|
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 2.4
|
|
33
|
+
version: '2.4'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.4'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: ostruct
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
39
46
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: pry
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,14 +72,14 @@ dependencies:
|
|
|
58
72
|
requirements:
|
|
59
73
|
- - "~>"
|
|
60
74
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
75
|
+
version: '13.0'
|
|
62
76
|
type: :development
|
|
63
77
|
prerelease: false
|
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
79
|
requirements:
|
|
66
80
|
- - "~>"
|
|
67
81
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
82
|
+
version: '13.0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: rspec
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -119,6 +133,7 @@ files:
|
|
|
119
133
|
- lib/block_repeater/repeatable.rb
|
|
120
134
|
- lib/block_repeater/repeater.rb
|
|
121
135
|
- lib/block_repeater/repeater_methods.rb
|
|
136
|
+
- lib/block_repeater/replay.rb
|
|
122
137
|
- lib/block_repeater/version.rb
|
|
123
138
|
homepage: https://github.com/dvla/block-repeater
|
|
124
139
|
licenses:
|
|
@@ -126,7 +141,7 @@ licenses:
|
|
|
126
141
|
metadata:
|
|
127
142
|
homepage_uri: https://github.com/dvla/block-repeater
|
|
128
143
|
source_code_uri: https://github.com/dvla/block-repeater
|
|
129
|
-
post_install_message:
|
|
144
|
+
post_install_message:
|
|
130
145
|
rdoc_options: []
|
|
131
146
|
require_paths:
|
|
132
147
|
- lib
|
|
@@ -142,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
142
157
|
version: '0'
|
|
143
158
|
requirements: []
|
|
144
159
|
rubygems_version: 3.2.15
|
|
145
|
-
signing_key:
|
|
160
|
+
signing_key:
|
|
146
161
|
specification_version: 4
|
|
147
162
|
summary: Conditionally repeat a block of code
|
|
148
163
|
test_files: []
|