klomp 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Changes
2
2
  --------------------------------------------------------------------------------
3
3
 
4
+ 0.0.7 (2012/8/15)
5
+ ================================================================================
6
+
7
+ - back out the stomp/onstomp adapter code. we'll revisit this later
8
+
4
9
  0.0.6 (2012/8/10)
5
10
  ================================================================================
6
11
 
data/Gemfile CHANGED
@@ -2,7 +2,6 @@ source "http://rubygems.org"
2
2
 
3
3
  gem "onstomp", "~> 1.0.7"
4
4
  # gem 'onstomp', :path => '../onstomp'
5
- gem "stomp", "~> 1.2.4"
6
5
  gem "json"
7
6
  gem "uuid", "~> 2.3.5"
8
7
 
data/Gemfile.lock CHANGED
@@ -9,7 +9,6 @@ GEM
9
9
  systemu (~> 2.5.0)
10
10
  onstomp (1.0.7)
11
11
  rake (0.9.2.2)
12
- stomp (1.2.4)
13
12
  systemu (2.5.1)
14
13
  thor (0.15.2)
15
14
  uuid (2.3.5)
@@ -24,5 +23,4 @@ DEPENDENCIES
24
23
  json
25
24
  onstomp (~> 1.0.7)
26
25
  rake
27
- stomp (~> 1.2.4)
28
26
  uuid (~> 2.3.5)
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Klomp
2
2
 
