stomper 0.3.0
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/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,68 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates the headers attached to a Frame from the Stomper::Frames
|
4
|
+
# module. Instances of this class wrap a hash, but do so in a way
|
5
|
+
# to allow its values to be accessed by string, symbol, or method name,
|
6
|
+
# similar to an OpenStruct.
|
7
|
+
class Headers
|
8
|
+
# Creates a new Header instance, derived from the supplied hash, +hsh+.
|
9
|
+
def initialize(hsh = {})
|
10
|
+
@intern_head = hsh.inject({}) { |acc, (k,v)| acc[k.to_sym] = v; acc }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the 'id' header value, if it exists. Explicitly implemented
|
14
|
+
# because Object#id is a valid method by default.
|
15
|
+
def id
|
16
|
+
@intern_head[:id]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Assigns the 'id' header value. Explicitly implemented because Object#id
|
20
|
+
# is a valid method, and we implemented +id+ explicitly so why not +id=+
|
21
|
+
def id=(id)
|
22
|
+
@intern_head[:id] = id
|
23
|
+
end
|
24
|
+
|
25
|
+
# Allows the headers to be accessed as though they were a Hash instance.
|
26
|
+
def [](idx)
|
27
|
+
@intern_head[idx.to_sym]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Allows the headers to be assigned as though they were a Hash instance.
|
31
|
+
def []=(idx, val)
|
32
|
+
@intern_head[idx.to_sym] = val
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(meth, *args) # :nodoc:
|
36
|
+
raise TypeError, "can't modify frozen headers" if frozen?
|
37
|
+
meth_str = meth.to_s
|
38
|
+
ret = if meth_str =~ /=$/
|
39
|
+
raise ArgumentError, "setter #{meth_str} can only accept one value" if args.size != 1
|
40
|
+
meth_str.chop!
|
41
|
+
@intern_head[meth_str.to_sym] = args.first
|
42
|
+
else
|
43
|
+
raise ArgumentError, "getter #{meth_str} cannot accept any values" if args.size > 0
|
44
|
+
@intern_head[meth_str.to_sym]
|
45
|
+
end
|
46
|
+
_create_helpers(meth_str)
|
47
|
+
# Do the appropriate thing the first time around.
|
48
|
+
ret
|
49
|
+
end
|
50
|
+
|
51
|
+
# Converts the headers encapsulated by this object into a format that
|
52
|
+
# the Stomp Protocol expects them to be presented as.
|
53
|
+
def to_stomp
|
54
|
+
@intern_head.sort { |a, b| a.first.to_s <=> b.first.to_s }.inject("") do |acc, (k,v)|
|
55
|
+
acc << "#{k.to_s}:#{v}\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def _create_helpers(meth)
|
61
|
+
return if self.respond_to?(meth)
|
62
|
+
meta = class << self; self; end
|
63
|
+
meta.send(:define_method, meth) { self[meth] }
|
64
|
+
meta.send(:define_method, :"#{meth}=") { |v| self[meth] = v }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates a "MESSAGE" server side frame for the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class Message < Stomper::Frames::ServerFrame
|
8
|
+
# This class is the factory for all MESSAGE frames received.
|
9
|
+
factory_for :message
|
10
|
+
|
11
|
+
# Creates a new message frame with the given +headers+ and +body+
|
12
|
+
def initialize(headers, body)
|
13
|
+
super('MESSAGE', headers, body)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the message id generated by the stomp broker.
|
17
|
+
#
|
18
|
+
# This is a convenience method for:
|
19
|
+
# frame.headers[:'message-id'] or frame.headers['message-id']
|
20
|
+
def id
|
21
|
+
@headers[:'message-id']
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the destination from which this message was delivered.
|
25
|
+
#
|
26
|
+
# This is a convenience method for:
|
27
|
+
# frame.headers.destination, frame.headers['destination'], or
|
28
|
+
# frame.headers[:destination]
|
29
|
+
def destination
|
30
|
+
@headers.destination
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the name of the subscription which is responsible for the
|
34
|
+
# client having received this message.
|
35
|
+
#
|
36
|
+
# This is a convenience method for:
|
37
|
+
# frame.headers.subscription, frame.headers['subscription'] or
|
38
|
+
# frame.headers[:subscription]
|
39
|
+
def subscription
|
40
|
+
@headers.subscription
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates a "RECEIPT" server side frame for the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class Receipt < Stomper::Frames::ServerFrame
|
8
|
+
# This class is a factory for all RECEIPT commands received.
|
9
|
+
factory_for :receipt
|
10
|
+
|
11
|
+
# Creates a new Receipt frame with the supplied +headers+ and +body+
|
12
|
+
def initialize(headers, body)
|
13
|
+
super('RECEIPT', headers, body)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the 'receipt-id' header of the frame, which
|
17
|
+
# will correspond to the 'receipt' header of the message
|
18
|
+
# that caused this receipt to be sent by the stomp broker.
|
19
|
+
def for
|
20
|
+
@headers[:'receipt-id']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates a "SEND" frame from the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class Send < Stomper::Frames::ClientFrame
|
8
|
+
def initialize(destination, body, headers={})
|
9
|
+
super('SEND', headers, body)
|
10
|
+
@headers.destination = destination
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates a server side frame for the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class ServerFrame
|
8
|
+
attr_reader :command, :headers, :body
|
9
|
+
|
10
|
+
# Creates a new server frame corresponding to the
|
11
|
+
# supplied +command+ with the given +headers+ and +body+.
|
12
|
+
def initialize(command, headers={}, body=nil)
|
13
|
+
@command = command
|
14
|
+
@headers = Headers.new(headers)
|
15
|
+
@body = body
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Provides a method for subclasses to register themselves
|
20
|
+
# as factories for particular stomp commands by passing a list
|
21
|
+
# of strings (or symbols) to this method. Each element in
|
22
|
+
# the list is interpretted as the command for which we will
|
23
|
+
# defer to the calling subclass to build.
|
24
|
+
def factory_for(*args)
|
25
|
+
@@registered_commands ||= {}
|
26
|
+
args.each do |command|
|
27
|
+
@@registered_commands[command.to_s.upcase] = self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Builds a new ServerFrame instance by first checking to
|
32
|
+
# see if some subclass of ServerFrame has registered itself
|
33
|
+
# as a builder of the particular command. If so, a new
|
34
|
+
# instance of that subclass is created, otherwise a generic
|
35
|
+
# ServerFrame instance is created with its +command+ attribute
|
36
|
+
# set appropriately.
|
37
|
+
def build(command, headers, body)
|
38
|
+
command = command.to_s.upcase
|
39
|
+
if @@registered_commands.has_key?(command)
|
40
|
+
@@registered_commands[command].new(headers, body)
|
41
|
+
else
|
42
|
+
ServerFrame.new(command, headers, body)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates a "SUBSCRIBE" frame from the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class Subscribe < Stomper::Frames::ClientFrame
|
8
|
+
def initialize(destination, headers={})
|
9
|
+
super('SUBSCRIBE', headers)
|
10
|
+
@headers['destination'] = destination
|
11
|
+
@headers['ack'] ||= 'auto'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the ack mode of this subscription. (defaults to 'auto')
|
15
|
+
#
|
16
|
+
# This is a convenience method, and may also be accessed through
|
17
|
+
# frame.headers.ack or frame.headers[:ack] or frame.headers['ack']
|
18
|
+
def ack
|
19
|
+
@headers['ack']
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the destination to which we are subscribing.
|
23
|
+
#
|
24
|
+
# This is a convenience method, and may also be accessed through
|
25
|
+
# frame.headers.destination or frame.headers[:destination] or frame.headers['destination']
|
26
|
+
def destination
|
27
|
+
@headers['destination']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the id of this subscription, if it has been set.
|
31
|
+
#
|
32
|
+
# This is a convenience method, and may also be accessed through
|
33
|
+
# frame.headers.id or frame.headers[:id] or frame.headers['id']
|
34
|
+
def id
|
35
|
+
@headers['id']
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the selector header of this subscription, if it has been set.
|
39
|
+
#
|
40
|
+
# This is a convenience method, and may also be accessed through
|
41
|
+
# frame.headers.selector or frame.headers[:selector] or frame.headers['selector']
|
42
|
+
def selector
|
43
|
+
@headers['selector']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Stomper
|
2
|
+
module Frames
|
3
|
+
# Encapsulates an "UNSUBSCRIBE" frame from the Stomp Protocol.
|
4
|
+
#
|
5
|
+
# See the {Stomp Protocol Specification}[http://stomp.codehaus.org/Protocol]
|
6
|
+
# for more details.
|
7
|
+
class Unsubscribe < Stomper::Frames::ClientFrame
|
8
|
+
def initialize(destination, headers={})
|
9
|
+
super('UNSUBSCRIBE', headers)
|
10
|
+
@headers['destination'] = destination
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the id of the subscription being unsubscribed from, if it
|
14
|
+
# exists.
|
15
|
+
#
|
16
|
+
# This is a convenience method, and may also be accessed through
|
17
|
+
# frame.headers.id or frame.headers[:id] or frame.headers['id']
|
18
|
+
def id
|
19
|
+
@headers['id']
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Stomper
|
2
|
+
# A representation of a subscription to a stomp broker destination. The
|
3
|
+
# attributes +id+, +destination+, +ack+ and +selector+ have the same
|
4
|
+
# semantic meaning as the headers of a Stomp "SUBSCRIBE" frame with the same
|
5
|
+
# name.
|
6
|
+
class Subscription
|
7
|
+
attr_reader :id, :destination, :ack, :selector
|
8
|
+
|
9
|
+
# Creates a new Subscription instance from the given parameters.
|
10
|
+
# The +destination_or_options+ parameter can either be a string
|
11
|
+
# specification of the destination, such as "/queue/target", or a hash
|
12
|
+
# corresponding to the headers of a "SUBSCRIBE" frame
|
13
|
+
# (eg: { :destination => "/queue/target", :id => "sub-001", ... })
|
14
|
+
#
|
15
|
+
# The optional +subscription_id+ parameter is a string corresponding
|
16
|
+
# to the name of this subscription. If this parameter is specified, it
|
17
|
+
# should be unique within the context of a given Stomper::Client, otherwise
|
18
|
+
# the behavior of the Stomper::Client#unsubscribe method may have unintended
|
19
|
+
# consequences.
|
20
|
+
#
|
21
|
+
# The optional +ack+ parameter specifies the mode that a client
|
22
|
+
# will use to acknowledge received messages and may be either :client or :auto.
|
23
|
+
# The default, :auto, does not require the client to notify the broker when
|
24
|
+
# it has received a message; however, setting +ack+ to :client will require
|
25
|
+
# each message received by this subscription to be acknowledged through the
|
26
|
+
# use of Stomper::Client#ack in order to ensure proper interaction between
|
27
|
+
# client and broker.
|
28
|
+
#
|
29
|
+
# The +selector+ parameter (again, optional) sets a SQL 92 selector for
|
30
|
+
# this subscription with the stomp broker as per the Stomp Protocol specification.
|
31
|
+
# Support of this functionality is entirely the responsibility of the broker,
|
32
|
+
# there is no client side filtering being done on incoming messages.
|
33
|
+
#
|
34
|
+
# When a message is "received" by an instance of Subscription, the supplied
|
35
|
+
# +block+ is inovked with the received message sent as a parameter.
|
36
|
+
#
|
37
|
+
# If no +subscription_id+ is specified, either explicitly or through a
|
38
|
+
# hash key of 'id' in +destination_or_options+, one may be automatically
|
39
|
+
# generated of the form "sub-#{Time.now.to_f}". The automatic generation
|
40
|
+
# of a subscription id occurs if and only if naive? returns false.
|
41
|
+
#
|
42
|
+
# While direct creation of Subscription instances is possible, the preferred
|
43
|
+
# method is for them to be constructed by a Stomper::Client through the use
|
44
|
+
# of the Stomper::Client#subscribe method.
|
45
|
+
#
|
46
|
+
# See also: naive?, Stomper::Client#subscribe, Stomper::Client#unsubscribe,
|
47
|
+
# Stomper::Client#ack
|
48
|
+
#
|
49
|
+
def initialize(destination_or_options, subscription_id=nil, ack=nil, selector=nil, &block)
|
50
|
+
if destination_or_options.is_a?(Hash)
|
51
|
+
options = Stomper::Frames::Headers.new(destination_or_options)
|
52
|
+
destination = options.destination
|
53
|
+
subscription_id ||= options.id
|
54
|
+
ack ||= options.ack
|
55
|
+
selector ||= options.selector
|
56
|
+
else
|
57
|
+
destination = destination_or_options.to_s
|
58
|
+
end
|
59
|
+
@id = subscription_id
|
60
|
+
@destination = destination
|
61
|
+
@ack = (ack || :auto).to_sym
|
62
|
+
@selector = selector
|
63
|
+
@call_back = block
|
64
|
+
@id ||= "sub-#{Time.now.to_f}" unless naive?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns true if this subscription has no explicitly specified id,
|
68
|
+
# has no selector specified, and acknowledges messages through the :auto
|
69
|
+
# mode.
|
70
|
+
def naive?
|
71
|
+
@id.nil? && @selector.nil? && @ack == :auto
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if this subscription is responsible for a Stomper::Client
|
75
|
+
# instance receiving +message_frame+.
|
76
|
+
#
|
77
|
+
# See also: receives_for?, perform
|
78
|
+
def accepts?(message_frame)
|
79
|
+
receives_for?(message_frame.destination, message_frame.subscription)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns true if this subscription is responsible for receiving
|
83
|
+
# messages for the given destination or subscription id, specified
|
84
|
+
# by +dest+ and +subid+ respectively.
|
85
|
+
#
|
86
|
+
# Note: if +subid+ is non-nil or this subscription is not naive?,
|
87
|
+
# then this method returns true if and only if the supplied +subid+ is
|
88
|
+
# equal to the +id+ of this subscription. Otherwise, the return value
|
89
|
+
# depends only upon the equality of +dest+ and this subscriptions +destination+
|
90
|
+
# attribute.
|
91
|
+
#
|
92
|
+
# See also: naive?
|
93
|
+
def receives_for?(dest, subid=nil)
|
94
|
+
if naive? && subid.nil?
|
95
|
+
@destination == dest
|
96
|
+
else
|
97
|
+
@id == subid
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Invokes the block associated with this subscription if
|
102
|
+
# this subscription accepts the supplied +message_frame+.
|
103
|
+
#
|
104
|
+
# See also: accepts?
|
105
|
+
def perform(message_frame)
|
106
|
+
@call_back.call(message_frame) if accepts?(message_frame)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Converts this representation of a subscription into a
|
110
|
+
# Stomper::Frames::Subscribe client frame that can be transmitted
|
111
|
+
# to a stomp broker through a Stomper::Connection instance.
|
112
|
+
def to_subscribe
|
113
|
+
headers = { 'destination' => @destination, 'ack' => @ack.to_s }
|
114
|
+
headers['id'] = @id unless @id.nil?
|
115
|
+
headers['selector'] = @selector unless @selector.nil?
|
116
|
+
Stomper::Frames::Subscribe.new(@destination, headers)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Converts this representation of a subscription into a
|
120
|
+
# Stomper::Frames::Unsubscribe client frame that can be transmitted
|
121
|
+
# to a stomp broker through a Stomper::Connection instance.
|
122
|
+
def to_unsubscribe
|
123
|
+
headers = { 'destination' => @destination }
|
124
|
+
headers['id'] = @id unless @id.nil?
|
125
|
+
Stomper::Frames::Unsubscribe.new(@destination, headers)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Stomper
|
2
|
+
# A Subscription collection class used internally by Stomper::Client to store
|
3
|
+
# its subscriptions. Instances of this class utilize synchronization making
|
4
|
+
# it safe to use in a multi-threaded context.
|
5
|
+
class Subscriptions
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Creates a new Subscriptions container.
|
9
|
+
def initialize
|
10
|
+
@subs = []
|
11
|
+
@sub_lock = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adds the supplied subscription, +sub+, to the collection.
|
15
|
+
def <<(sub)
|
16
|
+
add(sub)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adds the supplied subscription, +sub+, to the collection.
|
20
|
+
def add(sub)
|
21
|
+
raise ArgumentError, "appended object must be a subscription" unless sub.is_a?(Subscription)
|
22
|
+
@sub_lock.synchronize { @subs << sub }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Removes all Subscription objects from the collection that match
|
26
|
+
# the supplied destination, +dest+, and subscription id, +subid+.
|
27
|
+
# If +dest+ is a hash, the value referenced by the :destination key
|
28
|
+
# will be used as the destination, and +subid+ will be set to the value
|
29
|
+
# referenced by :id, unless it is explicitly set beforehand. If +dest+ is
|
30
|
+
# an instance of Subscription, the +destination+ attribute will be used
|
31
|
+
# as the destination, and +subid+ will be set to the +id+ attribute, unless
|
32
|
+
# explicitly set beforehand. The Subscription objects removed are all of
|
33
|
+
# those, and only those, for which the Stomper::Subscription#receives_for?
|
34
|
+
# method returns true given the destination and/or subscription id.
|
35
|
+
#
|
36
|
+
# This method returns an array of all the Subscription objects that were
|
37
|
+
# removed, or an empty array if none were removed.
|
38
|
+
#
|
39
|
+
# See also: Stomper::Subscription#receives_for?, Stomper::Client#unsubscribe
|
40
|
+
def remove(dest, subid=nil)
|
41
|
+
if dest.is_a?(Hash)
|
42
|
+
subid ||= dest[:id]
|
43
|
+
dest = dest[:destination]
|
44
|
+
elsif dest.is_a?(Subscription)
|
45
|
+
subid ||= dest.id
|
46
|
+
dest = dest.destination
|
47
|
+
end
|
48
|
+
_remove(dest, subid)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the number of Subscription objects within the container through
|
52
|
+
# the use of synchronization.
|
53
|
+
def size
|
54
|
+
@sub_lock.synchronize { @subs.size }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the first Subscription object within the container through
|
58
|
+
# the use of synchronization.
|
59
|
+
def first
|
60
|
+
@sub_lock.synchronize { @subs.first }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the last Subscription object within the container through
|
64
|
+
# the use of synchronization.
|
65
|
+
def last
|
66
|
+
@sub_lock.synchronize { @subs.last }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Evaluates the supplied +block+ for each Subscription object
|
70
|
+
# within the container, or yields an Enumerator for the collection
|
71
|
+
# if no +block+ is given. As this method is synchronized, it is
|
72
|
+
# entirely possible to enter into a dead-lock if the supplied block
|
73
|
+
# in turn calls any other synchronized method of the container.
|
74
|
+
# [This could be remedied by creating a new array with
|
75
|
+
# the same Subscription objects currently contained, and performing
|
76
|
+
# the +each+ call on the new array. Give this some thought.]
|
77
|
+
def each(&block)
|
78
|
+
@sub_lock.synchronize { @subs.each(&block) }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Passes the supplied +message+ to all Subscription objects within the
|
82
|
+
# collection through their Stomper::Subscription#perform method.
|
83
|
+
def perform(message)
|
84
|
+
@sub_lock.synchronize { @subs.each { |sub| sub.perform(message) } }
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def _remove(dest, subid)
|
89
|
+
@sub_lock.synchronize do
|
90
|
+
to_remove, @subs = @subs.partition { |s| s.receives_for?(dest,subid) }
|
91
|
+
to_remove
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|