rhod 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: deae1b7b10203a606f6ba69985c0b0520e3ed666
4
- data.tar.gz: e7485d2cb082e4697c55ac0af77ad59a94f5e9d2
3
+ metadata.gz: 48eaf0e214a15e8bbedf592f83cdac4f5ad24af8
4
+ data.tar.gz: c58dedc35eb28bd9247516b64dee33d8083e69ae
5
5
  SHA512:
6
- metadata.gz: fbb5a386678bbdb265d75323df5cbb8c8f77100a4224d814dbe7d3391367f7cbfeaee3b9e203de061a3d2a50eacbd0ce35d49c0d3ae4472c1427b113ea340c37
7
- data.tar.gz: 69cab5ac043ac3806c37f47e55f39b5a42b91cc01b9015ddebda5ce91d7258dfe97a999a260225597405aef2ce082d99a4d671e6fd0daac79b7319d420d75580
6
+ metadata.gz: 67aca7d1a71afb19bc469c07ad193054c7df1ab1aa0e31dae5ed522bd9b04eb91cec3fea0f6d259c8f3c3ef0673e46ee8f82468ae50ecbb64029c269ee9a1f3f
7
+ data.tar.gz: 8cb15b2867eb6b00c1574841508e31317b8791b0f16e545b4a7df6da7ed5759c2a56b4a24ae0b2d088e795d3bde087c02960bd2e29f68b54369098aa7bc1547a
data/README.md CHANGED
@@ -27,7 +27,9 @@ Rhod requires Ruby 1.9.2 or greater.
27
27
 
28
28
  Add this line to your application's Gemfile:
29
29
 
30
- gem 'rhod'
30
+ ```ruby
31
+ gem 'rhod'
32
+ ```
31
33
 
32
34
  And then execute:
33
35
 
@@ -41,18 +43,22 @@ Or install it yourself as:
41
43
 
42
44
  Rhod has a very simple API. Design your application as you would normally, then enclose network accessing portions of your code with:
43
45
 
44
- Rhod.execute do
45
- ...
46
- end
46
+ ```ruby
47
+ Rhod.execute do
48
+ ...
49
+ end
50
+ ```
47
51
 
48
52
  This implements the "Fail Fast" scenario by default.
49
53
 
50
54
  Example, open a remote reasource, fail immediately if it fails:
51
55
 
52
- require 'open-uri'
53
- require 'rhod'
56
+ ```ruby
57
+ require 'open-uri'
58
+ require 'rhod'
54
59
 
55
- Rhod.execute { open("http://google.com").read }
60
+ Rhod.execute { open("http://google.com").read }
61
+ ```
56
62
 
57
63
  ### Retries with and without backoffs
58
64
 
@@ -62,44 +68,54 @@ Code within a `Rhod::Command` block with reties in use must be _idempotent_, i.e
62
68
 
63
69
  Rhod supports retying up to N times. By default it uses a logarithmic backoff:
64
70
 
65
- Rhod::Backoffs.default.take(5)
66
- # [0.7570232465074598, 2.403267722339301, 3.444932048942182, 4.208673319629471, 4.811984719351674]
71
+ ```ruby
72
+ Rhod::Backoffs.default.take(5)
73
+ # [0.7570232465074598, 2.403267722339301, 3.444932048942182, 4.208673319629471, 4.811984719351674]
74
+ ```
67
75
 
68
76
  Rhod also comes with exponential and constant (always the same value) backoffs. You can also supply any Enumerator that produces a series of numbers. See `lib/rhod/backoffs.rb` for examples.
69
77
 
70
78
  Example, open a remote reasource, fail once it has failed 10 times, with the default (logarithmic) backoff:
71
79
 
72
- require 'open-uri'
73
- require 'rhod'
80
+ ```ruby
81
+ require 'open-uri'
82
+ require 'rhod'
74
83
 
75
- Rhod::Command.execute(:retries => 10) { open("http://google.com").read }
84
+ Rhod::Command.execute(:retries => 10) { open("http://google.com").read }
85
+ ```
76
86
 
77
87
  Example, open a remote reasource, fail once it has failed 10 times, waiting 0.2 seconds between attempts:
78
88
 