3
- Klomp is a simple wrapper around the [Stomp](https://github.com/stompgem/stomp)
4
- and [OnStomp](https://github.com/meadvillerb/onstomp/) libraries with some
5
- additional HA and usability features:
3
+ Klomp is a simple wrapper around the [OnStomp](https://github.com/meadvillerb/onstomp/)
4
+ library with some additional HA and usability features:
6
5
 
7
6
  * When initialized with multiple broker URIs, Klomp will publish messages to
8
7
  one broker at a time, but will consume from all brokers simultaneously. This is
9
- a slight improvement over traditional failover clients that work by publishing
10
- and subscribing through a single "active" broker. This one-broker-at-a-time
11
- technique can lead to a split-brain scenario in which messages are only
12
- received by a subset of your STOMP clients. By consuming from all brokers
13
- simultaneously, Klomp ensures that no message is left behind.
8
+ a slight improvement over the regular [OnStomp::Failover::Client](http://mdvlrb.com/onstomp/OnStomp/Failover/Client.html)
9
+ which handles all publishing and subscribing through a single "active" broker.
10
+ This traditional one-broker-at-a-time technique can lead to a split-brain
11
+ scenario in which messages are only received by a subset of your STOMP clients.
12
+ By consuming from all brokers simultaneously, Klomp ensures that no message is
13
+ left behind.
14
14
 
15
15
  * Where applicable, message bodies are automatically translated between native
16
16
  Ruby and JSON objects.
@@ -26,13 +26,14 @@ subscribe block.
26
26
  ## Example usage
27
27
 
28
28
  The goal is that you should be able to use most (if not all) of the standard
29
- Stomp/OnStomp APIs via a `Klomp::Client`:
29
+ OnStomp API (see [OnStomp's UserNarrative](https://github.com/meadvillerb/onstomp/blob/master/extra_doc/UserNarrative.md))
30
+ via a `Klomp::Client`:
30
31
 
31
32
  client = Klomp::Client.new([ ... ])
32
33
 
33
34
  However, there will be some differences in the API due to how `Klomp::Client`
34
- manages connections. For example, while OnStomp's `connected?` method normally
35
- returns a single boolean value, Klomp's `connected?` will return many booleans
35
+ manages connections. For example, while the `connected?` method normally
36
+ returns a boolean value, Klomp's `connected?` will return an array of booleans
36
37
  (i.e. one result for each broker).
37
38
 
38
39
  ### Fibonacci back-off retry behavior
@@ -58,11 +59,6 @@ pass implements a `#generate` method that returns a string ID.
58
59
  <th>Default value</th>
59
60
  <th>Description</th>
60
61
  </tr>
61
- <tr>
62
- <td>:adapter</td>
63
- <td>:onstomp</td>
64
- <td>Stomp library to use. Currently, only :stomp and :onstomp are supported.</td>
65
- </tr>
66
62
  <tr>
67
63
  <td>:translate_json</td>
68
64
  <td>true</td>
@@ -79,9 +75,9 @@ pass implements a `#generate` method that returns a string ID.
79
75
  <td>Logger object</td>
80
76
  </tr>
81
77
  <tr>
82
- <td>:uuid</td>
83
- <td>UUID.new</td>
84
- <td>UUID generator object, responds to :generate and returns an ID</td>
78
+ <td>:uuid</td>
79
+ <td>UUID.new</td>
80
+ <td>UUID generator object, responds to :generate and returns an ID</td>
85
81
  </tr>
86
82
  </table>
87
83
 
data/lib/klomp.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'klomp/client'
2
2
 
3
3
  module Klomp
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.7'
5
5
  end
data/lib/klomp/client.rb CHANGED
@@ -3,47 +3,19 @@ require 'onstomp/failover'
3
3
  require 'json'
4
4
  require 'uuid'
5
5
  require 'logger'
6
- require 'pp'
7
6
 
8
7
  class OnStomp::Failover::Client
9
8
  # Save previous N, N-1 delays for fibonacci backoff
10
9
  attr_accessor :prev_retry_delay
11
10
  end
12
11
 
13
- class Hash
14
- def to_query_string
15
- self.inject([]) { |memo,(k,v)| memo + ["#{k}=#{v}"] }.join('&')
16
- end
17
- end
18
-
19
12
  module Klomp
20
13
 
21
14
  class Client
22
- attr_reader :read_conn, :write_conn, :all_conn
15
+ attr_reader :read_conn, :write_conn
23
16
  attr_accessor :last_connect_exception
24
17
 
25
- WRITE_ONLY_METHODS = [
26
- :abort,
27
- :begin,
28
- :commit,
29
- ]
30
-
31
- READ_ONLY_METHODS = [
32
- :ack,
33
- :nack,
34
- ]
35
-
36
- TRANSLATE_METHODS = {
37
- :connected? => {
38
- :stomp => :open?
39
- },
40
- :ack => {
41
- :stomp => :acknowledge
42
- }
43
- }
44
-
45
18
  def initialize(uri, options={})
46
- @adapter = options.fetch(:adapter, :onstomp)
47
19
  @translate_json = options.fetch(:translate_json, true)
48
20
  @auto_reply_to = options.fetch(:auto_reply_to, true)
49
21
  @logger = options.fetch(:logger, nil)
@@ -55,93 +27,44 @@ module Klomp
55
27
  options[:retry_delay] ||= 1
56
28
  options[:retry_attempts] ||= -1
57
29
 
58
- case @adapter
59
- when :onstomp
60
- if uri.is_a?(Array)
61
- @write_conn = OnStomp::Failover::Client.new(uri, options)
62
- @read_conn = uri.map {|obj| OnStomp::Failover::Client.new([obj], options) }
63
- else
64
- @write_conn = OnStomp::Failover::Client.new([uri], options)
65
- @read_conn = [@write_conn]
66
- end
67
- when :stomp
68
- require 'stomp'
69
-
70
- # Failover in the Stomp library is kind of flaky. If the client
71
- # temporarily loses its connection, it is eventually able to reconnect
72
- # and resume sending messages. However, the subscribe thread never
73
- # seems to recover. One workaround I discovered is to always create new
74
- # clients with the following URL scheme:
75
- #
76
- # failover:(stomp://localhost,stomp://localhost)
77
- #
78
- # Notice that we're using a failover URL in which the same host is
79
- # specified twice. It's a pretty silly hack, but it's the only way I've
80
- # been able to get failover to work reliably.
81
- #
82
- # - Mike Conigliaro
83
- #
84
- if uri.is_a?(Array)
85
- @write_conn = Stomp::Client.new("failover:(#{uri.join(',')})?#{options.to_query_string}")
86
- @read_conn = uri.map {|obj| Stomp::Client.new("failover:(#{obj},#{obj})?#{options.to_query_string}") }
87
- else
88
- @write_conn = Stomp::Client.new("failover:(#{uri},#{uri})?#{options.to_query_string}")
89
- @read_conn = [@write_conn]
90
- end
30
+ if uri.is_a?(Array)
31
+ @write_conn = OnStomp::Failover::Client.new(uri, options)
32
+ @read_conn = uri.map {|obj| OnStomp::Failover::Client.new([obj], options) }
91
33
  else
92
- raise ArgumentError, "Klomp does not support the #{@adapter} library"
34
+ @write_conn = OnStomp::Failover::Client.new([uri], options)
35
+ @read_conn = [@write_conn]
93
36
  end
94
37
  @all_conn = ([@write_conn] + @read_conn).uniq
95
38
  configure_connections
96
39
  end
97
40
 
98
41
  def connect
99
- case @adapter
100
- when :onstomp
101
- @all_conn.each do |conn|
102
- begin
103
- attempts = conn.retry_attempts
104
- conn.retry_attempts = 1
105
- conn.connect
106
- rescue OnStomp::Failover::MaximumRetriesExceededError
107
- location = conn.active_client.uri.dup.tap {|u| u.password = 'REDACTED' }.to_s
108
- msg = ": #{last_connect_exception.message}" if last_connect_exception
109
- raise OnStomp::ConnectFailedError, "initial connection failed for #{location}#{msg}"
110
- ensure
111
- conn.retry_attempts = attempts
112
- end
42
+ @all_conn.each do |conn|
43
+ begin
44
+ attempts = conn.retry_attempts
45
+ conn.retry_attempts = 1
46
+ conn.connect
47
+ rescue OnStomp::Failover::MaximumRetriesExceededError
48
+ location = conn.active_client.uri.dup.tap {|u| u.password = 'REDACTED' }.to_s
49
+ msg = ": #{last_connect_exception.message}" if last_connect_exception
50
+ raise OnStomp::ConnectFailedError, "initial connection failed for #{location}#{msg}"
51
+ ensure
52
+ conn.retry_attempts = attempts
113
53
  end
114
54
  end
115
55
  self
116
56
  end
117
57
 
118
- def disconnect
119
- method = case @adapter
120
- when :onstomp
121
- :disconnect
122
- when :stomp
123
- :close
124
- end
125
- @all_conn.inject({}) { |memo,obj| memo.update({ obj => obj.__send__(method) }) }
126
- end
127
-
128
- def send(dest, body, headers={}, &block)
58
+ def send(dest, body, headers={}, &cb)
129
59
  if @translate_json && body.respond_to?(:to_json)
130
60
  body = body.to_json
131
- headers['content-type'] = 'application/json'
61
+ headers[:'content-type'] = 'application/json'
132
62
  else
133
63
  body = body.to_s
134
64
  end
135
- uuid = headers['id'] = @uuid.generate if @uuid
65
+ uuid = headers[:id] = @uuid.generate if @uuid
136
66
  log.debug("[Sending] ID=#{uuid} Destination=#{dest} Body=#{body.inspect} Headers=#{headers.inspect}") if log
137
-
138
- method = case @adapter
139
- when :onstomp
140
- :send
141
- when :stomp
142
- :publish
143
- end
144
- @write_conn.__send__(method, dest, body, headers, &block)
67
+ @write_conn.send(dest, body, headers, &cb)
145
68
  end
146
69
  alias publish send
147
70
 
@@ -149,7 +72,7 @@ module Klomp
149
72
  frames = []
150
73
  @read_conn.each do |c|
151
74
  frames << c.subscribe(*args) do |msg|
152
- log.debug("[Received] ID=#{msg.headers['id']} Body=#{msg.body.inspect} Headers=#{msg.headers.to_hash.inspect}") if log
75
+ log.debug("[Received] ID=#{msg[:id]} Body=#{msg.body.inspect} Headers=#{msg.headers.to_hash.inspect}") if log
153
76
  if @translate_json
154
77
  msg.body = begin
155
78
  JSON.parse(msg.body)
@@ -158,11 +81,11 @@ module Klomp
158
81
  end
159
82
  end
160
83
  reply_args = yield msg
161
- if @auto_reply_to && !msg.headers['reply-to'].nil?
84
+ if @auto_reply_to && !msg.headers[:'reply-to'].nil?
162
85
  if reply_args.is_a?(Array)
163
- send(msg.headers['reply-to'], *reply_args)
86
+ send(msg.headers[:'reply-to'], *reply_args)
164
87
  else
165
- send(msg.headers['reply-to'], reply_args)
88
+ send(msg.headers[:'reply-to'], reply_args)
166
89
  end
167
90
  end
168
91
  end
@@ -171,16 +94,11 @@ module Klomp
171
94
  end
172
95
 
173
96
  def unsubscribe(frames, headers={})
174
- case @adapter
175
- when :onstomp
176
- if !frames.respond_to?(:length) || frames.length != @read_conn.length
177
- raise ArgumentError,
178
- "frames is not an array or its length does not match number of connections"
179
- end
180
- frames.each_with_index.map {|f,i| @read_conn[i].unsubscribe f, headers }
181
- else
182
- @read_conn.each { |obj| obj.unsubscribe(frames, headers) }
97
+ if !frames.respond_to?(:length) || frames.length != @read_conn.length
98
+ raise ArgumentError,
99
+ "frames is not an array or its length does not match number of connections"
183
100
  end
101
+ frames.each_with_index.map {|f,i| @read_conn[i].unsubscribe f, headers }
184
102
  end
185
103
 
186
104
  def subscriptions
@@ -191,45 +109,47 @@ module Klomp
191
109
  @logger
192
110
  end
193
111
 
194
- def method_missing(method, *args, &block)
195
- if TRANSLATE_METHODS.has_key?(method) && TRANSLATE_METHODS[method].has_key?(@adapter)
196
- method = TRANSLATE_METHODS[method][@adapter]
197
- end
112
+ WRITE_ONLY_METHODS = [
113
+ :abort,
114
+ :begin,
115
+ :commit,
116
+ ]
198
117
 
118
+ READ_ONLY_METHODS = [
119
+ :ack,
120
+ :nack,
121
+ ]
122
+
123
+ def method_missing(method, *args, &block)
199
124
  case method
200
125
  when *WRITE_ONLY_METHODS
201
126
  @write_conn.__send__(method, *args, &block)
202
127
  when *READ_ONLY_METHODS
203
- @read_conn.inject({}) { |memo,obj| memo.update({ obj => obj.__send__(method, *args, &block) }) }
128
+ @read_conn.map {|c| c.__send__(method, *args, &block) }
204
129
  else
205
- @all_conn.inject({}) { |memo,obj| memo.update({ obj => obj.__send__(method, *args, &block) }) }
130
+ @all_conn.map {|c| c.__send__(method, *args) }
206
131
  end
207
132
  end
208
133
 
209
134
  private
210
-
211
135
  def configure_connections
212
- case @adapter
213
- when :onstomp
214
- klomp_client = self
215
- @all_conn.each do |c|
216
- if @fib_retry_backoff
217
- c.before_failover_retry do |conn, attempt|
218
- if attempt == 1
219
- conn.prev_retry_delay, conn.retry_delay = 0, 1
220
- else
221
- conn.prev_retry_delay, conn.retry_delay = conn.retry_delay, conn.prev_retry_delay + conn.retry_delay
222
- end
136
+ klomp_client = self
137
+ @all_conn.each do |c|
138
+ if @fib_retry_backoff
139
+ c.before_failover_retry do |conn, attempt|
140
+ if attempt == 1
141
+ conn.prev_retry_delay, conn.retry_delay = 0, 1
142
+ else
143
+ conn.prev_retry_delay, conn.retry_delay = conn.retry_delay, conn.prev_retry_delay + conn.retry_delay
223
144
  end
224
145
  end
146
+ end
225
147
 
226
- c.on_failover_connect_failure do
227
- klomp_client.last_connect_exception = $!
228
- end
148
+ c.on_failover_connect_failure do
149
+ klomp_client.last_connect_exception = $!
229
150
  end
230
151
  end
231
152
  end
232
-
233
153
  end
234
154
 
235
155
  end
@@ -10,12 +10,12 @@ task :test_failover do
10
10
  client = Klomp::Client.new([
11
11
  'stomp://admin:password@localhost:61613',
12
12
  'stomp://admin:password@127.0.0.1:62613'
13
- ], :adapter => :onstomp).connect
13
+ ]).connect
14
14
 
15
15
  last_i = nil
16
16
  client.subscribe("/queue/test") do |msg|
17
17
  print "-"
18
- last_i = msg.body.gsub('"', '').to_i
18
+ last_i = msg.body.to_i
19
19
  end
20
20
 
21
21
  begin
@@ -0,0 +1,232 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+
4
+ require 'klomp'
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Klomp::Client do
8
+
9
+ include KlompTestHelpers
10
+
11
+ before do
12
+ @uris = [
13
+ 'stomp://admin:password@localhost:61613',
14
+ 'stomp://admin:password@127.0.0.1:62613'
15
+ ]
16
+ @destination = '/queue/test_component.test_event'
17
+ end
18
+
19
+ it 'accepts a single uri and establishes separate failover connections for writes and reads' do
20
+ client = Klomp::Client.new(@uris.first).connect
21
+
22
+ assert_equal [client.write_conn], client.read_conn
23
+ assert client.write_conn.connected?
24
+
25
+ client.disconnect
26
+ end
27
+
28
+ it 'accepts an array of uris and establishes separate failover connections for writes and reads' do
29
+ client = Klomp::Client.new(@uris).connect
30
+
31
+ assert client.write_conn.connected?
32
+ refute_empty client.read_conn
33
+ client.read_conn.each do |obj|
34
+ assert obj.connected?
35
+ end
36
+
37
+ client.disconnect
38
+ end
39
+
40
+ it 'raises an error if authentication fails' do
41
+ assert_raises OnStomp::ConnectFailedError do
42
+ Klomp::Client.new(@uris.first.sub('password', 'psswrd')).connect
43
+ end
44
+ end
45
+
46
+ it 'disconnnects' do
47
+ client = Klomp::Client.new(@uris.first).connect
48
+ assert client.write_conn.connected?
49
+ client.disconnect
50
+ refute client.write_conn.connected?
51
+ end
52
+
53
+ it 'has a logger' do
54
+ logger = Logger.new(STDOUT)
55
+ client = Klomp::Client.new(@uris, :logger=>logger)
56
+ assert_equal client.log, logger
57
+ end
58
+
59
+ it 'sends heartbeat' do
60
+ client = Klomp::Client.new(@uris).connect
61
+ client.beat
62
+ client.disconnect
63
+ end
64
+
65
+ it 'sends requests and gets responses' do
66
+ client = Klomp::Client.new(@uris).connect
67
+ body = { 'body' => rand(36**128).to_s(36) }
68
+
69
+ client.send(@destination, body, :ack=>'client')
70
+
71
+ got_message = false
72
+ client.subscribe(@destination) do |msg|
73
+ got_message = true if msg.body == body
74
+ client.ack(msg)
75
+ end
76
+ let_background_processor_run
77
+ assert got_message
78
+
79
+ client.disconnect
80
+ end
81
+
82
+ it 'automatically publishes responses to the reply-to destination' do
83
+ client = Klomp::Client.new(@uris).connect
84
+ reply_to_body = { 'reply_to_body' => rand(36**128).to_s(36) }
85
+
86
+ client.send(@destination, nil, { 'reply-to' => @destination })
87
+
88
+ got_message = false
89
+ client.subscribe(@destination) do |msg|
90
+ got_message = true if msg.body == reply_to_body
91
+ reply_to_body
92
+ end
93
+ let_background_processor_run
94
+ assert got_message
95
+
96
+ client.disconnect
97
+ end
98
+
99
+ it 'unsubscribes' do
100
+ client = Klomp::Client.new(@uris).connect
101
+
102
+ subscribe_frames = client.subscribe(@destination) { |msg| }
103
+ unsub_frames = client.unsubscribe(subscribe_frames)
104
+ assert_equal subscribe_frames.length, unsub_frames.length
105
+ let_background_processor_run
106
+
107
+ assert client.subscriptions.flatten.empty?, "expected connection to have no subscriptions"
108
+
109
+ client.disconnect
110
+ end
111
+
112
+ it 'sends all unknown options through to OnStomp' do
113
+ client = Klomp::Client.new(@uris.first, :haz_cheezburgers => true, :retry_attempts => 42).connect
114
+ assert client.write_conn.connected?
115
+ assert_equal 42, client.write_conn.retry_attempts
116
+ client.disconnect
117
+ end
118
+
119
+ it 'uses a fibonacci back-off approach to reconnect' do
120
+ good_client = Object.new
121
+ def good_client.connect; true; end
122
+ def good_client.connected?; true; end
123
+ def good_client.connection; true; end
124
+
125
+ bad_client = Object.new
126
+ def bad_client.connect; raise "could not connect"; end
127
+ def bad_client.connected?; false; end
128
+
129
+ test_context = self
130
+ attempts = 0
131
+ conn = nil
132
+ fib = lambda {|n| (1..n).inject([0, 1]) {|fib,_| [fib[1], fib[0]+fib[1]]}.first}
133
+
134
+ pool_class = Class.new do
135
+ def initialize(*) end
136
+ def each(&blk) end
137
+ define_method :next_client do
138
+ attempts += 1
139
+ test_context.assert_equal fib[attempts], conn.retry_delay
140
+ if attempts == 6
141
+ good_client
142
+ else
143
+ bad_client
144
+ end
145
+ end
146
+ end
147
+
148
+ client = Klomp::Client.new(@uris.first, :pool => pool_class)
149
+ conn = client.write_conn
150
+ def conn.sleep_for_retry(*) end # skip sleep between retries for test
151
+
152
+ client.reconnect
153
+ assert_equal 6, attempts
154
+ end
155
+
156
+ it 'sends messages with uuids in the :id header' do
157
+ client = Klomp::Client.new(@uris, :translate_json => false).connect
158
+ client.send(@destination, '')
159
+
160
+ received_message = false
161
+ client.subscribe(@destination) do |msg|
162
+ received_message = msg
163
+ end
164
+ let_background_processor_run
165
+ assert received_message
166
+ assert received_message[:id], "message did not have an id"
167
+ assert received_message[:id] =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
168
+ "message id did not look like a uuid"
169
+
170
+ client.disconnect
171
+ end
172
+
173
+ it 'allows customization of the uuid generator' do
174
+ generator = Object.new
175
+ def generator.generate; "42"; end
176
+
177
+ client = Klomp::Client.new(@uris, :translate_json => false, :uuid => generator).connect
178
+ client.send(@destination, '')
179
+
180
+ received_message = false
181
+ client.subscribe(@destination) do |msg|
182
+ received_message = msg
183
+ end
184
+ let_background_processor_run
185
+ assert received_message
186
+ assert received_message[:id], "message did not have an id"
187
+ assert_equal "42", received_message[:id]
188
+
189
+ client.disconnect
190
+ end
191
+
192
+ it 'allows disabling generated message ids' do
193
+ client = Klomp::Client.new(@uris, :translate_json => false, :uuid => false).connect
194
+ client.send(@destination, '')
195
+
196
+ received_message = false
197
+ client.subscribe(@destination) do |msg|
198
+ received_message = msg
199
+ end
200
+ let_background_processor_run
201
+ assert received_message
202
+ refute received_message[:id], "message had an id"
203
+
204
+ client.disconnect
205
+ end
206
+
207
+ it 'logs message ids' do
208
+ logger = Object.new
209
+ def logger.msgs; @msgs; end
210
+ def logger.debug(msg) (@msgs ||= []) << msg end
211
+
212
+ client = Klomp::Client.new(@uris, :translate_json => false, :logger => logger).connect
213
+ client.send(@destination, '')
214
+
215
+ received_message = false
216
+ client.subscribe(@destination) do |msg|
217
+ received_message = msg
218
+ end
219
+ let_background_processor_run
220
+ assert received_message
221
+ assert received_message[:id], "message did not have an id"
222
+
223
+ assert_equal 2, logger.msgs.length
224
+ assert logger.msgs[0] =~ /\[Sending\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
225
+ sent_id = $1
226
+ assert logger.msgs[1] =~ /\[Received\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
227
+ received_id = $1
228
+ assert_equal sent_id, received_id
229
+
230
+ client.disconnect
231
+ end
232
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klomp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-10 00:00:00.000000000 Z
12
+ date: 2012-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: onstomp
@@ -78,9 +78,7 @@ files:
78
78
  - lib/klomp.rb
79
79
  - lib/klomp/client.rb
80
80
  - tasks/test_failover.rake
81
- - test/test_client_all.rb
82
- - test/test_client_onstomp.rb
83
- - test/test_client_stomp.rb
81
+ - test/test_client.rb
84
82
  - test/test_helper.rb
85
83
  homepage: https://github.com/livingsocial/klomp
86
84
  licenses: []
@@ -96,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
94
  version: '0'
97
95
  segments:
98
96
  - 0
99
- hash: -1910889246392223441
97
+ hash: 1929971310502642728
100
98
  required_rubygems_version: !ruby/object:Gem::Requirement
101
99
  none: false
102
100
  requirements:
@@ -110,7 +108,5 @@ signing_key:
110
108
  specification_version: 3
111
109
  summary: A simple wrapper around the OnStomp library with additional features
112
110
  test_files:
113
- - test/test_client_all.rb
114
- - test/test_client_onstomp.rb
115
- - test/test_client_stomp.rb
111
+ - test/test_client.rb
116
112
  - test/test_helper.rb
@@ -1,167 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/pride'
3
-
4
- require 'klomp'
5
- require File.expand_path('../test_helper', __FILE__)
6
-
7
- describe Klomp::Client do
8
-
9
- include KlompTestHelpers
10
-
11
- before do
12
- @uris = [
13
- 'stomp://admin:password@localhost:61613',
14
- 'stomp://admin:password@127.0.0.1:62613'
15
- ]
16
- @destination = '/queue/test_component.test_event'
17
- end
18
-
19
- [ :onstomp, :stomp ].each do |adapter|
20
-
21
- it "(#{adapter}) has a logger" do
22
- logger = Logger.new(STDOUT)
23
- client = Klomp::Client.new(@uris, :adapter => adapter, :logger => logger)
24
- assert_equal client.log, logger
25
- end
26
-
27
- it "(#{adapter}) sends all unknown options through to the underlying library" do
28
- client = Klomp::Client.new(@uris.first, :adapter => adapter, :haz_cheezburgers => true, :retry_attempts => 42).connect
29
- assert client.connected?.values.all?
30
- assert_equal 42, client.write_conn.retry_attempts if adapter == :onstomp
31
- client.disconnect
32
- end
33
-
34
- it "(#{adapter}) accepts a single uri and establishes separate failover connections for writes and reads" do
35
- client = Klomp::Client.new(@uris.first, :adapter => adapter).connect
36
- assert_equal [client.write_conn], client.read_conn
37
- assert client.connected?.values.all?
38
- client.disconnect
39
- end
40
-
41
- it "(#{adapter}) accepts an array of uris and establishes separate failover connections for writes and reads" do
42
- client = Klomp::Client.new(@uris, :adapter => adapter).connect
43
- assert client.all_conn.length == @uris.length + 1
44
- assert client.connected?.values.all?
45
- client.disconnect
46
- end
47
-
48
- it "(#{adapter}) disconnnects" do
49
- client = Klomp::Client.new(@uris.first, :adapter => adapter).connect
50
- assert client.connected?.values.all?
51
- client.disconnect
52
- refute client.connected?.values.any?
53
- end
54
-
55
- it "(#{adapter}) sends requests and gets responses" do
56
- client = Klomp::Client.new(@uris, :adapter => adapter).connect
57
- body = { 'body' => rand(36**128).to_s(36) }
58
-
59
- client.send(@destination, body, :ack=>'client')
60
- got_message = false
61
- client.subscribe(@destination) do |msg|
62
- got_message = true if msg.body == body
63
- client.ack(msg)
64
- end
65
- let_background_processor_run
66
- client.disconnect
67
-
68
- assert got_message
69
- end
70
-
71
- it "(#{adapter}) automatically publishes responses to the reply-to destination" do
72
- client = Klomp::Client.new(@uris, :adapter => adapter).connect
73
- reply_to_body = { 'reply_to_body' => rand(36**128).to_s(36) }
74
-
75
- client.send(@destination, nil, { 'reply-to' => @destination })
76
-
77
- got_message = false
78
- client.subscribe(@destination) do |msg|
79
- got_message = true if msg.body == reply_to_body
80
- reply_to_body
81
- end
82
- let_background_processor_run
83
- client.disconnect
84
-
85
- assert got_message
86
- end
87
-
88
- it "(#{adapter}) sends messages with uuids in the 'id' header" do
89
- client = Klomp::Client.new(@uris, :adapter => adapter, :translate_json => false).connect
90
- client.send(@destination, '')
91
-
92
- received_message = false
93
- client.subscribe(@destination) do |msg|
94
- received_message = msg
95
- end
96
- let_background_processor_run
97
- client.disconnect
98
-
99
- assert received_message
100
- assert received_message.headers['id'], "message did not have an id"
101
- assert received_message.headers['id'] =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
102
- "message id did not look like a uuid"
103
- end
104
-
105
- it "(#{adapter}) allows customization of the uuid generator" do
106
- generator = Object.new
107
- def generator.generate; "42"; end
108
-
109
- client = Klomp::Client.new(@uris, :adapter => adapter, :translate_json => false, :uuid => generator).connect
110
- client.send(@destination, '')
111
-
112
- received_message = false
113
- client.subscribe(@destination) do |msg|
114
- received_message = msg
115
- end
116
- let_background_processor_run
117
- client.disconnect
118
-
119
- assert received_message
120
- assert received_message.headers['id'], "message did not have an id"
121
- assert_equal "42", received_message.headers['id']
122
- end
123
-
124
- it "(#{adapter}) allows disabling generated message ids" do
125
- client = Klomp::Client.new(@uris, :adapter => adapter, :translate_json => false, :uuid => false).connect
126
- client.send(@destination, '')
127
-
128
- received_message = false
129
- client.subscribe(@destination) do |msg|
130
- received_message = msg
131
- end
132
- let_background_processor_run
133
- client.disconnect
134
-
135
- assert received_message
136
- refute received_message.headers['id'], "message had an id"
137
- end
138
-
139
- it "(#{adapter}) logs message ids" do
140
- logger = Object.new
141
- def logger.msgs; @msgs; end
142
- def logger.debug(msg) (@msgs ||= []) << msg end
143
-
144
- client = Klomp::Client.new(@uris, :adapter => adapter, :translate_json => false, :logger => logger).connect
145
- client.send(@destination, '')
146
-
147
- received_message = false
148
- client.subscribe(@destination) do |msg|
149
- received_message = msg
150
- end
151
- let_background_processor_run
152
- client.disconnect
153
-
154
- assert received_message
155
- assert received_message.headers['id'], "message did not have an id"
156
-
157
- assert_equal 2, logger.msgs.length
158
- assert logger.msgs[0] =~ /\[Sending\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
159
- sent_id = $1
160
- assert logger.msgs[1] =~ /\[Received\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
161
- received_id = $1
162
- assert_equal sent_id, received_id
163
- end
164
-
165
- end
166
-
167
- end
@@ -1,81 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/pride'
3
-
4
- require 'klomp'
5
- require File.expand_path('../test_helper', __FILE__)
6
-
7
- describe Klomp::Client do
8
-
9
- include KlompTestHelpers
10
-
11
- before do
12
- @adapter = :onstomp
13
- @uris = [
14
- 'stomp://admin:password@localhost:61613',
15
- 'stomp://admin:password@127.0.0.1:62613'
16
- ]
17
- @destination = '/queue/test_component.test_event'
18
- end
19
-
20
- it "raises an error if authentication fails" do
21
- assert_raises OnStomp::ConnectFailedError do
22
- Klomp::Client.new(@uris.first.sub('password', 'psswrd'), :adapter => @adapter).connect
23
- end
24
- end
25
-
26
- it "sends heartbeat" do
27
- client = Klomp::Client.new(@uris, :adapter => @adapter).connect
28
- client.beat
29
- client.disconnect
30
- end
31
-
32
- it "unsubscribes" do
33
- client = Klomp::Client.new(@uris, :adapter => @adapter).connect
34
-
35
- subscribe_frames = client.subscribe(@destination) { |msg| }
36
- unsub_frames = client.unsubscribe(subscribe_frames)
37
- let_background_processor_run
38
- client.disconnect
39
-
40
- assert_equal subscribe_frames.length, unsub_frames.length
41
- assert client.subscriptions.flatten.empty?, "expected connection to have no subscriptions"
42
- end
43
-
44
- it 'uses a fibonacci back-off approach to reconnect' do
45
- good_client = Object.new
46
- def good_client.connect; true; end
47
- def good_client.connected?; true; end
48
- def good_client.connection; true; end
49
-
50
- bad_client = Object.new
51
- def bad_client.connect; raise "could not connect"; end
52
- def bad_client.connected?; false; end
53
-
54
- test_context = self
55
- attempts = 0
56
- conn = nil
57
- fib = lambda {|n| (1..n).inject([0, 1]) {|fib,_| [fib[1], fib[0]+fib[1]]}.first}
58
-
59
- pool_class = Class.new do
60
- def initialize(*) end
61
- def each(&blk) end
62
- define_method :next_client do
63
- attempts += 1
64
- test_context.assert_equal fib[attempts], conn.retry_delay
65
- if attempts == 6
66
- good_client
67
- else
68
- bad_client
69
- end
70
- end
71
- end
72
-
73
- client = Klomp::Client.new(@uris.first, :adapter => @adapter, :pool => pool_class)
74
- conn = client.write_conn
75
- def conn.sleep_for_retry(*) end # skip sleep between retries for test
76
-
77
- client.reconnect
78
- assert_equal 6, attempts
79
- end
80
-
81
- end
@@ -1,27 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/pride'
3
-
4
- require 'klomp'
5
- require File.expand_path('../test_helper', __FILE__)
6
-
7
- describe Klomp::Client do
8
-
9
- include KlompTestHelpers
10
-
11
- before do
12
- @adapter = :stomp
13
- @uris = [
14
- 'stomp://admin:password@localhost:61613',
15
- 'stomp://admin:password@127.0.0.1:62613'
16
- ]
17
- @destination = '/queue/test_component.test_event'
18
- end
19
-
20
- it "unsubscribes" do
21
- client = Klomp::Client.new(@uris, :adapter => @adapter).connect
22
- client.subscribe(@destination) { |msg| }
23
- client.unsubscribe(@destination)
24
- client.disconnect
25
- end
26
-
27
- end