onstomp 1.0.0pre1
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.
- data/.autotest +2 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +4 -0
- data/DeveloperNarrative.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.md +221 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/UserNarrative.md +8 -0
- data/examples/basic.rb +40 -0
- data/examples/events.rb +72 -0
- data/lib/onstomp/client.rb +152 -0
- data/lib/onstomp/components/frame.rb +108 -0
- data/lib/onstomp/components/frame_headers.rb +212 -0
- data/lib/onstomp/components/nil_processor.rb +20 -0
- data/lib/onstomp/components/scopes/header_scope.rb +25 -0
- data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
- data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
- data/lib/onstomp/components/scopes.rb +45 -0
- data/lib/onstomp/components/subscription.rb +30 -0
- data/lib/onstomp/components/threaded_processor.rb +62 -0
- data/lib/onstomp/components/uri.rb +30 -0
- data/lib/onstomp/components.rb +13 -0
- data/lib/onstomp/connections/base.rb +208 -0
- data/lib/onstomp/connections/heartbeating.rb +82 -0
- data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
- data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
- data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
- data/lib/onstomp/connections/serializers.rb +9 -0
- data/lib/onstomp/connections/stomp_1.rb +69 -0
- data/lib/onstomp/connections/stomp_1_0.rb +28 -0
- data/lib/onstomp/connections/stomp_1_1.rb +65 -0
- data/lib/onstomp/connections.rb +119 -0
- data/lib/onstomp/interfaces/client_configurable.rb +55 -0
- data/lib/onstomp/interfaces/client_events.rb +168 -0
- data/lib/onstomp/interfaces/connection_events.rb +62 -0
- data/lib/onstomp/interfaces/event_manager.rb +69 -0
- data/lib/onstomp/interfaces/frame_methods.rb +190 -0
- data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
- data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
- data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
- data/lib/onstomp/interfaces.rb +14 -0
- data/lib/onstomp/version.rb +13 -0
- data/lib/onstomp.rb +147 -0
- data/onstomp.gemspec +29 -0
- data/spec/onstomp/client_spec.rb +265 -0
- data/spec/onstomp/components/frame_headers_spec.rb +163 -0
- data/spec/onstomp/components/frame_spec.rb +144 -0
- data/spec/onstomp/components/nil_processor_spec.rb +32 -0
- data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
- data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
- data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
- data/spec/onstomp/components/scopes_spec.rb +63 -0
- data/spec/onstomp/components/subscription_spec.rb +58 -0
- data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
- data/spec/onstomp/components/uri_spec.rb +33 -0
- data/spec/onstomp/connections/base_spec.rb +349 -0
- data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
- data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
- data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
- data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
- data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
- data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
- data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
- data/spec/onstomp/connections_spec.rb +135 -0
- data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
- data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
- data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
- data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
- data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
- data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
- data/spec/onstomp_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/custom_argument_matchers.rb +51 -0
- data/spec/support/frame_matchers.rb +88 -0
- data/spec/support/shared_frame_method_examples.rb +116 -0
- data/yard_extensions.rb +32 -0
- metadata +219 -0
data/onstomp.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "onstomp/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "onstomp"
|
|
7
|
+
s.version = OnStomp::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Ian D. Eccles"]
|
|
10
|
+
s.email = ["ian.eccles@gmail.com"]
|
|
11
|
+
s.homepage = "http://github.com/meadvillerb/onstomp"
|
|
12
|
+
s.summary = %q{Client for message queues implementing the Stomp protocol interface.}
|
|
13
|
+
s.description = %q{Client library for message passing with brokers that support the Stomp protocol.}
|
|
14
|
+
|
|
15
|
+
s.rubyforge_project = "onstomp-core"
|
|
16
|
+
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
s.required_ruby_version = '>= 1.8.7'
|
|
23
|
+
s.has_rdoc = 'yard'
|
|
24
|
+
s.add_development_dependency('rspec', '~> 2.4.0')
|
|
25
|
+
s.add_development_dependency('simplecov', '>= 0.3.0')
|
|
26
|
+
s.add_development_dependency('yard', '>= 0.6.0')
|
|
27
|
+
s.add_development_dependency('rake')
|
|
28
|
+
s.add_development_dependency('bluecloth')
|
|
29
|
+
end
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp
|
|
5
|
+
describe Client do
|
|
6
|
+
let(:client_uri) { "stomp:///" }
|
|
7
|
+
let(:client_options) { Hash.new }
|
|
8
|
+
let(:client) {
|
|
9
|
+
Client.new client_uri, client_options
|
|
10
|
+
}
|
|
11
|
+
let(:processor) {
|
|
12
|
+
mock('processor')
|
|
13
|
+
}
|
|
14
|
+
let(:processor_class) {
|
|
15
|
+
mock('processor class', :new => processor)
|
|
16
|
+
}
|
|
17
|
+
let(:connection) {
|
|
18
|
+
mock('connection')
|
|
19
|
+
}
|
|
20
|
+
let(:frame) {
|
|
21
|
+
mock('frame')
|
|
22
|
+
}
|
|
23
|
+
let(:headers) {
|
|
24
|
+
mock('headers')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let(:frame_method_interface) { client }
|
|
28
|
+
it_should_behave_like "frame method interfaces"
|
|
29
|
+
|
|
30
|
+
describe "configuration" do
|
|
31
|
+
it "should provide some defaults" do
|
|
32
|
+
client.versions.should == ['1.0', '1.1']
|
|
33
|
+
client.host.should == 'localhost'
|
|
34
|
+
client.login.should == ''
|
|
35
|
+
client.passcode.should == ''
|
|
36
|
+
client.heartbeats.should == [0, 0]
|
|
37
|
+
client.processor.should == OnStomp::Components::ThreadedProcessor
|
|
38
|
+
end
|
|
39
|
+
it "should be configurable by options" do
|
|
40
|
+
client_options[:versions] = ['1.1']
|
|
41
|
+
client_options[:heartbeats] = [90, 110]
|
|
42
|
+
client_options[:host] = 'my broker host'
|
|
43
|
+
client_options[:login] = 'my login'
|
|
44
|
+
client_options[:passcode] = 'sup3r s3cr3t'
|
|
45
|
+
client_options[:processor] = processor_class
|
|
46
|
+
client_options[:ssl] = { :cert_path => '/path/to/certs' }
|
|
47
|
+
client.versions.should == ['1.1']
|
|
48
|
+
client.heartbeats.should == [90, 110]
|
|
49
|
+
client.host.should == 'my broker host'
|
|
50
|
+
client.login.should == 'my login'
|
|
51
|
+
client.passcode.should == 'sup3r s3cr3t'
|
|
52
|
+
client.processor.should == processor_class
|
|
53
|
+
client.ssl.should == { :cert_path => '/path/to/certs' }
|
|
54
|
+
end
|
|
55
|
+
it "should be configurable by query" do
|
|
56
|
+
client_uri << '?versions=1.0'
|
|
57
|
+
client_uri << '&heartbeats=80&heartbeats=210'
|
|
58
|
+
client_uri << '&host=query%20host'
|
|
59
|
+
client_uri << '&login=query%20login'
|
|
60
|
+
client_uri << '&passcode=qu3ry%20s3cr3t'
|
|
61
|
+
client_uri << '&processor=OnStomp::Connections'
|
|
62
|
+
client.versions.should == ['1.0']
|
|
63
|
+
client.heartbeats.should == [80, 210]
|
|
64
|
+
client.host.should == 'query host'
|
|
65
|
+
client.login.should == 'query login'
|
|
66
|
+
client.passcode.should == 'qu3ry s3cr3t'
|
|
67
|
+
client.processor.should == OnStomp::Connections
|
|
68
|
+
end
|
|
69
|
+
it "should be configurable through parts of the URI" do
|
|
70
|
+
client_uri.replace("stomp://uzer:s3cr3t@host.domain.tld")
|
|
71
|
+
client.host.should == 'host.domain.tld'
|
|
72
|
+
client.login.should == 'uzer'
|
|
73
|
+
client.passcode.should == 's3cr3t'
|
|
74
|
+
end
|
|
75
|
+
it "should prefer option hash over query over uri attributes" do
|
|
76
|
+
client_uri.replace("stomp://uzer:s3cr3t@host.domain.tld?host=query%20host&passcode=qu3ry%20s3cr3t&login=query%20login")
|
|
77
|
+
client_options[:login] = 'my login'
|
|
78
|
+
client.host.should == 'query host'
|
|
79
|
+
client.login.should == 'my login'
|
|
80
|
+
client.passcode.should == 'qu3ry s3cr3t'
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe ".initialize" do
|
|
85
|
+
it "should initialize from a string uri" do
|
|
86
|
+
uri = Client.new('stomp://host.domain.tld:10101').uri
|
|
87
|
+
uri.should be_a_kind_of(::URI)
|
|
88
|
+
uri.host.should == 'host.domain.tld'
|
|
89
|
+
uri.port.should == 10101
|
|
90
|
+
uri.scheme.should == 'stomp'
|
|
91
|
+
end
|
|
92
|
+
it "should initialize with a URI uri" do
|
|
93
|
+
uri = ::URI.parse('stomp://host.domain.tld:10101')
|
|
94
|
+
Client.new(uri).uri.should == uri
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe ".connect" do
|
|
99
|
+
let(:pending_events) {
|
|
100
|
+
mock('pending events')
|
|
101
|
+
}
|
|
102
|
+
before(:each) do
|
|
103
|
+
client.stub(:processor => processor_class)
|
|
104
|
+
end
|
|
105
|
+
it "should create a connection and start the processor" do
|
|
106
|
+
OnStomp::Connections.should_receive(:connect).with(client, headers,
|
|
107
|
+
{ :'accept-version' => '1.1', :host => 'my host',
|
|
108
|
+
:'heart-beat' => '30,110', :login => 'my login',
|
|
109
|
+
:passcode => 's3cr3t' }, pending_events).and_return(connection)
|
|
110
|
+
processor.should_receive(:start)
|
|
111
|
+
client.stub(:pending_connection_events => pending_events)
|
|
112
|
+
client.versions = '1.1'
|
|
113
|
+
client.host = 'my host'
|
|
114
|
+
client.login = 'my login'
|
|
115
|
+
client.passcode = 's3cr3t'
|
|
116
|
+
client.heartbeats = [30,110]
|
|
117
|
+
client.connect(headers)
|
|
118
|
+
client.connection.should == connection
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe ".disconnect_with_flush" do
|
|
123
|
+
before(:each) do
|
|
124
|
+
client.stub(:processor => processor_class)
|
|
125
|
+
end
|
|
126
|
+
it "should call disconnect_without_flush and join the processor" do
|
|
127
|
+
processor.should_receive(:join)
|
|
128
|
+
client.should_receive(:disconnect_without_flush).with(headers).and_return(frame)
|
|
129
|
+
client.disconnect_with_flush(headers).should == frame
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe ".connected?" do
|
|
134
|
+
it "should not be connected if there is no connect" do
|
|
135
|
+
client.should_not be_connected
|
|
136
|
+
end
|
|
137
|
+
it "should not be connected if the connection is not connected" do
|
|
138
|
+
client.stub(:connection => connection)
|
|
139
|
+
connection.stub(:connected? => false)
|
|
140
|
+
client.should_not be_connected
|
|
141
|
+
end
|
|
142
|
+
it "should be connected if the connection exists and is connected" do
|
|
143
|
+
client.stub(:connection => connection)
|
|
144
|
+
connection.stub(:connected? => true)
|
|
145
|
+
client.should be_connected
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
describe ".close" do
|
|
150
|
+
before(:each) do
|
|
151
|
+
client.stub(:connection => connection)
|
|
152
|
+
end
|
|
153
|
+
it "should close the connection and clear all receipts and subscriptions" do
|
|
154
|
+
client.should_receive(:clear_receipts)
|
|
155
|
+
client.should_receive(:clear_subscriptions)
|
|
156
|
+
connection.should_receive(:close)
|
|
157
|
+
client.__send__ :close
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
describe ".close!" do
|
|
162
|
+
before(:each) do
|
|
163
|
+
client.stub(:processor => processor_class)
|
|
164
|
+
end
|
|
165
|
+
it "should call close and stop the processor" do
|
|
166
|
+
client.should_receive(:close)
|
|
167
|
+
processor.should_receive(:stop)
|
|
168
|
+
client.close!
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
describe ".transmit" do
|
|
173
|
+
let(:callbacks) {
|
|
174
|
+
{ :subscribe => 'subscribe', :receipt => 'receipt' }
|
|
175
|
+
}
|
|
176
|
+
before(:each) do
|
|
177
|
+
client.stub(:connection => connection)
|
|
178
|
+
end
|
|
179
|
+
it "should register any callbacks, trigger events, write and return the frame" do
|
|
180
|
+
client.should_receive(:trigger_before_transmitting).with(frame)
|
|
181
|
+
client.should_receive(:add_subscription).with(frame, 'subscribe')
|
|
182
|
+
client.should_receive(:add_receipt).with(frame, 'receipt')
|
|
183
|
+
connection.should_receive(:write_frame_nonblock).with(frame)
|
|
184
|
+
client.transmit(frame, callbacks).should == frame
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
describe ".dispatch_transmitted" do
|
|
189
|
+
it "should trigger the after transmitting event" do
|
|
190
|
+
client.should_receive(:trigger_after_transmitting).with(frame)
|
|
191
|
+
client.dispatch_transmitted(frame)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe ".dispatch_received" do
|
|
196
|
+
it "should trigger the before and after receiving events" do
|
|
197
|
+
client.should_receive(:trigger_before_receiving).with(frame)
|
|
198
|
+
client.should_receive(:trigger_after_receiving).with(frame)
|
|
199
|
+
client.dispatch_received(frame)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
describe "on_disconnect" do
|
|
204
|
+
it "should call close if there is no receipt header on the frame" do
|
|
205
|
+
client.should_receive(:close)
|
|
206
|
+
client.trigger_after_receiving OnStomp::Components::Frame.new('DISCONNECT')
|
|
207
|
+
end
|
|
208
|
+
it "should not call close if a receipt header is present" do
|
|
209
|
+
client.should_not_receive(:close)
|
|
210
|
+
client.trigger_after_receiving OnStomp::Components::Frame.new('DISCONNECT', :receipt => 'r-1234')
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
describe ".versions" do
|
|
215
|
+
it "should select only those versions that are supported" do
|
|
216
|
+
client.versions = ['1.9', '1.1', '2.5', '1.0', '82']
|
|
217
|
+
client.versions.should == ['1.0', '1.1']
|
|
218
|
+
end
|
|
219
|
+
it "should raise an unsupported protocol version error if no versions are viable" do
|
|
220
|
+
lambda {
|
|
221
|
+
client.versions = ['1.3', '8.x']
|
|
222
|
+
}.should raise_error(OnStomp::UnsupportedProtocolVersionError)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
describe ".heartbeats" do
|
|
227
|
+
it "should convert values to non-negative integers" do
|
|
228
|
+
client.heartbeats = ['-9100', '31313']
|
|
229
|
+
client.heartbeats.should == [0, 31313]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
describe ".processor" do
|
|
234
|
+
it "should convert the processor to a class/module" do
|
|
235
|
+
client.processor = '::Module'
|
|
236
|
+
client.processor.should == Module
|
|
237
|
+
end
|
|
238
|
+
it "should use the NilProcessor if set to nil" do
|
|
239
|
+
client.processor = nil
|
|
240
|
+
client.processor.should == OnStomp::Components::NilProcessor
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe ".login" do
|
|
245
|
+
it "should convert to a string" do
|
|
246
|
+
client.login = 314.3
|
|
247
|
+
client.login.should == '314.3'
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
describe ".passcode" do
|
|
252
|
+
it "should convert to a string" do
|
|
253
|
+
client.passcode = 42
|
|
254
|
+
client.passcode.should == '42'
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
describe ".host" do
|
|
259
|
+
it "should convert to a string" do
|
|
260
|
+
client.host = :hostname
|
|
261
|
+
client.host.should == 'hostname'
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Components
|
|
5
|
+
describe FrameHeaders do
|
|
6
|
+
let(:headers) {
|
|
7
|
+
FrameHeaders.new
|
|
8
|
+
}
|
|
9
|
+
let(:enumerator_module) {
|
|
10
|
+
RUBY_VERSION >= '1.9' ? Enumerator : Enumerable::Enumerator
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe ".initialize" do
|
|
14
|
+
it "should create an empty set of headers" do
|
|
15
|
+
FrameHeaders.new.names.should == []
|
|
16
|
+
end
|
|
17
|
+
it "should create headers initialized by a hash" do
|
|
18
|
+
head = FrameHeaders.new({:one => 'one', 'two' => 2})
|
|
19
|
+
head[:one].should == 'one'
|
|
20
|
+
head[:two].should == '2'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe ".merge!" do
|
|
25
|
+
it "should overwrite existing headers and create new ones" do
|
|
26
|
+
headers[:header1] = 'some value'
|
|
27
|
+
headers['header2'] = 43
|
|
28
|
+
headers.merge!( :header2 => 'new value', 'header3' => 'new header')
|
|
29
|
+
headers.should have_headers(:header1 => 'some value', :header2 => 'new value', :header3 => 'new header')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe ".reverse_merge!" do
|
|
34
|
+
it "should not overwrite existing headers but create new ones" do
|
|
35
|
+
headers[:header1] = 'some value'
|
|
36
|
+
headers['header2'] = 43
|
|
37
|
+
headers.reverse_merge!( :header2 => 'new value', 'header3' => 'new header')
|
|
38
|
+
headers.should have_headers(:header1 => 'some value', :header2 => '43', :header3 => 'new header')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe ".set? / .present?" do
|
|
43
|
+
it "should be false if the header is not set" do
|
|
44
|
+
headers.set?(:any_old_thing).should be_false
|
|
45
|
+
headers.present?(:any_old_thing).should be_false
|
|
46
|
+
end
|
|
47
|
+
it "should be set? but not present? when set to nil" do
|
|
48
|
+
headers[:nil_valued] = nil
|
|
49
|
+
headers.set?(:nil_valued).should be_true
|
|
50
|
+
headers.present?(:nil_valued).should be_false
|
|
51
|
+
end
|
|
52
|
+
it "should be set? but not present? when set to empty string" do
|
|
53
|
+
headers[:empty_valued] = ''
|
|
54
|
+
headers.set?(:empty_valued).should be_true
|
|
55
|
+
headers.present?(:empty_valued).should be_false
|
|
56
|
+
end
|
|
57
|
+
it "should be set? and present? when set to a non-empty value" do
|
|
58
|
+
headers[:nonempty_valued] = 31011
|
|
59
|
+
headers.set?(:nonempty_valued).should be_true
|
|
60
|
+
headers.present?(:nonempty_valued).should be_true
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe ".append / .all_values" do
|
|
65
|
+
it "should create a new header name if one did not exist" do
|
|
66
|
+
headers.set?(:appended).should be_false
|
|
67
|
+
headers.append('appended', 'value 1')
|
|
68
|
+
headers.set?(:appended).should be_true
|
|
69
|
+
headers.all_values(:appended).should == ['value 1']
|
|
70
|
+
end
|
|
71
|
+
it "should append to existing values" do
|
|
72
|
+
headers[:appended1] = 'me'
|
|
73
|
+
headers.append('appended1', 'me too!')
|
|
74
|
+
headers.append('appended2', 'me also?')
|
|
75
|
+
headers.append(:appended2, 'why not')
|
|
76
|
+
headers.all_values(:appended1).should == ['me', 'me too!']
|
|
77
|
+
headers.all_values('appended2').should == ['me also?', 'why not']
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe ".delete" do
|
|
82
|
+
it "should return nil if the header has not been set" do
|
|
83
|
+
headers.delete(:whatever).should be_nil
|
|
84
|
+
end
|
|
85
|
+
it "should return all values as an array and remove the set header" do
|
|
86
|
+
headers['todelete'] = 5
|
|
87
|
+
headers.append(:todelete, 510)
|
|
88
|
+
headers.delete(:todelete).should == ['5', '510']
|
|
89
|
+
headers.set?(:todelete).should be_false
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "hash-like access .[] and .[]=" do
|
|
94
|
+
it "should be accessible via strings or symbols" do
|
|
95
|
+
headers['indifferent'] = 'sweet'
|
|
96
|
+
headers[:indifferent].should == 'sweet'
|
|
97
|
+
headers['indifferent'].should == 'sweet'
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should return nil if the header isn't set" do
|
|
101
|
+
headers[:what_of_it?].should be_nil
|
|
102
|
+
headers['what_of_it?'].should be_nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should return the principle (first assigned) value" do
|
|
106
|
+
headers.append('appended', 'first value')
|
|
107
|
+
headers.append(:'appended', 'second value')
|
|
108
|
+
headers.append('appended', 'last value')
|
|
109
|
+
headers[:appended].should == 'first value'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should convert the assigned value to a string" do
|
|
113
|
+
headers['conversion'] = 310831
|
|
114
|
+
headers[:conversion].should == '310831'
|
|
115
|
+
headers[:convert_nil] = nil
|
|
116
|
+
headers['convert_nil'].should == ''
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe ".to_hash" do
|
|
121
|
+
it "should convert itself to a hash of header names and their principle values" do
|
|
122
|
+
headers[:lonely_me] = true
|
|
123
|
+
headers.append('appended', 'first value')
|
|
124
|
+
headers.append(:'appended', 'second value')
|
|
125
|
+
headers.append('appended', 'last value')
|
|
126
|
+
headers.to_hash.should == { :lonely_me => 'true', :appended => 'first value' }
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe ".each" do
|
|
131
|
+
it "should be a kind of enumerable" do
|
|
132
|
+
headers.should be_a_kind_of(Enumerable)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should yield an enumerator if called without a block" do
|
|
136
|
+
headers.each.should be_a_kind_of(enumerator_module)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should yield header names and values as pairs of strings" do
|
|
140
|
+
yielded = []
|
|
141
|
+
headers[:named1] = 'value 1'
|
|
142
|
+
headers[:named2] = 'value 2'
|
|
143
|
+
headers.each { |pair| yielded << pair }
|
|
144
|
+
yielded.should == [ ['named1', 'value 1'], ['named2', 'value 2'] ]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "should yield repeated headers within the pairs" do
|
|
148
|
+
yielded = []
|
|
149
|
+
headers[:named1] = 'value 1'
|
|
150
|
+
headers[:named2] = 'value 2'
|
|
151
|
+
headers[:named3] = 'value 3'
|
|
152
|
+
headers.append('named2', 'yet another')
|
|
153
|
+
headers.append('named1', 'why not one more?')
|
|
154
|
+
headers.each { |k, v| yielded << [k, v] }
|
|
155
|
+
yielded.should == [
|
|
156
|
+
['named1', 'value 1'], ['named1', 'why not one more?'],
|
|
157
|
+
['named2', 'value 2'], ['named2', 'yet another'],
|
|
158
|
+
['named3', 'value 3']
|
|
159
|
+
]
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Components
|
|
5
|
+
describe Frame do
|
|
6
|
+
let(:frame) {
|
|
7
|
+
Frame.new
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe ".initialize" do
|
|
11
|
+
it "should create a frame with no command" do
|
|
12
|
+
Frame.new.should have_command(nil)
|
|
13
|
+
end
|
|
14
|
+
it "should create a frame with the supplied command" do
|
|
15
|
+
Frame.new('MANIAC').should have_command('MANIAC')
|
|
16
|
+
end
|
|
17
|
+
it "should create a rame with the supplied command and headers" do
|
|
18
|
+
Frame.new('MANIAC',
|
|
19
|
+
{:mansion => true, 'edit' => 'meteor'}
|
|
20
|
+
).should be_an_onstomp_frame('MANIAC',
|
|
21
|
+
{:mansion => 'true', :edit => 'meteor'}, nil)
|
|
22
|
+
end
|
|
23
|
+
it "should create a rame with the supplied command, headers and body" do
|
|
24
|
+
Frame.new('MANIAC',
|
|
25
|
+
{:mansion => true, 'edit' => 'meteor'}, 'Bitcrusher'
|
|
26
|
+
).should be_an_onstomp_frame('MANIAC',
|
|
27
|
+
{:mansion => 'true', :edit => 'meteor'}, 'Bitcrusher')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "hash-like access .[] and .[]=" do
|
|
32
|
+
it "should assign a header in a hash-like style" do
|
|
33
|
+
frame[:maniac] = 'mansion'
|
|
34
|
+
frame.headers[:maniac].should == 'mansion'
|
|
35
|
+
end
|
|
36
|
+
it "should retrieve a header in a hash-like style" do
|
|
37
|
+
frame.headers[:maniac] = 'mansion'
|
|
38
|
+
frame[:maniac].should == 'mansion'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe ".content_length" do
|
|
43
|
+
it "should convert a content-length header to an integer else return nil" do
|
|
44
|
+
frame[:'content-length'] = '510'
|
|
45
|
+
frame.content_length.should == 510
|
|
46
|
+
frame[:'content-length'] = nil
|
|
47
|
+
frame.content_length.should be_nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe ".content_type" do
|
|
52
|
+
it "should return an array of nils if no content-type header is set" do
|
|
53
|
+
frame.content_type.should == [nil, nil, nil]
|
|
54
|
+
end
|
|
55
|
+
it "should return an array of nils if the content-type header is not in the standard form" do
|
|
56
|
+
frame[:'content-type'] = ";charset=UTF-8 text/plain"
|
|
57
|
+
frame.content_type.should == [nil, nil, nil]
|
|
58
|
+
end
|
|
59
|
+
it "should parse a type and subtype" do
|
|
60
|
+
frame[:'content-type'] = 'text/plain; param1=value; param2=value;param3=4'
|
|
61
|
+
frame.content_type.should == ['text', 'plain', nil]
|
|
62
|
+
end
|
|
63
|
+
it "should parse a type, subtype and charset" do
|
|
64
|
+
frame[:'content-type'] = 'application-foo/many.pants&shirts; param1=value;charset=UTF-8; param2=value;param3=4'
|
|
65
|
+
frame.content_type.should == ['application-foo', 'many.pants&shirts', 'UTF-8']
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe ".header?" do
|
|
70
|
+
it "should be false if the header is not set" do
|
|
71
|
+
frame.header?(:blather).should be_false
|
|
72
|
+
end
|
|
73
|
+
it "should be false if the header is nil" do
|
|
74
|
+
frame[:blather] = nil
|
|
75
|
+
frame.header?(:blather).should be_false
|
|
76
|
+
end
|
|
77
|
+
it "should be false if the header is empty" do
|
|
78
|
+
frame[:blather] = ''
|
|
79
|
+
frame.header?(:blather).should be_false
|
|
80
|
+
end
|
|
81
|
+
it "should be true if the header is set and non-empty" do
|
|
82
|
+
frame[:blather] = ' '
|
|
83
|
+
frame.header?(:blather).should be_true
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe ".all_headers?" do
|
|
88
|
+
it "should be false if any header is not set" do
|
|
89
|
+
frame[:blather1] = 'test-set'
|
|
90
|
+
frame[:blather2] = 'test-set'
|
|
91
|
+
frame.all_headers?(:blather1, :blather2, :blather3).should be_false
|
|
92
|
+
end
|
|
93
|
+
it "should be false if any header is nil" do
|
|
94
|
+
frame[:blather1] = 'test-set'
|
|
95
|
+
frame[:blather2] = 'test-set'
|
|
96
|
+
frame[:blather3] = nil
|
|
97
|
+
frame.headers?(:blather1, :blather2, :blather3).should be_false
|
|
98
|
+
end
|
|
99
|
+
it "should be false if any header is empty" do
|
|
100
|
+
frame[:blather1] = 'test-set'
|
|
101
|
+
frame[:blather2] = ''
|
|
102
|
+
frame[:blather3] = 'test-set'
|
|
103
|
+
frame.headers?(:blather1, :blather2, :blather3).should be_false
|
|
104
|
+
end
|
|
105
|
+
it "should be true if all headers are set and non-empty" do
|
|
106
|
+
frame[:blather1] = 'three'
|
|
107
|
+
frame[:blather2] = ' '
|
|
108
|
+
frame[:blather3] = 'test-set'
|
|
109
|
+
frame.all_headers?(:blather1, :blather2, :blather3).should be_true
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe ".heart_beat" do
|
|
114
|
+
it "should return [0,0] if no heart-beat header is set" do
|
|
115
|
+
frame.heart_beat.should == [0,0]
|
|
116
|
+
end
|
|
117
|
+
it "should parse a heart-beat header and return an array of integers" do
|
|
118
|
+
frame[:'heart-beat'] = '310,91'
|
|
119
|
+
frame.heart_beat.should == [310, 91]
|
|
120
|
+
end
|
|
121
|
+
it "should return only non-negative values" do
|
|
122
|
+
frame[:'heart-beat'] = '-310,-91'
|
|
123
|
+
frame.heart_beat.should == [0, 0]
|
|
124
|
+
frame[:'heart-beat'] = '-310,91'
|
|
125
|
+
frame.heart_beat.should == [0, 91]
|
|
126
|
+
frame[:'heart-beat'] = '310,-91'
|
|
127
|
+
frame.heart_beat.should == [310, 0]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe ".force_content_length" do
|
|
132
|
+
it "should not assign a content-length header if there is no body" do
|
|
133
|
+
frame.force_content_length
|
|
134
|
+
frame.headers.set?(:'content-length').should be_false
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should set a content-length header to the byte-size of the body" do
|
|
138
|
+
frame.body = 'this is a tëst'
|
|
139
|
+
frame.force_content_length
|
|
140
|
+
frame[:'content-length'].should == '15'
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Components
|
|
5
|
+
describe NilProcessor do
|
|
6
|
+
let(:client) { mock('client') }
|
|
7
|
+
let(:processor) { NilProcessor.new client }
|
|
8
|
+
|
|
9
|
+
describe ".start" do
|
|
10
|
+
it "should return itself" do
|
|
11
|
+
processor.start.should == processor
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
describe ".stop" do
|
|
15
|
+
it "should return itself" do
|
|
16
|
+
processor.stop.should == processor
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
describe ".join" do
|
|
20
|
+
it "should return itself" do
|
|
21
|
+
processor.join.should == processor
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
describe ".running?" do
|
|
25
|
+
it "should never be running" do
|
|
26
|
+
processor.running?.should be_false
|
|
27
|
+
processor.start
|
|
28
|
+
processor.running?.should be_false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
module OnStomp::Components::Scopes
|
|
5
|
+
describe HeaderScope do
|
|
6
|
+
let(:client) {
|
|
7
|
+
OnStomp::Client.new("stomp:///")
|
|
8
|
+
}
|
|
9
|
+
let(:headers) {
|
|
10
|
+
{ :header1 => 'value 1', 'header2' => 'value 2'}
|
|
11
|
+
}
|
|
12
|
+
let(:scope) {
|
|
13
|
+
HeaderScope.new headers, client
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let(:frame_method_interface) { scope }
|
|
17
|
+
it_should_behave_like "frame method interfaces"
|
|
18
|
+
|
|
19
|
+
describe ".transmit" do
|
|
20
|
+
it "should add all its headers to a frame, unless that header name is set" do
|
|
21
|
+
client.stub(:transmit).and_return { |f,_| f }
|
|
22
|
+
frame = scope.transmit OnStomp::Components::Frame.new('COMMAND', {:header2 => 'my value'})
|
|
23
|
+
frame.should have_headers(:header1 => 'value 1', :header2 => 'my value')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|