cottus 0.1.4 → 0.2.0

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: ecc4ff9c7665bc9c8dd0bfd2f823b75cad6274e2
4
- data.tar.gz: 0689ac0d84036f71550017bde201bb533215a8fb
3
+ metadata.gz: 362f67d0f8c8fd3245548533ae68bbcf1e012957
4
+ data.tar.gz: be8e86a230fac1d891506d1616a84745b3c6f7d4
5
5
  SHA512:
6
- metadata.gz: e297a556782028c264222df3e6a3edaf657ddc9e4eaa243f238fae2188eb512e6635827edadf05a31f24350f8b4cf5ccecaa3880cc9acfcb7c5e357dafdb2b5b
7
- data.tar.gz: bd848b6c1b94e89a97e16bf701086251bdf5cfe65af1c0a75d03782ab271ad9792134dda673186e061b45da9adc18356a18c5a2d4907db16de11149cbec21915
6
+ metadata.gz: 5c7139181bc75a318f1ad87e5989c7d8c76d67fa44ed512a628aad0be3a3c227bd5a4bb745a7879b2b980a52170a5528f0c43e7b89217cb8ecf5454a1a7b3c93
7
+ data.tar.gz: dda18ac01482b6d1885517dbfd231d637e590e1c930ef42409d66faefd5e1b7a9a6f2daeb7bf0184d3df9316cf3686ae3ce1758948695916aef0c2268d626e1e
data/README.md CHANGED
@@ -46,11 +46,11 @@ responsible for carrying out the action specified by the passed ```meth```
46
46
  argument.
47
47
 
48
48
  The Strategy class must however also implement an ```#initialize``` method which
49
- takes three parameters: ```hosts```, ```client``` and an ```options``` hash:
49
+ takes two parameters: ```connections``` and an ```options``` hash:
50
50
 
51
51
  ```ruby
52
52
  class SomeStrategy
53
- def initialize(hosts, client, options={})
53
+ def initialize(connections, options={})
54
54
  end
55
55
 
56
56
  def execute(meth, path, options={}, &block)
@@ -72,7 +72,7 @@ end
72
72
 
73
73
  If you'd like to do some initialization on your own and override
74
74
  ```#initialize``` make sure to call ```#super``` or set the required instance
75
- variables (```@hosts```, ```@client```) on your own.
75
+ variable (```@connections```) on your own.
76
76
 
77
77
  It should be noted that I haven't decided on how strategies should be working to
78
78
  a 100% yet, so this might change in future releases.
data/lib/cottus/client.rb CHANGED
@@ -2,60 +2,36 @@
2
2
 
3
3
  module Cottus
4
4
  class Client
5
+ extend Forward
5
6
 
6
- attr_reader :hosts, :strategy
7
+ forward :get, :put, :post, :delete, :head, :patch, :options, :move, :to => :@strategy, :through => :execute
8
+
9
+ attr_reader :connections, :strategy
7
10
 
8
11
  def initialize(hosts, options={})
9
- @hosts = parse_hosts(hosts)
12
+ @connections = create_connections(hosts)
10
13
  @strategy = create_strategy(options)
11
14
  end
12
15
 
13
- def get(path, options={}, &block)
14
- @strategy.execute(:get, path, options, &block)
15
- end
16
-
17
- def put(path, options={}, &block)
18
- @strategy.execute(:put, path, options, &block)
19
- end
20
-
21
- def post(path, options={}, &block)
22
- @strategy.execute(:post, path, options, &block)
23
- end
24
-
25
- def delete(path, options={}, &block)
26
- @strategy.execute(:delete, path, options, &block)
27
- end
28
-
29
- def head(path, options={}, &block)
30
- @strategy.execute(:head, path, options, &block)
31
- end
32
-
33
- def patch(path, options={}, &block)
34
- @strategy.execute(:patch, path, options, &block)
35
- end
36
-
37
- def options(path, options={}, &block)
38
- @strategy.execute(:options, path, options, &block)
39
- end
40
-
41
- def move(path, options={}, &block)
42
- @strategy.execute(:move, path, options, &block)
16
+ def hosts
17
+ @connections.map(&:host)
43
18
  end
