rhod 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.
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