79
- require 'open-uri'
80
- require 'rhod'
89
+ ```ruby
90
+ require 'open-uri'
91
+ require 'rhod'
81
92
 
82
- Rhod.execute(:retries => 10, :backoffs => Rhod::Backoffs.constant_backoff(0.2)) do
93
+ Rhod.execute(:retries => 10, :backoffs => 0.2) do
83
94
  open("http://google.com").read
84
95
  end
96
+ ```
85
97
 
86
98
  Example, open a remote reasource, fail once it has failed 10 times, with an exponetially growing wait time between attempts:
87
99
 
88
- require 'open-uri'
89
- require 'rhod'
100
+ ```ruby
101
+ require 'open-uri'
102
+ require 'rhod'
90
103
 
91
- Rhod.execute(:retries => 10, :backoffs => Rhod::Backoffs.expoential_backoffs) do
92
- open("http://google.com").read
93
- end
104
+ Rhod.execute(:retries => 10, :backoffs => :^) do
105
+ open("http://google.com").read
106
+ end
107
+ ```
94
108
 
95
- Example, open a remote reasource, fail once it has failed 10 times, with waiting between attempts:
109
+ Example, open a remote reasource, fail once it has failed 10 times, with no waiting between attempts:
96
110
 
97
- require 'open-uri'
98
- require 'rhod'
111
+ ```ruby
112
+ require 'open-uri'
113
+ require 'rhod'
99
114
 
100
- Rhod.execute(:retries => 10, :backoffs => Rhod::Backoffs.constant_backoff(0)) do
101
- open("http://google.com").read
102
- end
115
+ Rhod.execute(:retries => 10, :backoffs => 0) do
116
+ open("http://google.com").read
117
+ end
118
+ ```
103
119
 
104
120
  ### Fail Silent
105
121
 
@@ -107,52 +123,58 @@ In the event of a failure, Rhod falls back to a `fallback`. The most basic case
107
123
 
108
124
  Example, open a remote reasource, if it fails return them empty string.
109
125
 
110
- require 'open-uri'
111
- require 'rhod'
126
+ ```ruby
127
+ require 'open-uri'
128
+ require 'rhod'
112
129
 
113
- Rhod.execute(:fallback => -> {""}) do
114
- open("http://google.com").read
115
- end
130
+ Rhod.execute(:fallback => -> {""}) do
131
+ open("http://google.com").read
132
+ end
133
+ ```
116
134
 
117
135
  ### Fail w/ Fallback
118
136
 
119
137
  If there is another network call that can be used to fetch the reasource, it's possible to use another `Rhod::Command` once a failure has occurred.
120
138
 
121
- require 'open-uri'
122
- require 'rhod'
139
+ ```ruby
140
+ require 'open-uri'
141
+ require 'rhod'
123
142
 
124
- search_engine_fallback = Rhod::Command.new(
125
- :fallback => -> {""} # couldn't get anything
126
- ) do
127
- open("https://yahoo.com").read
128
- end
143
+ search_engine_fallback = Rhod::Command.new(
144
+ :fallback => -> {""} # couldn't get anything
145
+ ) do
146
+ open("https://yahoo.com").read
147
+ end
129
148
 
130
- Rhod.execute(:fallback => -> { search_engine_fallback.execute }) do
131
- open("http://google.com").read
132
- end
149
+ Rhod.execute(:fallback => -> { search_engine_fallback.execute }) do
150
+ open("http://google.com").read
151
+ end
152
+ ```
133
153
 
134
154
  ### Primary / Secondary ("Hot Spare") switch over
135
155
 
136
156
  Sometimes the fallback is just a part of normal operation. Just code in the state of which back end to access.
137
157
 
138
- require 'open-uri'
139
- require 'rhod'
158
+ ```ruby
159
+ require 'open-uri'
160
+ require 'rhod'
140
161
 
141
- class SearchEngineHTML
142
- attr_accessor :secondary
162
+ class SearchEngineHTML
163
+ attr_accessor :secondary
143
164
 
144
- def fetch
145
- url = !@secondary ? "http://google.com" : "https://yahoo.com"
165
+ def fetch
166
+ url = !@secondary ? "http://google.com" : "https://yahoo.com"
146
167
 
147
- Rhod.execute(url, :fallback => Proc.new { @secondary = !@secondary; fetch }) do |url|
148
- open(url).read
149
- end
150
- end
168
+ Rhod.execute(url, :fallback => Proc.new { @secondary = !@secondary; fetch }) do |url|
169
+ open(url).read
151
170
  end
171
+ end
172
+ end
152
173
 
153
- search_engine_html = SearchEngineHTML.new
174
+ search_engine_html = SearchEngineHTML.new
154
175
 
155
- search_engine_html.fetch
176
+ search_engine_html.fetch
177
+ ```
156
178
 