44
19
 
45
20
  private
46
21
 
47
- def parse_hosts(hosts)
48
- hosts.is_a?(String) ? hosts.split(',') : hosts
49
- end
50
-
51
- def http
52
- HTTParty
22
+ def create_connections(hosts)
23
+ hosts = hosts.is_a?(String) ? hosts.split(',') : hosts
24
+ hosts.map { |host| Connection.new(http, host) }
53
25
  end
54
26
 
55
27
  def create_strategy(options)
56
28
  strategy_options = options[:strategy_options] || {}
57
29
  strategy_impl = options[:strategy] || RoundRobinStrategy
58
- strategy_impl.new(hosts, http, strategy_options)
30
+ strategy_impl.new(connections, strategy_options)
31
+ end
32
+
33
+ def http
34
+ HTTParty
59
35
  end
60
36
  end
61
37
  end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ module Cottus
4
+ class Connection
5
+ extend Forward
6
+
7
+ forward :get, :put, :post, :delete, :head, :patch, :options, :move, :to => :wrapper
8
+
9
+ attr_reader :host
10
+
11
+ def initialize(*args)
12
+ @http, @host = *args
13
+ end
14
+
15
+ private
16
+
17
+ def wrapper(verb, path, options={}, &blk)
18
+ @http.send(verb, @host + path, options, &blk)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Cottus
4
+ module Forward
5
+ def forward(*args)
6
+ options = args.pop
7
+ to, through = options.values_at(:to, :through)
8
+
9
+ args.each do |verb|
10
+ define_method(verb) do |path, opts={}, &blk|
11
+ if to && through
12
+ instance_variable_get(to).send(through, verb, path, opts, &blk)
13
+ else
14
+ self.send(to, verb, path, opts, &blk)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -10,8 +10,8 @@ module Cottus
10
10
  ].freeze
11
11
 
12
12
  class Strategy
13
- def initialize(hosts, client, options={})
14
- @hosts, @client = hosts, client
13
+ def initialize(connections, options={})
14
+ @connections = connections
15
15
  end
16
16
 
17
17
  def execute(meth, path, options={}, &block)
@@ -20,10 +20,10 @@ module Cottus
20
20
  end
21
21
 
22
22
  class RoundRobinStrategy < Strategy
23
- def initialize(hosts, client, options={})
23
+ def initialize(connections, options={})
24
24
  super
25
25
 
26
- @current = 0
26
+ @index = 0
27
27
  @mutex = Mutex.new
28
28
  end
29
29
 
@@ -31,9 +31,9 @@ module Cottus
31
31
  tries = 0
32
32
 
33
33
  begin
34
- @client.send(meth, next_host + path, options, &block)
34
+ next_connection.send(meth, path, options, &block)
35
35
  rescue *VALID_EXCEPTIONS => e
36
- if tries >= @hosts.count
36
+ if tries >= @connections.count
37
37
  raise e
38
38
  else
39
39
  tries += 1
@@ -44,17 +44,17 @@ module Cottus
44
44
 
45
45
  private
46
46
 
47
- def next_host
47
+ def next_connection
48
48
  @mutex.synchronize do
49
- h = @hosts[@current]
50
- @current = (@current + 1) % @hosts.count
51
- h
49
+ connection = @connections[@index]
50
+ @index = (@index + 1) % @connections.count
51
+ connection
52
52
  end
53
53
  end
54
54
  end
55
55
 
56
56
  class RetryableRoundRobinStrategy < RoundRobinStrategy
57
- def initialize(hosts, client, options={})
57
+ def initialize(connections, options={})
58
58
  super
59
59
 
60
60
  @timeouts = options[:timeouts] || [1, 3, 5]
@@ -62,18 +62,18 @@ module Cottus
62
62
 
