klomp 0.0.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,36 @@
1
+ require 'simplecov'
2
+ require 'klomp'
3
+ require 'rspec'
4
+ require 'rspec-given'
5
+
6
+ Dir[File.expand_path('../support/', __FILE__) + '/*.rb'].each {|f| require f }
7
+
8
+ module Frames
9
+ def frame(type)
10
+ File.read(File.expand_path("../frames/#{type}.txt", __FILE__))
11
+ end
12
+ end
13
+
14
+ def queue_available?
15
+ s = TCPSocket.new 'localhost', 61613
16
+ true
17
+ rescue
18
+ false
19
+ ensure
20
+ s && s.close
21
+ end
22
+
23
+ RSpec.configure do |config|
24
+ config.treat_symbols_as_metadata_keys_with_true_values = true
25
+ config.run_all_when_everything_filtered = true
26
+ config.filter_run :focus
27
+
28
+ config.filter_run_excluding :acceptance unless queue_available?
29
+ if ENV['PERF']
30
+ config.filter_run :performance
31
+ else
32
+ config.filter_run_excluding :performance
33
+ end
34
+
35
+ config.include Frames
36
+ end
@@ -0,0 +1,101 @@
1
+ # Like rspec-spies, but better.
2
+ #
3
+ # Leverages RSpec's builtin MessageExpectation class/DSL so that you can use arg
4
+ # matchers, # of invocation matchers, etc.
5
+
6
+ require 'rspec/mocks'
7
+
8
+ class RSpec::Mocks::Proxy
9
+ attr_reader :messages_received, :error_generator
10
+
11
+ # This does the equivalent of rspec-spies monkey patch, but with less work
12
+ alias orig_message_received message_received
13
+ def message_received(*args, &block)
14
+ record_message_received(*args, &block)
15
+ orig_message_received(*args, &block)
16
+ end
17
+
18
+ # Be sure to reset the messages received between specs!
19
+ alias orig_reset reset
20
+ def reset
21
+ orig_reset.tap { messages_received.clear }
22
+ end
23
+ end
24
+
25
+ module RSpec::Mocks::Methods
26
+
27
+ def messages_received
28
+ __mock_proxy.messages_received
29
+ end
30
+
31
+ def error_generator
32
+ __mock_proxy.error_generator
33
+ end
34
+
35
+ def replay_on(other, &match_block)
36
+ messages_received.each do |msg, args, &block|
37
+ if !match_block || match_block.call(msg,args,&block)
38
+ other.send msg, *args, &block
39
+ end
40
+ end
41
+ end
42
+
43
+ def reset
44
+ __mock_proxy.reset
45
+ end
46
+ end
47
+
48
+ class RSpec::Mocks::MessageExpectation
49
+ public :error_generator=
50
+ end
51
+
52
+ module RSpec::Matchers::HaveReceived
53
+ class Matcher
54
+ def initialize(message, expected_from)
55
+ @message, @mock = message, RSpec::Mocks::Mock.new
56
+ @mock.stub!(message)
57
+ @expectation = @mock.should_receive(message, expected_from: expected_from)
58
+ end
59
+
60
+ def matches?(actual)
61
+ begin
62
+ @expectation.error_generator = actual.error_generator
63
+ actual.replay_on(@mock) {|msg,args,&block| @message == msg }
64
+ @mock.rspec_verify
65
+ true
66
+ rescue RSpec::Mocks::MockExpectationError => e
67
+ @exception = e
68
+ false
69
+ end
70
+ end
71
+
72
+ def description
73
+ "have received #{@message.inspect}"
74
+ end
75
+
76
+ def failure_message_for_should
77
+ @exception.message
78
+ end
79
+
80
+ def failure_message_for_should_not
81
+ begin
82
+ @expectation.generate_error
83
+ rescue RSpec::Mocks::MockExpectationError => e
84
+ e.message.sub(/expected.*/m, "unexpected match")
85
+ end
86
+ end
87
+
88
+ def method_missing(meth, *args, &block)
89
+ @expectation.send meth, *args, &block
90
+ self
91
+ end
92
+ end
93
+
94
+ def have_received(message)
95
+ Matcher.new(message, caller(1)[0])
96
+ end
97
+ end
98
+
99
+ RSpec.configure do |config|
100
+ config.include RSpec::Matchers::HaveReceived
101
+ end
metadata CHANGED
@@ -1,89 +1,219 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klomp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
- - LivingSocial
8
+ - Nick Sieger
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-15 00:00:00.000000000 Z
12
+ date: 2012-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: onstomp
15
+ name: rdoc
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.0.7
22
- type: :runtime
21
+ version: '3.10'
22
+ type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.0.7
29
+ version: '3.10'
30
30
  - !ruby/object:Gem::Dependency