157
179
  ## Contributing
158
180
 
data/lib/rhod/backoffs.rb CHANGED
@@ -1,21 +1,45 @@
1
1
  module Rhod::Backoffs
2
2
 
3
3
  extend self
4
- # Returns a generator of a expoentially increasing series starting at 1
5
- def expoential_backoffs
4
+
5
+ def backoff_sugar_to_enumerator(backoff)
6
+ if backoff.is_a?(Enumerator)
7
+ backoff
8
+ elsif backoff.is_a?(Numeric)
9
+ constant_backoff(backoff)
10
+ elsif backoff.is_a?(String)
11
+ n = (backoff[1..-1].to_i)
12
+ case backoff[0]
13
+ when "^"
14
+ expoential_backoffs(n)
15
+ when "l"
16
+ logarithmic_backoffs(n)
17
+ end
18
+ elsif backoff.is_a?(Symbol)
19
+ case backoff
20
+ when :^
21
+ expoential_backoffs
22
+ when :l
23
+ logarithmic_backoffs
24
+ end
25
+ end
26
+ end
27
+
28
+ # Returns a generator of a expoentially increasing series starting at n
29
+ def expoential_backoffs(n=1)
6
30
  Enumerator.new do |yielder|
7
- x = 0
31
+ x = (n - 1)
8
32
  loop do
9
33
  x += 1
10
- yielder << (1.0/2.0*(2.0**x - 1.0)).ceil
34
+ yielder << 2.0**x
11
35
  end
12
36
  end
13
37
  end
14
38
 
15
- # Returns a generator of a logarithmicly increasing series starting at 0.3
16
- def logarithmic_backoffs
39
+ # Returns a generator of a logarithmicly increasing series starting at n
40
+ def logarithmic_backoffs(n=0.3)
17
41
  Enumerator.new do |yielder|
18
- x = 0.3
42
+ x = n
19
43
  loop do
20
44
  x += 1
21
45
  yielder << Math.log2(x**2)
@@ -24,10 +48,10 @@ module Rhod::Backoffs
24
48
  end
25
49
 
26
50
  # Always the same backoff
27
- def constant_backoff(i)
51
+ def constant_backoff(n)
28
52
  Enumerator.new do |yielder|
29
53
  loop do
30
- yielder << i
54
+ yielder << n
31
55
  end
32
56
  end
33
57
  end
data/lib/rhod/command.rb CHANGED
@@ -13,7 +13,7 @@ class Rhod::Command
13
13
  @retries ||= 0
14
14
  @attempts = 0
15
15
 
16
- @backoffs = opts[:backoffs]
16
+ @backoffs = Rhod::Backoffs.backoff_sugar_to_enumerator(opts[:backoffs])
17
17
  @backoffs ||= Rhod::Backoffs.default
18
18
 
19
19
  @fallback = opts[:fallback]
data/lib/rhod/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rhod
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,34 @@
1
+ require 'minitest/autorun'
2
+ require File.expand_path(File.dirname(__FILE__) + '/helper')
3
+
4
+ describe Rhod::Backoffs do
5
+ describe "backoff_sugar_to_enumerator" do
6
+ it "returns enumerators as is" do
7
+ e = Rhod::Backoffs.constant_backoff(0)
8
+ Rhod::Backoffs.backoff_sugar_to_enumerator(e).must_equal e
9
+ end
10
+
11
+ it "generates constant backoffs from a Numeric" do
12
+ Rhod::Backoffs.backoff_sugar_to_enumerator(2.0).next.must_equal 2.0
13
+ Rhod::Backoffs.backoff_sugar_to_enumerator(5).next.must_equal 5
14
+ end
15
+
16
+ it "generates expoential backoffs with '^' syntax" do
17
+ Rhod::Backoffs.backoff_sugar_to_enumerator("^2.0").take(3).must_equal [4.0, 8.0, 16.0]
18
+ end
19
+
20
+ it "generates logarithmic backoffs with 'l' syntax" do
21
+ Rhod::Backoffs.backoff_sugar_to_enumerator("l2.0").
22
+ take(3).must_equal [3.169925001442312, 4.0, 4.643856189774724]
23
+ end
24
+
25
+ it "generates expoential backoffs with :^ syntax" do
26
+ Rhod::Backoffs.backoff_sugar_to_enumerator(:^).take(3).must_equal [2.0, 4.0, 8.0]
27
+ end
28
+
29
+ it "generates logarithmic backoffs with :l syntax" do
30
+ Rhod::Backoffs.backoff_sugar_to_enumerator(:l).
31
+ take(3).must_equal [0.7570232465074598, 2.403267722339301, 3.444932048942182]
32
+ end
33
+ end
34
+ end
data/test/test_command.rb CHANGED
@@ -9,45 +9,67 @@ describe Rhod::Command do
9
9
  end