63
63
  def execute(meth, path, options={}, &block)
64
64
  tries = 0
65
- starting_host = host = next_host
65
+ starting_connection = connection = next_connection
66
66
 
67
67
  begin
68
- @client.send(meth, host + path, options, &block)
68
+ connection.send(meth, path, options, &block)
69
69
  rescue *VALID_EXCEPTIONS => e
70
70
  if tries < @timeouts.size
71
71
  sleep @timeouts[tries]
72
72
  tries += 1
73
73
  retry
74
74
  else
75
- host = next_host
76
- raise e if host == starting_host
75
+ connection = next_connection
76
+ raise e if connection == starting_connection
77
77
 
78
78
  tries = 0
79
79
  retry
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Cottus
4
- VERSION = '0.1.4'.freeze
4
+ VERSION = '0.2.0'.freeze
5
5
  end
data/lib/cottus.rb CHANGED
@@ -2,5 +2,7 @@
2
2
 
3
3
  require 'httparty'
4
4
 
5
+ require 'cottus/forward'
5
6
  require 'cottus/client'
6
7
  require 'cottus/strategies'
8
+ require 'cottus/connection'
@@ -47,7 +47,7 @@ module Cottus
47
47
  context 'strategy options' do
48
48
  it 'passes explicit options when creating strategy' do
49
49
  client = described_class.new('http://localhost:1234', strategy: strategy, strategy_options: {timeouts: [1, 3, 5]})
50
- expect(strategy).to have_received(:new).with(anything, anything, {timeouts: [1, 3, 5]})
50
+ expect(strategy).to have_received(:new).with(anything, {timeouts: [1, 3, 5]})
51
51
  end
52
52
  end
53
53
  end
@@ -5,16 +5,24 @@ require 'spec_helper'
5
5
  module Cottus
6
6
  describe Strategy do
7
7
  let :strategy do
8
- described_class.new(['http://n1.com', 'http://n2.com'], http)
8
+ described_class.new(connections)
9
9
  end
10
10
 
11
11
  let :http do
12
- double(:http, meth: nil)
12
+ double(:http, get: nil)
13
+ end
14
+
15
+ let :hosts do
16
+ ['http://n1.com', 'http://n2.com']
17
+ end
18
+
19
+ let :connections do
20
+ hosts.map { |host| Connection.new(http, host) }
13
21
  end
14
22
 
15
23
  describe '#execute' do
16
24
  it 'raises a NotImplementedError' do
17
- expect { strategy.execute(:meth, '/some/path') }.to raise_error(NotImplementedError, 'implement me in subclass')
25
+ expect { strategy.execute(:get, '/some/path') }.to raise_error(NotImplementedError, 'implement me in subclass')
18
26
  end
19
27
  end
20
28
  end
@@ -26,15 +34,15 @@ module Cottus
26
34
  end
27
35
 
28
36
  it 'uses the single host for the first request' do
29
- strategy.execute(:meth, '/some/path', query: { query: 1 })
37
+ strategy.execute(:get, '/some/path', query: { query: 1 })
30
38
 
31
- expect(http).to have_received(:meth).with('n1/some/path', query: { query: 1 }).once
39
+ expect(http).to have_received(:get).with('n1/some/path', query: { query: 1 }).once
32
40
  end
33
41
 
34
42
  it 'uses the single host for the second request' do
35
- 2.times { strategy.execute(:meth, '/some/path', query: { query: 1 }) }
43
+ 2.times { strategy.execute(:get, '/some/path', query: { query: 1 }) }
36
44
 
37
- expect(http).to have_received(:meth).with('n1/some/path', query: { query: 1 }).twice
45
+ expect(http).to have_received(:get).with('n1/some/path', query: { query: 1 }).twice
38
46
  end
39
47
  end
40
48
 
@@ -44,35 +52,39 @@ module Cottus
44
52
  end
45
53
 
46
54
  it 'uses the first host for the first request' do