31
- name: json
31
+ name: hoe-bundler
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
35
- - - ! '>='
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.1.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.1.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: hoe-gemspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
36
60
  - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :runtime
61
+ version: 1.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: hoe-git
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.5.0
70
+ type: :development
39
71
  prerelease: false
40
72
  version_requirements: !ruby/object:Gem::Requirement
41
73
  none: false
42
74
  requirements:
43
- - - ! '>='
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.5.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.11.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
44
92
  - !ruby/object:Gem::Version
45
- version: '0'
93
+ version: 2.11.0
46
94
  - !ruby/object:Gem::Dependency
47
- name: uuid
95
+ name: ZenTest
48
96
  requirement: !ruby/object:Gem::Requirement
49
97
  none: false
50
98
  requirements:
51
99
  - - ~>
52
100
  - !ruby/object:Gem::Version
53
- version: 2.3.5
54
- type: :runtime
101
+ version: 4.8.0
102
+ type: :development
55
103
  prerelease: false
56
104
  version_requirements: !ruby/object:Gem::Requirement
57
105
  none: false
58
106
  requirements:
59
107
  - - ~>
60
108
  - !ruby/object:Gem::Version
61
- version: 2.3.5
62
- description: A simple wrapper around the OnStomp library with additional features
109
+ version: 4.8.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec-given
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '1.0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 0.6.0
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 0.6.0
142
+ - !ruby/object:Gem::Dependency
143
+ name: hoe
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: '3.0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: '3.0'
158
+ description: ! 'Klomp is a simple [Stomp] messaging client that keeps your sanity
159
+ intact.
160
+
161
+
162
+ The purpose of Klomp is to be the simplest possible Stomp client. No in-memory
163
+
164
+ buffering of outgoing messages, no fanout subscriptions in-process, no
165
+
166
+ transactions, no complicated messaging patterns. Code simple enough so that when
167
+
168
+ something goes wrong, the problem is obvious.
169
+
170
+
171
+ [Stomp]: http://stomp.github.com/'
63
172
  email:
64
- - dev.happiness@livingsocial.com
173
+ - nick.sieger@livingsocial.com
65
174
  executables: []
66
175
  extensions: []
67
- extra_rdoc_files: []
176
+ extra_rdoc_files:
177
+ - Manifest.txt
68
178
  files:
69
- - .gitignore
70
- - .rvmrc
179
+ - .rspec
180
+ - .simplecov
71
181
  - ChangeLog.md
72
182
  - Gemfile
73
183
  - Gemfile.lock
74
- - Procfile
184
+ - Manifest.txt
75
185
  - README.md
76
186
  - Rakefile
77
187
  - klomp.gemspec
78
188
  - lib/klomp.rb
