stomper 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +21 -0
- data/CHANGELOG +3 -0
- data/LICENSE +202 -0
- data/README.rdoc +68 -0
- data/lib/stomper.rb +15 -0
- data/lib/stomper/client.rb +300 -0
- data/lib/stomper/connection.rb +176 -0
- data/lib/stomper/frames.rb +24 -0
- data/lib/stomper/frames/abort.rb +14 -0
- data/lib/stomper/frames/ack.rb +29 -0
- data/lib/stomper/frames/begin.rb +14 -0
- data/lib/stomper/frames/client_frame.rb +86 -0
- data/lib/stomper/frames/commit.rb +14 -0
- data/lib/stomper/frames/connect.rb +15 -0
- data/lib/stomper/frames/connected.rb +27 -0
- data/lib/stomper/frames/disconnect.rb +13 -0
- data/lib/stomper/frames/error.rb +26 -0
- data/lib/stomper/frames/headers.rb +68 -0
- data/lib/stomper/frames/message.rb +44 -0
- data/lib/stomper/frames/receipt.rb +24 -0
- data/lib/stomper/frames/send.rb +14 -0
- data/lib/stomper/frames/server_frame.rb +48 -0
- data/lib/stomper/frames/subscribe.rb +47 -0
- data/lib/stomper/frames/unsubscribe.rb +23 -0
- data/lib/stomper/subscription.rb +128 -0
- data/lib/stomper/subscriptions.rb +95 -0
- data/lib/stomper/transaction.rb +180 -0
- data/spec/client_spec.rb +167 -0
- data/spec/connection_spec.rb +12 -0
- data/spec/frames/client_frame_spec.rb +142 -0
- data/spec/frames/headers_spec.rb +54 -0
- data/spec/frames/server_frame_spec.rb +86 -0
- data/spec/shared_connection_examples.rb +84 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/subscription_spec.rb +157 -0
- data/spec/subscriptions_spec.rb +148 -0
- data/spec/transaction_spec.rb +139 -0
- metadata +121 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
module Stomper
|
2
|
+
# An exception raised whenever a Transaction object has been aborted
|
3
|
+
# due to an unhandled exception generated by its supplied block, or when
|
4
|
+
# the block explicitly aborts the transaction.
|
5
|
+
#
|
6
|
+
# See also: Stomper::Transaction#perform
|
7
|
+
class TransactionAborted < RuntimeError; end
|
8
|
+
|
9
|
+
# An encapsulation of a stomp transaction. Manually managing transactions
|
10
|
+
# is possible through the use of Stomper::Client#begin, Stomper::Client#commit,
|
11
|
+
# and Stomper::Client#abort.
|
12
|
+
#
|
13
|
+
# === Example Usage
|
14
|
+
#
|
15
|
+
# When the transaction is passed to the block:
|
16
|
+
#
|
17
|
+
# client.transaction do |t|
|
18
|
+
# t.send("/queue/target", "doing some work")
|
19
|
+
#
|
20
|
+
# # do something that might raise an exception, indicating that any
|
21
|
+
# # messages and acknowledgements we have sent should be "undone"
|
22
|
+
#
|
23
|
+
# t.send("/queue/target", "completed work")
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# When the block is evaluated within the transaction:
|
27
|
+
#
|
28
|
+
# client.transaction do
|
29
|
+
# send("/queue/target", "doing some work")
|
30
|
+
#
|
31
|
+
# # ...
|
32
|
+
#
|
33
|
+
# send("/queue/target", "completed work")
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Nesting transactions:
|
37
|
+
#
|
38
|
+
# client.transaction do |t|
|
39
|
+
# t.transaction do |nt|
|
40
|
+
# nt.send("/queue/target", ...)
|
41
|
+
#
|
42
|
+
# nt.transaction do |nnt|
|
43
|
+
# nnt.send("/queue/target", ...)
|
44
|
+
#
|
45
|
+
# # do something with potentially exceptional results
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# nt.send("/queue/target", ...)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# t.send("/queue/target", ...)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# See also: Stomper::Client#transaction
|
55
|
+
#
|
56
|
+
class Transaction
|
57
|
+
# The id of this transaction, used to reference the transaction with the stomp broker.
|
58
|
+
attr_reader :id
|
59
|
+
|
60
|
+
# Creates a new Transaction instance. The +client+ parameter
|
61
|
+
# is an instance of Stomper::Client and is required so that the Transaction
|
62
|
+
# instance has somewhere to forward +begin+, +ack+ and +abort+ methods
|
63
|
+
# to. If the +trans_id+ parameter is not specified, an id is automatically
|
64
|
+
# generated of the form "tx-{Time.now.to_f}". This name can be accessed
|
65
|
+
# through the +id+ attribute and is used in naming the transaction to
|
66
|
+
# the stomp broker. If +block+ is given, the Transaction instance immediately
|
67
|
+
# calls its perform method with the supplied +block+.
|
68
|
+
def initialize(client, trans_id=nil, &block)
|
69
|
+
@client = client
|
70
|
+
@id = trans_id || "tx-#{Time.now.to_f}"
|
71
|
+
@committed = false
|
72
|
+
@aborted = false
|
73
|
+
perform(&block) if block_given?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Invokes the given +block+. If the +block+ executes normally, the
|
77
|
+
# transaction is committed, otherwise it is aborted.
|
78
|
+
# If +block+ accepts a parameter, this method yields itself to the block,
|
79
|
+
# otherwise, +block+ is evaluated within the context of this instance through
|
80
|
+
# +instance_eval+.
|
81
|
+
#
|
82
|
+
# If a call to +abort+ is issued within the block, the transaction is aborted
|
83
|
+
# as demanded, and no attempt is made to commit it; however, no code after the
|
84
|
+
# call to +abort+ will be evaluated, as +abort+ raises a TransactionAborted
|
85
|
+
# exception.
|
86
|
+
#
|
87
|
+
# If a call to +commit+ is issued within the block, the transaction is committed
|
88
|
+
# as demanded, and no attempt is made to commit it after +block+ has finished
|
89
|
+
# executing. As +commit+ does not raise an excpetion, all code after the call
|
90
|
+
# to commit will be evaluated.
|
91
|
+
#
|
92
|
+
# If you are using Transaction objects directly, and not relying on their
|
93
|
+
# generation through Stomper::Client#transaction, be warned that this method
|
94
|
+
# will raise a TransactionAborted exception if the +block+ evaluation fails.
|
95
|
+
# This behavior allows for nesting transactions and ensuring that if a nested
|
96
|
+
# transaction fails, so do all of its ancestors.
|
97
|
+
def perform(&block) #:yields: transaction
|
98
|
+
begin
|
99
|
+
@client.begin(@id)
|
100
|
+
if block.arity == 1
|
101
|
+
yield self
|
102
|
+
else
|
103
|
+
instance_eval(&block)
|
104
|
+
end
|
105
|
+
commit
|
106
|
+
rescue => err
|
107
|
+
_abort
|
108
|
+
raise TransactionAborted, "aborted transaction '#{@id}' originator: #{err.to_s}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns true if the Transaction object has already been committed, false
|
113
|
+
# otherwise.
|
114
|
+
def committed?
|
115
|
+
@committed
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns true if the Transaction object has already been aborted, false
|
119
|
+
# otherwise.
|
120
|
+
def aborted?
|
121
|
+
@aborted
|
122
|
+
end
|
123
|
+
|
124
|
+
# Similar to Stomper::Client#transaction, this method creates a new
|
125
|
+
# Transaction object, nested inside of this one. To prevent name
|
126
|
+
# collisions, this method automatically generates a transaction id,
|
127
|
+
# if one is not specified, of the form "#{parent_transaction_id}-#{Time.now.to_f}.
|
128
|
+
def transaction(transaction_id=nil,&block)
|
129
|
+
# To get a transaction name guaranteed to not collide with this one
|
130
|
+
# we will supply an explicit id to the constructor unless an id was
|
131
|
+
# provided
|
132
|
+
transaction_id ||= "#{@id}-#{Time.now.to_f}"
|
133
|
+
self.class.new(@client, transaction_id, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Wraps the Stomper::Client#send method, injecting a "transaction" header
|
137
|
+
# into the +headers+ hash, thus informing the stomp broker that the message
|
138
|
+
# generated here is part of this transaction.
|
139
|
+
def send(destination, body, headers={})
|
140
|
+
headers['transaction'] = @id
|
141
|
+
@client.send(destination, body, headers)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Wraps the Stomper::Client#ack method, injecting a "transaction" header
|
145
|
+
# into the +headers+ hash, thus informing the stomp broker that the message
|
146
|
+
# acknowledgement is part of this transaction.
|
147
|
+
def ack(message_or_id, headers={})
|
148
|
+
headers['transaction'] = @id
|
149
|
+
@client.ack(message_or_id, headers)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Aborts this transaction if it has not already been committed or aborted.
|
153
|
+
# Note that it does so by raising a TransactionAborted exception, allowing
|
154
|
+
# the +abort+ call to force any ancestral transactions to also fail.
|
155
|
+
#
|
156
|
+
# See also: commit, committed?, aborted?
|
157
|
+
def abort
|
158
|
+
raise TransactionAborted, "transaction '#{@id}' aborted explicitly" if _abort
|
159
|
+
end
|
160
|
+
|
161
|
+
# Commits this transaction unless it has already been committed or aborted.
|
162
|
+
#
|
163
|
+
# See also: abort, committed?, aborted?
|
164
|
+
def commit
|
165
|
+
# Guard against sending multiple commit messages to the server for a
|
166
|
+
# single transaction.
|
167
|
+
@client.commit(@id) unless committed? || aborted?
|
168
|
+
@committed = true
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
def _abort
|
173
|
+
# Guard against sending multiple abort messages to the server for a
|
174
|
+
# single transaction.
|
175
|
+
return false if committed? || aborted?
|
176
|
+
@client.abort(@id)
|
177
|
+
@aborted = true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper
|
4
|
+
describe Client do
|
5
|
+
before(:each) do
|
6
|
+
# For the client, we want to mock the underlying connection
|
7
|
+
@mock_connection = mock("connection")
|
8
|
+
@mock_connection.should_receive(:disconnect).with(no_args()).at_most(:once).and_return(nil)
|
9
|
+
Stomper::Connection.stub!(:new).and_return(@mock_connection)
|
10
|
+
@client = Client.new("stomp:///")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "expected interface" do
|
14
|
+
it "should provide a send method" do
|
15
|
+
@client.should respond_to(:send)
|
16
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Send)).twice.and_return(nil)
|
17
|
+
@client.send("/queue/to", "message body", {:additional => 'header'})
|
18
|
+
@client.send("/queue/to", "message body")
|
19
|
+
end
|
20
|
+
it "should provide a subscribe method" do
|
21
|
+
@client.should respond_to(:subscribe)
|
22
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
23
|
+
@client.subscribe("/queue/to", {:additional => 'header'})
|
24
|
+
@client.subscribe("/queue/to")
|
25
|
+
end
|
26
|
+
it "should provide an unsubscribe method" do
|
27
|
+
@client.should respond_to(:unsubscribe)
|
28
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Subscribe)).twice.and_return(nil)
|
29
|
+
@client.subscribe("/queue/to", {:id => 'subscription-id'})
|
30
|
+
@client.subscribe("/queue/to")
|
31
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Unsubscribe)).twice.and_return(nil)
|
32
|
+
@client.unsubscribe("/queue/to", 'subscription-id')
|
33
|
+
@client.unsubscribe("/queue/to")
|
34
|
+
end
|
35
|
+
it "should provide an ack method" do
|
36
|
+
@client.should respond_to(:ack)
|
37
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Ack)).exactly(3).times.and_return(nil)
|
38
|
+
@client.ack("message-id", {:additional => "header"})
|
39
|
+
@client.ack("message-id")
|
40
|
+
@client.ack(Stomper::Frames::Message.new({:'message-id' => 'msg-001'}, "body"))
|
41
|
+
end
|
42
|
+
it "should provide a begin method" do
|
43
|
+
@client.should respond_to(:begin)
|
44
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Begin)).once.and_return(nil)
|
45
|
+
@client.begin("tx-001")
|
46
|
+
end
|
47
|
+
it "should proivde an abort method" do
|
48
|
+
@client.should respond_to(:abort)
|
49
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Abort)).once.and_return(nil)
|
50
|
+
@client.abort("tx-001")
|
51
|
+
end
|
52
|
+
it "should provide a commit method" do
|
53
|
+
@client.should respond_to(:commit)
|
54
|
+
@mock_connection.should_receive(:transmit).with(an_instance_of(Stomper::Frames::Commit)).once.and_return(nil)
|
55
|
+
@client.commit("tx-001")
|
56
|
+
end
|
57
|
+
it "should provide a recieve method" do
|
58
|
+
@client.should respond_to(:receive)
|
59
|
+
end
|
60
|
+
it "should provide a disconnect method" do
|
61
|
+
@client.should respond_to(:disconnect)
|
62
|
+
end
|
63
|
+
it "should provide a close method" do
|
64
|
+
@client.should respond_to(:close)
|
65
|
+
end
|
66
|
+
it "should provide a connectivity test" do
|
67
|
+
@client.should respond_to(:connected?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "threaded receiver" do
|
72
|
+
it "should respond to start and stop" do
|
73
|
+
@client.should respond_to(:start)
|
74
|
+
@client.should respond_to(:stop)
|
75
|
+
@client.should respond_to(:receiving?)
|
76
|
+
end
|
77
|
+
it "should only be receiving when it is started" do
|
78
|
+
@mock_connection.stub!(:receive).and_return(nil)
|
79
|
+
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
80
|
+
@client.receiving?.should be_false
|
81
|
+
@client.start
|
82
|
+
@client.receiving?.should be_true
|
83
|
+
@client.stop
|
84
|
+
@client.receiving?.should be_false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "subscribing to queue" do
|
89
|
+
before(:each) do
|
90
|
+
@message_sent = Stomper::Frames::Message.new({'destination' => "/queue/test"}, "test message")
|
91
|
+
@mock_connection.should_receive(:connected?).any_number_of_times.and_return(true)
|
92
|
+
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
93
|
+
@mock_connection.should_receive(:receive).any_number_of_times.and_return(@message_sent)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should subscribe to a destination with a block" do
|
97
|
+
wait_for_message = true
|
98
|
+
@message_received = nil
|
99
|
+
@client.start
|
100
|
+
@client.subscribe("/queue/test") do |msg|
|
101
|
+
@message_received = msg
|
102
|
+
wait_for_message = false
|
103
|
+
end
|
104
|
+
true while wait_for_message
|
105
|
+
@client.stop
|
106
|
+
@message_received.should == @message_sent
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should not unsubscribe from all destinations when a subscription id is provided" do
|
110
|
+
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
111
|
+
@message_received = msg
|
112
|
+
end
|
113
|
+
@client.subscribe("/queue/test") do |msg|
|
114
|
+
@message_received = msg
|
115
|
+
end
|
116
|
+
@client.subscribe("/queue/test", :id => 'subscription-2') do |msg|
|
117
|
+
@message_received = msg
|
118
|
+
end
|
119
|
+
@client.unsubscribe("/queue/test", 'subscription-1')
|
120
|
+
@client.subscriptions.size.should == 2
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should not unsubscribe from non-naive subscriptions when only a destination is supplied" do
|
124
|
+
@client.subscribe("/queue/test", { 'id' => 'subscription-1' }) do |msg|
|
125
|
+
@message_received = msg
|
126
|
+
end
|
127
|
+
@client.subscribe("/queue/test") do |msg|
|
128
|
+
@message_received = msg
|
129
|
+
end
|
130
|
+
@client.subscribe("/queue/test") do |msg|
|
131
|
+
@message_received = msg
|
132
|
+
end
|
133
|
+
@client.unsubscribe("/queue/test")
|
134
|
+
@client.subscriptions.size.should == 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# Due to the receiver running in a separate thread, this may not be correct?
|
138
|
+
it "should unsubscribe from a destination and receive no more messages" do
|
139
|
+
@mutex = Mutex.new
|
140
|
+
@last_message_received = nil
|
141
|
+
@client.start
|
142
|
+
@client.subscribe("/queue/test") do |msg|
|
143
|
+
@last_message_received = Time.now
|
144
|
+
end
|
145
|
+
true until @last_message_received
|
146
|
+
@client.unsubscribe("/queue/test")
|
147
|
+
@unsubscribed_at = Time.now
|
148
|
+
@client.stop
|
149
|
+
(@last_message_received < @unsubscribed_at).should be_true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "transactions" do
|
154
|
+
before(:each) do
|
155
|
+
@mock_connection.should_receive(:transmit).with(duck_type(:to_stomp)).at_least(:once).and_return(nil)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should provide a transaction method that generates a new Transaction" do
|
159
|
+
@evaluated = false
|
160
|
+
@client.transaction do |t|
|
161
|
+
@evaluated = true
|
162
|
+
end
|
163
|
+
@evaluated.should be_true
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'shared_connection_examples'))
|
3
|
+
|
4
|
+
module Stomper
|
5
|
+
describe Connection do
|
6
|
+
before(:each) do
|
7
|
+
@connection = Connection.new("stomp:///")
|
8
|
+
end
|
9
|
+
|
10
|
+
it_should_behave_like "All Client Connections"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Stomper::Frames
|
4
|
+
describe ClientFrame do
|
5
|
+
before(:each) do
|
6
|
+
ClientFrame.generate_content_length = true
|
7
|
+
@client_frame = ClientFrame.new('COMMAND')
|
8
|
+
end
|
9
|
+
|
10
|
+
def str_size(str)
|
11
|
+
str.respond_to?(:bytesize) ? str.bytesize : str.size
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be provide a headers as an instance of Headers" do
|
15
|
+
@client_frame.headers.should be_an_instance_of(Stomper::Frames::Headers)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be convertable into a stomp frame" do
|
19
|
+
@client_frame.to_stomp.should == "COMMAND\n\n\0"
|
20
|
+
@client_frame.headers.destination = "/queue/test/1"
|
21
|
+
@client_frame.headers['transaction-id'] = '2'
|
22
|
+
@client_frame.headers[:ack] = 'client'
|
23
|
+
@client_frame.to_stomp.should == "COMMAND\nack:client\ndestination:/queue/test/1\ntransaction-id:2\n\n\0"
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "generating content-length header" do
|
27
|
+
it "should provide the header by default, overriding any existing header" do
|
28
|
+
@frame_body = 'testing'
|
29
|
+
@client_frame = ClientFrame.new('COMMAND', {'content-length' => 1}, @frame_body)
|
30
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not provide the header if the class option is set to false, unless explicitly set on the frame in particular" do
|
34
|
+
ClientFrame.generate_content_length = false
|
35
|
+
@frame_body = 'testing'
|
36
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
37
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
38
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
39
|
+
@client_frame.generate_content_length = true
|
40
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not provide the header if instance option is set false, when the class option is true" do
|
44
|
+
@frame_body = 'testing'
|
45
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
46
|
+
@client_frame.generate_content_length = false
|
47
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
48
|
+
@client_frame = ClientFrame.new('COMMAND', {:generate_content_length => false}, @frame_body)
|
49
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not overwrite an explicit content-length header when option is off at class or instance level" do
|
53
|
+
@frame_body = 'testing'
|
54
|
+
@client_frame = ClientFrame.new('COMMAND', { 'content-length' => 4}, @frame_body)
|
55
|
+
@client_frame.generate_content_length = false
|
56
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:4\n\n#{@frame_body}\0"
|
57
|
+
ClientFrame.generate_content_length = false
|
58
|
+
@client_frame = ClientFrame.new('COMMAND', {'content-length' => 2}, @frame_body)
|
59
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:2\n\n#{@frame_body}\0"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should the class option should be scoped to the class it is set on" do
|
63
|
+
@frame_body = 'testing'
|
64
|
+
Send.generate_content_length = false
|
65
|
+
@send_frame = Send.new('/queue/test/1', @frame_body)
|
66
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
67
|
+
@client_frame.to_stomp.should == "COMMAND\ncontent-length:#{str_size(@frame_body)}\n\n#{@frame_body}\0"
|
68
|
+
@send_frame.to_stomp.should == "SEND\ndestination:#{@send_frame.headers.destination}\n\n#{@frame_body}\0"
|
69
|
+
Send.generate_content_length = true
|
70
|
+
ClientFrame.generate_content_length = false
|
71
|
+
@send_frame = Send.new('/queue/test/1', @frame_body)
|
72
|
+
@client_frame = ClientFrame.new('COMMAND', {}, @frame_body)
|
73
|
+
@client_frame.to_stomp.should == "COMMAND\n\n#{@frame_body}\0"
|
74
|
+
@send_frame.to_stomp.should == "SEND\ncontent-length:#{str_size(@frame_body)}\ndestination:#{@send_frame.headers.destination}\n\n#{@frame_body}\0"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
describe "client frames" do
|
78
|
+
describe Abort do
|
79
|
+
it "should produce a proper stomp message" do
|
80
|
+
@abort = Abort.new("transaction-test", { :a_header => 'test'})
|
81
|
+
@abort.to_stomp.should == "ABORT\na_header:test\ntransaction:transaction-test\n\n\0"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
describe Ack do
|
85
|
+
it "should produce a proper stomp message" do
|
86
|
+
@ack = Ack.new("message-test", { :a_header => 'test'})
|
87
|
+
@ack.to_stomp.should == "ACK\na_header:test\nmessage-id:message-test\n\n\0"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should provide an Ack for a given message frame" do
|
91
|
+
@ack = Ack.ack_for(Message.new({'message-id' => 'test'}, "a body"))
|
92
|
+
@ack.to_stomp.should == "ACK\nmessage-id:test\n\n\0"
|
93
|
+
@ack = Ack.ack_for(Message.new({'message-id' => 'test', 'transaction' => 'tx-test'}, "a body"))
|
94
|
+
@ack.to_stomp.should == "ACK\nmessage-id:test\ntransaction:tx-test\n\n\0"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
describe Begin do
|
99
|
+
it "should produce a proper stomp message" do
|
100
|
+
@begin = Begin.new("transaction-test", { :a_header => 'test'})
|
101
|
+
@begin.to_stomp.should == "BEGIN\na_header:test\ntransaction:transaction-test\n\n\0"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
describe Commit do
|
105
|
+
it "should produce a proper stomp message" do
|
106
|
+
@commit = Commit.new("transaction-test", { :a_header => 'test'})
|
107
|
+
@commit.to_stomp.should == "COMMIT\na_header:test\ntransaction:transaction-test\n\n\0"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
describe Connect do
|
111
|
+
it "should produce a proper stomp message" do
|
112
|
+
@connect = Connect.new('uzer','s3cr3t', { :a_header => 'test' })
|
113
|
+
@connect.to_stomp.should == "CONNECT\na_header:test\nlogin:uzer\npasscode:s3cr3t\n\n\0"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
describe Disconnect do
|
117
|
+
it "should produce a proper stomp message" do
|
118
|
+
@disconnect = Disconnect.new({ :a_header => 'test'})
|
119
|
+
@disconnect.to_stomp.should == "DISCONNECT\na_header:test\n\n\0"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
describe Send do
|
123
|
+
it "should produce a proper stomp message" do
|
124
|
+
@send = Send.new("/queue/a/target", "a body", { :a_header => 'test'})
|
125
|
+
@send.to_stomp.should == "SEND\na_header:test\ncontent-length:6\ndestination:/queue/a/target\n\na body\0"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
describe Subscribe do
|
129
|
+
it "should produce a proper stomp message" do
|
130
|
+
@subscribe = Subscribe.new("/topic/some/target", { :a_header => 'test'})
|
131
|
+
@subscribe.to_stomp.should == "SUBSCRIBE\na_header:test\nack:auto\ndestination:/topic/some/target\n\n\0"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
describe Unsubscribe do
|
135
|
+
it "should produce a proper stomp message" do
|
136
|
+
@unsubscribe = Unsubscribe.new("/topic/target.name.path", { :a_header => 'test'})
|
137
|
+
@unsubscribe.to_stomp.should == "UNSUBSCRIBE\na_header:test\ndestination:/topic/target.name.path\n\n\0"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|