47
- strategy.execute(:meth, '/some/path', query: { query: 1 })
55
+ strategy.execute(:get, '/some/path', query: { query: 1 })
48
56
 
49
- expect(http).to have_received(:meth).with('n1/some/path', query: { query: 1 }).once
57
+ expect(http).to have_received(:get).with('n1/some/path', query: { query: 1 }).once
50
58
  end
51
59
 
52
60
  it 'uses the second host for the second request' do
53
- 2.times { strategy.execute(:meth, '/some/path', query: { query: 1 }) }
61
+ 2.times { strategy.execute(:get, '/some/path', query: { query: 1 }) }
54
62
 
55
- expect(http).to have_received(:meth).with('n1/some/path', query: { query: 1 }).once
56
- expect(http).to have_received(:meth).with('n2/some/path', query: { query: 1 }).once
63
+ expect(http).to have_received(:get).with('n1/some/path', query: { query: 1 }).once
64
+ expect(http).to have_received(:get).with('n2/some/path', query: { query: 1 }).once
57
65
  end
58
66
 
59
67
  it 'uses each host in turn' do
60
- 3.times { strategy.execute(:meth, '/some/path', query: { query: 1 }) }
68
+ 3.times { strategy.execute(:get, '/some/path', query: { query: 1 }) }
61
69
 
62
- expect(http).to have_received(:meth).with('n1/some/path', query: { query: 1 }).once
63
- expect(http).to have_received(:meth).with('n2/some/path', query: { query: 1 }).once
64
- expect(http).to have_received(:meth).with('n3/some/path', query: { query: 1 }).once
70
+ expect(http).to have_received(:get).with('n1/some/path', query: { query: 1 }).once
71
+ expect(http).to have_received(:get).with('n2/some/path', query: { query: 1 }).once
72
+ expect(http).to have_received(:get).with('n3/some/path', query: { query: 1 }).once
65
73
  end
66
74
  end
67
75
  end
68
76
 
69
77
  describe RoundRobinStrategy do
70
78
  let :strategy do
71
- described_class.new(hosts, http)
79
+ described_class.new(connections)
72
80
  end
73
81
 
74
82
  let :http do
75
- double(:http, meth: nil)
83
+ double(:http, get: nil)
84
+ end
85
+
86
+ let :connections do
87
+ hosts.map { |host| Connection.new(http, host) }
76
88
  end
77
89
 
78
90
  describe '#execute' do
@@ -89,9 +101,9 @@ module Cottus
89
101
  end
90
102
 
91
103
  it 'gives up' do
92
- hosts.each { |h| http.stub(:meth).with("#{h}/some/path", {}).and_raise(error) }
104
+ hosts.each { |h| http.stub(:get).with("#{h}/some/path", {}).and_raise(error) }
93
105
 
94
- expect { strategy.execute(:meth, '/some/path') }.to raise_error(error)
106
+ expect { strategy.execute(:get, '/some/path') }.to raise_error(error)
95
107
  end
96
108
  end
97
109
 
@@ -101,16 +113,16 @@ module Cottus
101
113
  end
102
114
 
103
115
  it 'attempts to use each host until one succeeds' do
104
- ['n1', 'n2'].each { |h| http.stub(:meth).with("#{h}/some/path", {}).and_raise(error) }
116
+ ['n1', 'n2'].each { |h| http.stub(:get).with("#{h}/some/path", {}).and_raise(error) }
105
117
 
106
- strategy.execute(:meth, '/some/path')
107
- expect(http).to have_received(:meth).with('n3/some/path', {})
118
+ strategy.execute(:get, '/some/path')
119
+ expect(http).to have_received(:get).with('n3/some/path', {})
108
120
  end
109
121
 
110
122
  it 'gives up after trying all hosts' do
111
- hosts.each { |h| http.stub(:meth).with("#{h}/some/path", {}).and_raise(error) }
123
+ hosts.each { |h| http.stub(:get).with("#{h}/some/path", {}).and_raise(error) }
112
124
 