79
- - lib/klomp/client.rb
80
- - tasks/test_failover.rake
81
- - test/test_client.rb
82
- - test/test_helper.rb
83
- homepage: https://github.com/livingsocial/klomp
189
+ - lib/klomp/connection.rb
190
+ - lib/klomp/frames.rb
191
+ - lib/klomp/sentinel.rb
192
+ - spec/acceptance/acceptance_spec.rb
193
+ - spec/frames/auth_error.txt
194
+ - spec/frames/connect.txt
195
+ - spec/frames/connect_vhost.txt
196
+ - spec/frames/connected.txt
197
+ - spec/frames/disconnect.txt
198
+ - spec/frames/error.txt
199
+ - spec/frames/greeting.txt
200
+ - spec/frames/message.txt
201
+ - spec/frames/receipt.txt
202
+ - spec/frames/subscribe.txt
203
+ - spec/frames/unsubscribe.txt
204
+ - spec/klomp/connection_spec.rb
205
+ - spec/klomp/frames_spec.rb
206
+ - spec/klomp/sentinel_spec.rb
207
+ - spec/klomp_spec.rb
208
+ - spec/spec_helper.rb
209
+ - spec/support/have_received.rb
210
+ - .gemtest
211
+ homepage: http://github.com/livingsocial/klomp
84
212
  licenses: []
85
213
  post_install_message:
86
- rdoc_options: []
214
+ rdoc_options:
215
+ - --main
216
+ - README.md
87
217
  require_paths:
88
218
  - lib
89
219
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -94,7 +224,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
94
224
  version: '0'
95
225
  segments:
96
226
  - 0
97
- hash: -4086948349046815425
227
+ hash: -1626497956462206890
98
228
  required_rubygems_version: !ruby/object:Gem::Requirement
99
229
  none: false
100
230
  requirements:
@@ -102,11 +232,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
232
  - !ruby/object:Gem::Version
103
233
  version: '0'
104
234
  requirements: []
105
- rubyforge_project:
235
+ rubyforge_project: klomp
106
236
  rubygems_version: 1.8.24
107
237
  signing_key:
108
238
  specification_version: 3
