cottus 0.1.4 → 0.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 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