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.
Files changed (80) hide show
  1. data/.autotest +2 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +4 -0
  6. data/DeveloperNarrative.md +15 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +221 -0
  9. data/README.md +73 -0
  10. data/Rakefile +6 -0
  11. data/UserNarrative.md +8 -0
  12. data/examples/basic.rb +40 -0
  13. data/examples/events.rb +72 -0
  14. data/lib/onstomp/client.rb +152 -0
  15. data/lib/onstomp/components/frame.rb +108 -0
  16. data/lib/onstomp/components/frame_headers.rb +212 -0
  17. data/lib/onstomp/components/nil_processor.rb +20 -0
  18. data/lib/onstomp/components/scopes/header_scope.rb +25 -0
  19. data/lib/onstomp/components/scopes/receipt_scope.rb +25 -0
  20. data/lib/onstomp/components/scopes/transaction_scope.rb +191 -0
  21. data/lib/onstomp/components/scopes.rb +45 -0
  22. data/lib/onstomp/components/subscription.rb +30 -0
  23. data/lib/onstomp/components/threaded_processor.rb +62 -0
  24. data/lib/onstomp/components/uri.rb +30 -0
  25. data/lib/onstomp/components.rb +13 -0
  26. data/lib/onstomp/connections/base.rb +208 -0
  27. data/lib/onstomp/connections/heartbeating.rb +82 -0
  28. data/lib/onstomp/connections/serializers/stomp_1.rb +166 -0
  29. data/lib/onstomp/connections/serializers/stomp_1_0.rb +41 -0
  30. data/lib/onstomp/connections/serializers/stomp_1_1.rb +134 -0
  31. data/lib/onstomp/connections/serializers.rb +9 -0
  32. data/lib/onstomp/connections/stomp_1.rb +69 -0
  33. data/lib/onstomp/connections/stomp_1_0.rb +28 -0
  34. data/lib/onstomp/connections/stomp_1_1.rb +65 -0
  35. data/lib/onstomp/connections.rb +119 -0
  36. data/lib/onstomp/interfaces/client_configurable.rb +55 -0
  37. data/lib/onstomp/interfaces/client_events.rb +168 -0
  38. data/lib/onstomp/interfaces/connection_events.rb +62 -0
  39. data/lib/onstomp/interfaces/event_manager.rb +69 -0
  40. data/lib/onstomp/interfaces/frame_methods.rb +190 -0
  41. data/lib/onstomp/interfaces/receipt_manager.rb +33 -0
  42. data/lib/onstomp/interfaces/subscription_manager.rb +48 -0
  43. data/lib/onstomp/interfaces/uri_configurable.rb +106 -0
  44. data/lib/onstomp/interfaces.rb +14 -0
  45. data/lib/onstomp/version.rb +13 -0
  46. data/lib/onstomp.rb +147 -0
  47. data/onstomp.gemspec +29 -0
  48. data/spec/onstomp/client_spec.rb +265 -0
  49. data/spec/onstomp/components/frame_headers_spec.rb +163 -0
  50. data/spec/onstomp/components/frame_spec.rb +144 -0
  51. data/spec/onstomp/components/nil_processor_spec.rb +32 -0
  52. data/spec/onstomp/components/scopes/header_scope_spec.rb +27 -0
  53. data/spec/onstomp/components/scopes/receipt_scope_spec.rb +33 -0
  54. data/spec/onstomp/components/scopes/transaction_scope_spec.rb +227 -0
  55. data/spec/onstomp/components/scopes_spec.rb +63 -0
  56. data/spec/onstomp/components/subscription_spec.rb +58 -0
  57. data/spec/onstomp/components/threaded_processor_spec.rb +92 -0
  58. data/spec/onstomp/components/uri_spec.rb +33 -0
  59. data/spec/onstomp/connections/base_spec.rb +349 -0
  60. data/spec/onstomp/connections/heartbeating_spec.rb +132 -0
  61. data/spec/onstomp/connections/serializers/stomp_1_0_spec.rb +50 -0
  62. data/spec/onstomp/connections/serializers/stomp_1_1_spec.rb +99 -0
  63. data/spec/onstomp/connections/serializers/stomp_1_spec.rb +104 -0
  64. data/spec/onstomp/connections/stomp_1_0_spec.rb +54 -0
  65. data/spec/onstomp/connections/stomp_1_1_spec.rb +137 -0
  66. data/spec/onstomp/connections/stomp_1_spec.rb +113 -0
  67. data/spec/onstomp/connections_spec.rb +135 -0
  68. data/spec/onstomp/interfaces/client_events_spec.rb +108 -0
  69. data/spec/onstomp/interfaces/connection_events_spec.rb +55 -0
  70. data/spec/onstomp/interfaces/event_manager_spec.rb +72 -0
  71. data/spec/onstomp/interfaces/frame_methods_spec.rb +109 -0
  72. data/spec/onstomp/interfaces/receipt_manager_spec.rb +53 -0
  73. data/spec/onstomp/interfaces/subscription_manager_spec.rb +64 -0
  74. data/spec/onstomp_spec.rb +15 -0
  75. data/spec/spec_helper.rb +12 -0
  76. data/spec/support/custom_argument_matchers.rb +51 -0
  77. data/spec/support/frame_matchers.rb +88 -0
  78. data/spec/support/shared_frame_method_examples.rb +116 -0
  79. data/yard_extensions.rb +32 -0
  80. 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