109
- summary: A simple wrapper around the OnStomp library with additional features
110
- test_files:
111
- - test/test_client.rb
112
- - test/test_helper.rb
239
+ summary: Klomp is a simple [Stomp] messaging client that keeps your sanity intact
240
+ test_files: []
data/.gitignore DELETED
@@ -1 +0,0 @@
1
- pkg/
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm use --create 1.9.3@klomp
data/Procfile DELETED
@@ -1,2 +0,0 @@
1
- apollo_primary: /usr/local/var/apollo-primary/bin/apollo-broker run
2
- apollo_secondary: /usr/local/var/apollo-secondary/bin/apollo-broker run
@@ -1,163 +0,0 @@
1
- require 'onstomp'
2
- require 'onstomp/failover'
3
- require 'json'
4
- require 'uuid'
5
- require 'logger'
6
-
7
- class OnStomp::Failover::Client
8
- # Save previous N, N-1 delays for fibonacci backoff
9
- attr_accessor :prev_retry_delay
10
- end
11
-
12
- module Klomp
13
-
14
- class Client
15
- attr_reader :read_conn, :write_conn, :all_conn, :vhost
16
- attr_accessor :last_connect_exception
17
-
18
- def initialize(uri, options={})
19
- @translate_json = options.fetch(:translate_json, true)
20
- @auto_reply_to = options.fetch(:auto_reply_to, true)
21
- @logger = options.fetch(:logger, nil)
22
- @uuid = options.fetch(:uuid) { UUID.new }
23
- @vhost = options.fetch(:vhost, nil)
24
-
25
- @fib_retry_backoff = !options.has_key?(:retry_attempts) && !options.has_key?(:retry_delay)
26
-
27
- # defaults for retry delay and attempts
28
- options[:retry_delay] ||= 1
29
- options[:retry_attempts] ||= -1
30
-
31
- if uri.is_a?(Array)
32
- @write_conn = OnStomp::Failover::Client.new(uri, options)
33
- @read_conn = uri.map {|obj| OnStomp::Failover::Client.new([obj], options) }
34
- else
35
- @write_conn = OnStomp::Failover::Client.new([uri], options)
36
- @read_conn = [@write_conn]
37
- end
38
- @all_conn = ([@write_conn] + @read_conn).uniq
39
- configure_connections
40
- end
41
-
42
- def connect
43
- @all_conn.each do |conn|
44
- begin
45
- attempts = conn.retry_attempts
46
- conn.retry_attempts = 1
47
- conn.connect
48
- rescue OnStomp::Failover::MaximumRetriesExceededError
49
- location = conn.active_client.uri.dup.tap {|u| u.password = 'REDACTED' }.to_s
50
- msg = ": #{last_connect_exception.message}" if last_connect_exception
51
- raise OnStomp::ConnectFailedError, "initial connection failed for #{location}#{msg}"
52
- ensure
53
- conn.retry_attempts = attempts
54
- end
55
- end
56
- self
57
- end
58
-
59
- def send(dest, body, headers={}, &cb)
60
- if @translate_json && body.respond_to?(:to_json)
61
- body = body.to_json
62
- headers[:'content-type'] = 'application/json'
63
- else
64
- body = body.to_s
65
- end
66
- uuid = headers[:id] = @uuid.generate if @uuid
67
- log.debug("[Sending] ID=#{uuid} Destination=#{dest} Body=#{body.inspect} Headers=#{headers.inspect}") if log
68
- @write_conn.send(dest, body, headers, &cb)
69
- end
70
- alias publish send
71
-
72
- def subscribe(*args, &block)
73
- frames = []
74
- @read_conn.each do |c|
75
- frames << c.subscribe(*args) do |msg|
76
- log.debug("[Received] ID=#{msg[:id]} Body=#{msg.body.inspect} Headers=#{msg.headers.to_hash.inspect}") if log
77
- if @translate_json
78
- msg.body = begin
79
- JSON.parse(msg.body)
80
- rescue JSON::ParserError
81
- msg.body
82
- end
83
- end
84
- reply_args = yield msg
85
- if @auto_reply_to && !msg.headers[:'reply-to'].nil?
86
- if reply_args.is_a?(Array)
87
- send(msg.headers[:'reply-to'], *reply_args)
88
- else
89
- send(msg.headers[:'reply-to'], reply_args)
90
- end
91
- end
92
- end
93
- end
94
- frames
95
- end
96
-
97
- def unsubscribe(frames, headers={})
98
- if !frames.respond_to?(:length) || frames.length != @read_conn.length
99
- raise ArgumentError,
100
- "frames is not an array or its length does not match number of connections"
101
- end
102
- frames.each_with_index.map {|f,i| @read_conn[i].unsubscribe f, headers }
103
- end
104
-
105
- def subscriptions
106
- @read_conn.map {|c| c.active_client.subscriptions }
107
- end
108
-
109
- def log
110
- @logger
111
- end
112
-
113
- WRITE_ONLY_METHODS = [
114
- :abort,
115
- :begin,
116
- :commit,
117
- ]
118
-
119
- READ_ONLY_METHODS = [
120
- :ack,
121
- :nack,
122
- ]
123
-
124
- def method_missing(method, *args, &block)
125
- case method
126
- when *WRITE_ONLY_METHODS
127
- @write_conn.__send__(method, *args, &block)
128
- when *READ_ONLY_METHODS
129
- @read_conn.map {|c| c.__send__(method, *args, &block) }
130
- else
131
- @all_conn.map {|c| c.__send__(method, *args) }
132
- end
133
- end
134
-
135
- private
136
- def configure_connections
137
- klomp_client = self
138
- @all_conn.each do |c|
139
-
140
- if @vhost
141
- c.client_pool.each do |client|
142
- client.host = @vhost
143
- end
144
- end
145
-
146
- if @fib_retry_backoff
147
- c.before_failover_retry do |conn, attempt|
148
- if attempt == 1
149
- conn.prev_retry_delay, conn.retry_delay = 0, 1
150
- else
151
- conn.prev_retry_delay, conn.retry_delay = conn.retry_delay, conn.prev_retry_delay + conn.retry_delay
152
- end
153
- end
154
- end
155
-
156
- c.on_failover_connect_failure do
157
- klomp_client.last_connect_exception = $!
158
- end
159
- end
160
- end
161
- end
162
-
163
- end