anticipate 0.0.1 → 0.0.2
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.
- data/README.rdoc +11 -14
- data/lib/anticipate/dsl.rb +91 -0
- data/lib/anticipate.rb +3 -66
- data/spec/anticipate/anticipate_spec.rb +52 -17
- data/spec/anticipate/integration_spec.rb +2 -2
- metadata +21 -41
data/README.rdoc
CHANGED
@@ -4,23 +4,20 @@
|
|
4
4
|
|
5
5
|
Anticipate is a fluent interface for retrying blocks of code:
|
6
6
|
|
7
|
-
|
7
|
+
sleeping(0.1).seconds.between_tries.failing_after(20).tries do
|
8
8
|
|
9
|
-
#
|
9
|
+
# Calls block every 0.1 seconds until it stops raising errors
|
10
10
|
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
trying_every(n).seconds {}
|
16
|
-
failing_after(n).tries {}
|
17
|
-
trying_every(x).seconds.failing_after(y).tries {}
|
13
|
+
Including the Anticipate module into your class gives you:
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
failing_after(3).tries {}
|
16
|
+
sleeping(0.1).seconds.between_tries {}
|
17
|
+
sleeping(123).seconds.between_tries.failing_after(456).tries {}
|
18
|
+
|
19
|
+
Blocks should contain an assertion, i.e. raise a descriptive error if some condition is unsatisfied. On the last iteration, any error will be wrapped in an Anticipate::TimeoutError and re-raised.
|
20
|
+
|
21
|
+
The first try is attempted immediately, there is no sleep before the first yield.
|
23
22
|
|
24
|
-
I'm aware of a couple of gems
|
25
|
-
begin-rescue-sleep-retry dance, namely 'retry' and 'attempt'.
|
26
|
-
The aim of this library is to offer more expressive syntax.
|
23
|
+
I'm aware of a couple of gems performing the begin-rescue-sleep-retry dance, namely 'retry' and 'attempt'. The aim of this library is to offer more expressive syntax.
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Anticipate
|
2
|
+
module DSL
|
3
|
+
|
4
|
+
def trying_every(amount)
|
5
|
+
anticipation.trying_every(amount)
|
6
|
+
end
|
7
|
+
|
8
|
+
def failing_after(amount)
|
9
|
+
anticipation.failing_after(amount)
|
10
|
+
end
|
11
|
+
|
12
|
+
def sleeping(amount)
|
13
|
+
anticipation.sleeping(amount)
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_tries
|
17
|
+
@default_tries ||= 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_interval
|
21
|
+
@default_interval ||= 0.1
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def anticipator
|
27
|
+
Anticipator.new(Kernel)
|
28
|
+
end
|
29
|
+
|
30
|
+
def anticipation
|
31
|
+
Anticipation.new(anticipator, default_interval, default_tries)
|
32
|
+
end
|
33
|
+
|
34
|
+
class Term
|
35
|
+
def initialize(anticipator, interval, timeout)
|
36
|
+
@anticipator, @interval, @timeout = anticipator, interval, timeout
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def exec
|
42
|
+
@anticipator.anticipate(@interval, @timeout) do
|
43
|
+
yield
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def chain
|
48
|
+
Anticipation.new(@anticipator, @interval, @timeout)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class TryingEvery < Term
|
53
|
+
def seconds
|
54
|
+
block_given? ? exec { yield } : chain
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class FailingAfter < Term
|
59
|
+
def tries
|
60
|
+
block_given? ? exec { yield } : chain
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class AfterSleep < Term
|
65
|
+
def between_tries
|
66
|
+
block_given? ? exec { yield } : chain
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Sleeping < Term
|
71
|
+
def seconds
|
72
|
+
AfterSleep.new(@anticipator, @interval, @timeout)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Anticipation < Term
|
77
|
+
def trying_every(amount)
|
78
|
+
TryingEvery.new(@anticipator, amount, @timeout)
|
79
|
+
end
|
80
|
+
|
81
|
+
def failing_after(amount)
|
82
|
+
FailingAfter.new(@anticipator, @interval, amount)
|
83
|
+
end
|
84
|
+
|
85
|
+
def sleeping(amount)
|
86
|
+
Sleeping.new(@anticipator, amount, @timeout)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
data/lib/anticipate.rb
CHANGED
@@ -2,73 +2,10 @@ lib = File.dirname(__FILE__)
|
|
2
2
|
$:.unshift(lib) unless $:.include?(lib) || $:.include?(File.expand_path(lib))
|
3
3
|
|
4
4
|
require 'anticipate/anticipator'
|
5
|
+
require 'anticipate/dsl'
|
5
6
|
|
6
7
|
module Anticipate
|
7
|
-
VERSION = '0.0.
|
8
|
+
VERSION = '0.0.2'
|
8
9
|
|
9
|
-
|
10
|
-
Anticipator.new(Kernel)
|
11
|
-
end
|
12
|
-
|
13
|
-
def default_tries
|
14
|
-
@default_tries ||= 1
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_interval
|
18
|
-
@default_interval ||= 0.1
|
19
|
-
end
|
20
|
-
|
21
|
-
def trying_every(amount)
|
22
|
-
anticipation.trying_every(amount)
|
23
|
-
end
|
24
|
-
|
25
|
-
def failing_after(amount)
|
26
|
-
anticipation.failing_after(amount)
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def anticipation
|
32
|
-
Anticipation.new(anticipator, default_interval, default_tries)
|
33
|
-
end
|
34
|
-
|
35
|
-
class Term
|
36
|
-
def initialize(anticipator, interval, timeout)
|
37
|
-
@anticipator, @interval, @timeout = anticipator, interval, timeout
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def exec
|
43
|
-
@anticipator.anticipate(@interval, @timeout) do
|
44
|
-
yield
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def chain
|
49
|
-
Anticipation.new(@anticipator, @interval, @timeout)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
class TimeUnit < Term
|
54
|
-
def seconds
|
55
|
-
block_given? ? exec { yield } : chain
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class CountUnit < Term
|
60
|
-
def tries
|
61
|
-
block_given? ? exec { yield } : chain
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Anticipation < Term
|
66
|
-
def trying_every(amount)
|
67
|
-
TimeUnit.new(@anticipator, amount, @timeout)
|
68
|
-
end
|
69
|
-
|
70
|
-
def failing_after(amount)
|
71
|
-
CountUnit.new(@anticipator, @interval, amount)
|
72
|
-
end
|
73
|
-
end
|
10
|
+
include DSL
|
74
11
|
end
|
@@ -4,13 +4,12 @@ module Anticipate
|
|
4
4
|
describe Anticipate do
|
5
5
|
include Anticipate
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
@anticipator.stub!(:anticipate).and_yield
|
7
|
+
let :anticipator do
|
8
|
+
mock("anticipator")
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
before do
|
12
|
+
anticipator.stub!(:anticipate).and_yield
|
14
13
|
end
|
15
14
|
|
16
15
|
describe "trying_every(n).seconds" do
|
@@ -21,16 +20,34 @@ module Anticipate
|
|
21
20
|
end
|
22
21
|
|
23
22
|
it "overrides the interval" do
|
24
|
-
|
23
|
+
anticipator.should_receive(:anticipate).with(55, anything)
|
25
24
|
trying_every(55).seconds {}
|
26
25
|
end
|
27
26
|
|
28
27
|
it "uses the default timeout" do
|
29
|
-
|
28
|
+
anticipator.should_receive(:anticipate).with(anything, default_tries)
|
30
29
|
trying_every(66).seconds {}
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
33
|
+
describe "sleeping(n).seconds.between_tries" do
|
34
|
+
it "yields" do
|
35
|
+
called = false
|
36
|
+
sleeping(1).seconds.between_tries { called = true }
|
37
|
+
called.should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "overrides the interval" do
|
41
|
+
anticipator.should_receive(:anticipate).with(77, anything)
|
42
|
+
trying_every(77).seconds {}
|
43
|
+
end
|
44
|
+
|
45
|
+
it "uses the default timeout" do
|
46
|
+
anticipator.should_receive(:anticipate).with(anything, default_tries)
|
47
|
+
trying_every(88).seconds {}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
34
51
|
describe "failing_after(n).tries" do
|
35
52
|
it "yields" do
|
36
53
|
called = false
|
@@ -39,12 +56,12 @@ module Anticipate
|
|
39
56
|
end
|
40
57
|
|
41
58
|
it "overrides the timeout" do
|
42
|
-
|
59
|
+
anticipator.should_receive(:anticipate).with(anything, 77)
|
43
60
|
failing_after(77).tries {}
|
44
61
|
end
|
45
62
|
|
46
63
|
it "uses the default interval" do
|
47
|
-
|
64
|
+
anticipator.should_receive(:anticipate).with(default_interval, anything)
|
48
65
|
failing_after(88).tries {}
|
49
66
|
end
|
50
67
|
end
|
@@ -57,13 +74,13 @@ module Anticipate
|
|
57
74
|
end
|
58
75
|
|
59
76
|
it "overrides the timeout" do
|
60
|
-
|
61
|
-
failing_after(
|
77
|
+
anticipator.should_receive(:anticipate).with(anything, 1)
|
78
|
+
failing_after(anything).tries.trying_every(1).seconds {}
|
62
79
|
end
|
63
80
|
|
64
81
|
it "overrides the interval" do
|
65
|
-
|
66
|
-
failing_after(
|
82
|
+
anticipator.should_receive(:anticipate).with(2, anything)
|
83
|
+
failing_after(anything).tries.trying_every(2).seconds {}
|
67
84
|
end
|
68
85
|
end
|
69
86
|
|
@@ -75,13 +92,31 @@ module Anticipate
|
|
75
92
|
end
|
76
93
|
|
77
94
|
it "overrides the timeout" do
|
78
|
-
|
79
|
-
trying_every(
|
95
|
+
anticipator.should_receive(:anticipate).with(anything, 3)
|
96
|
+
trying_every(anything).seconds.failing_after(3).tries {}
|
97
|
+
end
|
98
|
+
|
99
|
+
it "overrides the interval" do
|
100
|
+
anticipator.should_receive(:anticipate).with(4, anything)
|
101
|
+
trying_every(4).seconds.failing_after(anything).tries {}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "sleeping(x).seconds.between_tries.failing_after(y).tries" do
|
106
|
+
it "yields" do
|
107
|
+
called = false
|
108
|
+
sleeping(1).seconds.between_tries.failing_after(2).tries { called = true }
|
109
|
+
called.should be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "overrides the timeout" do
|
113
|
+
anticipator.should_receive(:anticipate).with(anything, 5)
|
114
|
+
sleeping(anything).seconds.between_tries.failing_after(5).tries {}
|
80
115
|
end
|
81
116
|
|
82
117
|
it "overrides the interval" do
|
83
|
-
|
84
|
-
trying_every(
|
118
|
+
anticipator.should_receive(:anticipate).with(6, anything)
|
119
|
+
trying_every(6).seconds.failing_after(anything).tries {}
|
85
120
|
end
|
86
121
|
end
|
87
122
|
end
|
@@ -7,7 +7,7 @@ module Anticipate
|
|
7
7
|
it "raises when a block continually raises" do
|
8
8
|
raises = []
|
9
9
|
lambda {
|
10
|
-
|
10
|
+
sleeping(0.01).seconds.between_tries.failing_after(5).tries {
|
11
11
|
raise (raises << Time.now).to_s
|
12
12
|
}
|
13
13
|
}.should raise_error(TimeoutError)
|
@@ -20,7 +20,7 @@ module Anticipate
|
|
20
20
|
it "continues when a block stops raising" do
|
21
21
|
raises = 0
|
22
22
|
lambda {
|
23
|
-
|
23
|
+
sleeping(0.01).seconds.between_tries.failing_after(3).tries {
|
24
24
|
raise (raises += 1).to_s unless raises == 2
|
25
25
|
}
|
26
26
|
}.should_not raise_error
|
metadata
CHANGED
@@ -1,75 +1,55 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: anticipate
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 0.0.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Josh Chisholm
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2011-01-31 00:00:00 +00:00
|
19
|
-
default_executable:
|
12
|
+
date: 2011-11-02 00:00:00.000000000Z
|
20
13
|
dependencies: []
|
21
|
-
|
22
14
|
description: Fluent interface for try-rescue-sleep-retry-abort
|
23
15
|
email: joshuachisholm@gmail.com
|
24
16
|
executables: []
|
25
|
-
|
26
17
|
extensions: []
|
27
|
-
|
28
|
-
extra_rdoc_files:
|
18
|
+
extra_rdoc_files:
|
29
19
|
- README.rdoc
|
30
|
-
files:
|
20
|
+
files:
|
31
21
|
- README.rdoc
|
32
22
|
- anticipate.gemspec
|
33
23
|
- lib/anticipate.rb
|
34
24
|
- lib/anticipate/anticipator.rb
|
25
|
+
- lib/anticipate/dsl.rb
|
35
26
|
- lib/anticipate/timeout_error.rb
|
36
27
|
- spec/anticipate/anticipate_spec.rb
|
37
28
|
- spec/anticipate/anticipator_spec.rb
|
38
29
|
- spec/anticipate/integration_spec.rb
|
39
30
|
- spec/spec_helper.rb
|
40
|
-
has_rdoc: true
|
41
31
|
homepage: http://github.com/joshski/anticipate
|
42
32
|
licenses: []
|
43
|
-
|
44
33
|
post_install_message:
|
45
34
|
rdoc_options: []
|
46
|
-
|
47
|
-
require_paths:
|
35
|
+
require_paths:
|
48
36
|
- lib
|
49
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
38
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
55
|
-
|
56
|
-
- 0
|
57
|
-
version: "0"
|
58
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
44
|
none: false
|
60
|
-
requirements:
|
61
|
-
- -
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
|
64
|
-
segments:
|
65
|
-
- 0
|
66
|
-
version: "0"
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
67
49
|
requirements: []
|
68
|
-
|
69
50
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
51
|
+
rubygems_version: 1.8.6
|
71
52
|
signing_key:
|
72
53
|
specification_version: 3
|
73
|
-
summary: anticipate-0.0.
|
54
|
+
summary: anticipate-0.0.2
|
74
55
|
test_files: []
|
75
|
-
|