113
- expect { strategy.execute(:meth, '/some/path') }.to raise_error(error)
125
+ expect { strategy.execute(:get, '/some/path') }.to raise_error(error)
114
126
  end
115
127
  end
116
128
  end
@@ -121,11 +133,15 @@ module Cottus
121
133
 
122
134
  describe RetryableRoundRobinStrategy do
123
135
  let :strategy do
124
- described_class.new(hosts, http, timeouts: [0, 0, 0])
136
+ described_class.new(connections, timeouts: [0, 0, 0])
125
137
  end
126
138
 
127
139
  let :http do
128
- double(:http, meth: nil)
140
+ double(:http, get: nil)
141
+ end
142
+
143
+ let :connections do
144
+ hosts.map { |host| Connection.new(http, host) }
129
145
  end
130
146
 
131
147
  describe '#execute' do
@@ -142,18 +158,18 @@ module Cottus
142
158
  end
143
159
 
144
160
  it 'uses the single host for three consecutive exceptions' do
145
- expect(http).to receive(:meth).with('n1/some/path', {}).exactly(3).times.and_raise(error)
146
- expect(http).to receive(:meth).with('n1/some/path', {}).once
161
+ expect(http).to receive(:get).with('n1/some/path', {}).exactly(3).times.and_raise(error)
162
+ expect(http).to receive(:get).with('n1/some/path', {}).once
147
163
  expect(strategy).to receive(:sleep).with(0).exactly(3).times
148
164
 
149
- strategy.execute(:meth, '/some/path')
165
+ strategy.execute(:get, '/some/path')
150
166
  end
151
167
 
152
168
  it 'gives up after three retries' do
153
- expect(http).to receive(:meth).with('n1/some/path', {}).exactly(4).times.and_raise(error)
169
+ expect(http).to receive(:get).with('n1/some/path', {}).exactly(4).times.and_raise(error)
154
170
  expect(strategy).to receive(:sleep).with(0).exactly(3).times
155
171
 
156
- expect { strategy.execute(:meth, '/some/path') }.to raise_error(error)
172
+ expect { strategy.execute(:get, '/some/path') }.to raise_error(error)
157
173
  end
158
174
  end
159
175
 
@@ -163,19 +179,19 @@ module Cottus
163
179
  end
164
180
 
165
181
  it 'uses the same host for three consecutive exceptions' do
166
- expect(http).to receive(:meth).with('n1/some/path', {}).exactly(3).times.and_raise(error)
167
- expect(http).to receive(:meth).with('n1/some/path', {}).once
182
+ expect(http).to receive(:get).with('n1/some/path', {}).exactly(3).times.and_raise(error)
183
+ expect(http).to receive(:get).with('n1/some/path', {}).once
168
184
  expect(strategy).to receive(:sleep).with(0).exactly(3).times
169
185
 
170
- strategy.execute(:meth, '/some/path')
186
+ strategy.execute(:get, '/some/path')
171
187
  end
172
188
 
173
189
  it 'switches host after three retries' do
174
- expect(http).to receive(:meth).with('n1/some/path', {}).exactly(4).times.and_raise(error)
175
- expect(http).to receive(:meth).with('n2/some/path', {}).once
190
+ expect(http).to receive(:get).with('n1/some/path', {}).exactly(4).times.and_raise(error)
191
+ expect(http).to receive(:get).with('n2/some/path', {}).once
176
192
  expect(strategy).to receive(:sleep).with(0).exactly(3).times
177
193
 
178
- strategy.execute(:meth, '/some/path')
194
+ strategy.execute(:get, '/some/path')
179
195
  end
180
196
  end
181
197
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cottus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathias Söderberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-25 00:00:00.000000000 Z
11
+ date: 2013-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -32,6 +32,8 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - lib/cottus/client.rb
35
+ - lib/cottus/connection.rb
36
+ - lib/cottus/forward.rb
35
37
  - lib/cottus/strategies.rb
36
38
  - lib/cottus/version.rb
37
39
  - lib/cottus.rb