10
10
 
11
11
  describe "execute" do
12
- it "retries requests" do
13
- val = 0
14
-
15
- begin
16
- Rhod::Command.new(:retries => 1, :backoffs => Rhod::Backoffs.constant_backoff(0)) do
17
- val += 1
18
- raise StandardError
19
- end.execute
20
- rescue
21
- end
22
-
23
- val.must_equal 2
24
- end
25
-
26
12
  it "takes args" do
27
13
  Rhod::Command.new(1) {|a| 1 + a}.execute.must_equal 2
28
14
  end
29
15
 
30
- describe "fallbacks" do
31
- it "triggers fallback on failure" do
32
- Rhod::Command.new(:fallback => -> { 1 }) {raise StandardError}.execute.must_equal 1
33
- end
16
+ describe "with failures" do
17
+
18
+ describe "retrying" do
19
+ it "retries" do
20
+ val = 0
21
+
22
+ begin
23
+ Rhod::Command.new(:retries => 1, :backoffs => 0) do
24
+ val += 1
25
+ raise StandardError
26
+ end.execute
27
+ rescue
28
+ end
29
+
30
+ val.must_equal 2
31
+ end
32
+
33
+ it "uses backoffs" do
34
+ backoff = MiniTest::Mock.new
35
+ backoff.expect(:next, 0)
36
+
37
+ Rhod::Backoffs.stub(:constant_backoff, backoff) do
38
+ begin
39
+ Rhod::Command.new(:retries => 1, :backoffs => 0) do
40
+ val += 1
41
+ raise StandardError
42
+ end.execute
43
+ rescue
44
+ end
45
+ end
46
+ backoff.verify
47
+ end
34
48
 
35
- it "passes args to fallbacks" do
36
- Rhod::Command.new(1, :fallback => ->(a) { 1 + a }) {raise StandardError}.execute.must_equal 2
37
49
  end
38
50
 
39
- it "only uses fallback after all retries" do
40
- val = 0
51
+ describe "it uses fallbacks" do
52
+ it "triggers fallback on failure" do
53
+ Rhod::Command.new(:fallback => -> { 1 }) {raise StandardError}.execute.must_equal 1
54
+ end
55
+
56
+ it "passes args to fallbacks" do
57
+ Rhod::Command.new(1, :fallback => ->(a) { 1 + a }) {raise StandardError}.execute.must_equal 2
58
+ end
59
+
60
+ it "only uses fallback after all retries" do
61
+ val = 0
41
62
 
42
- Rhod::Command.new(
43
- :retries => 1,
44
- :backoffs => Rhod::Backoffs.constant_backoff(0),
45
- :fallback => -> { 1 }) do
46
- val += 1
47
- raise StandardError
48
- end.execute
63
+ Rhod::Command.new(
64
+ :retries => 1,
65
+ :backoffs => 0,
66
+ :fallback => -> { 1 }) do
67
+ val += 1
68
+ raise StandardError
69
+ end.execute
49
70
 
50
- val.must_equal 2
71
+ val.must_equal 2
72
+ end
51
73
  end
52
74
  end
53
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Bergeron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-05 00:00:00.000000000 Z
11
+ date: 2013-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -99,6 +99,7 @@ files:
99
99
  - lib/rhod/version.rb
100
100
  - rhod.gemspec
101
101
  - test/helper.rb
102
+ - test/test_backoffs.rb
102
103
  - test/test_command.rb
103
104
  homepage: https://github.com/dinedal/rhod
104
105
  licenses:
@@ -126,4 +127,5 @@ specification_version: 4
126
127
  summary: A High Avalibility framework for Ruby
127
128
  test_files:
128
129
  - test/helper.rb
130
+ - test/test_backoffs.rb
129
131
  - test/test_command.rb