stomper 1.0.0 → 2.0.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.
Files changed (152) hide show
  1. data/.gitignore +5 -0
  2. data/{spec/spec.opts → .rspec} +0 -2
  3. data/Gemfile +4 -0
  4. data/LICENSE +201 -201
  5. data/README.md +130 -0
  6. data/Rakefile +5 -0
  7. data/examples/basic.rb +38 -0
  8. data/examples/events.rb +54 -0
  9. data/features/acking_messages.feature +147 -0
  10. data/features/disconnecting.feature +12 -0
  11. data/features/establish_connection.feature +44 -0
  12. data/features/protocol_version_negotiation.feature +61 -0
  13. data/features/receipts.feature +72 -0
  14. data/features/scopes.feature +32 -0
  15. data/features/secure_connections.feature +38 -0
  16. data/features/send_and_message.feature +28 -0
  17. data/features/steps/acking_messages_steps.rb +39 -0
  18. data/features/steps/disconnecting_steps.rb +8 -0
  19. data/features/steps/establish_connection_steps.rb +74 -0
  20. data/features/steps/frame_transmission_steps.rb +35 -0
  21. data/features/steps/protocol_version_negotiation_steps.rb +15 -0
  22. data/features/steps/receipts_steps.rb +79 -0
  23. data/features/steps/scopes_steps.rb +52 -0
  24. data/features/steps/secure_connections_steps.rb +41 -0
  25. data/features/steps/send_and_message_steps.rb +35 -0
  26. data/features/steps/subscribing_steps.rb +36 -0
  27. data/features/steps/threaded_receiver_steps.rb +8 -0
  28. data/features/steps/transactions_steps.rb +0 -0
  29. data/features/subscribing.feature +151 -0
  30. data/features/support/env.rb +11 -0
  31. data/features/support/header_helpers.rb +12 -0
  32. data/features/support/ssl/README +6 -0
  33. data/features/support/ssl/broker_cert.csr +17 -0
  34. data/features/support/ssl/broker_cert.pem +72 -0
  35. data/features/support/ssl/broker_key.pem +27 -0
  36. data/features/support/ssl/client_cert.csr +17 -0
  37. data/features/support/ssl/client_cert.pem +72 -0
  38. data/features/support/ssl/client_key.pem +27 -0
  39. data/features/support/ssl/demoCA/cacert.pem +17 -0
  40. data/features/support/ssl/demoCA/index.txt +2 -0
  41. data/features/support/ssl/demoCA/index.txt.attr +1 -0
  42. data/features/support/ssl/demoCA/index.txt.attr.old +1 -0
  43. data/features/support/ssl/demoCA/index.txt.old +1 -0
  44. data/features/support/ssl/demoCA/newcerts/01.pem +72 -0
  45. data/features/support/ssl/demoCA/newcerts/02.pem +72 -0
  46. data/features/support/ssl/demoCA/private/cakey.pem +17 -0
  47. data/features/support/ssl/demoCA/serial +1 -0
  48. data/features/support/ssl/demoCA/serial.old +1 -0
  49. data/features/support/test_stomp_server.rb +150 -0
  50. data/features/threaded_receiver.feature +11 -0
  51. data/features/transactions.feature +66 -0
  52. data/lib/stomper.rb +30 -20
  53. data/lib/stomper/connection.rb +442 -102
  54. data/lib/stomper/errors.rb +59 -0
  55. data/lib/stomper/extensions.rb +10 -0
  56. data/lib/stomper/extensions/common.rb +258 -0
  57. data/lib/stomper/extensions/events.rb +213 -0
  58. data/lib/stomper/extensions/heartbeat.rb +101 -0
  59. data/lib/stomper/extensions/scoping.rb +56 -0
  60. data/lib/stomper/frame.rb +54 -0
  61. data/lib/stomper/frame_serializer.rb +217 -0
  62. data/lib/stomper/headers.rb +15 -0
  63. data/lib/stomper/receipt_manager.rb +36 -0
  64. data/lib/stomper/receivers.rb +7 -0
  65. data/lib/stomper/receivers/threaded.rb +71 -0
  66. data/lib/stomper/scopes.rb +9 -0
  67. data/lib/stomper/scopes/header_scope.rb +49 -0
  68. data/lib/stomper/scopes/receipt_scope.rb +44 -0
  69. data/lib/stomper/scopes/transaction_scope.rb +109 -0
  70. data/lib/stomper/sockets.rb +66 -28
  71. data/lib/stomper/subscription_manager.rb +79 -0
  72. data/lib/stomper/support.rb +68 -0
  73. data/lib/stomper/support/1.8/frame_serializer.rb +53 -0
  74. data/lib/stomper/support/1.8/headers.rb +183 -0
  75. data/lib/stomper/support/1.9/frame_serializer.rb +64 -0
  76. data/lib/stomper/support/1.9/headers.rb +172 -0
  77. data/lib/stomper/support/ruby.rb +13 -0
  78. data/lib/stomper/uris.rb +49 -0
  79. data/lib/stomper/version.rb +7 -0
  80. data/spec/spec_helper.rb +13 -9
  81. data/spec/stomper/connection_spec.rb +712 -0
  82. data/spec/stomper/extensions/common_spec.rb +187 -0
  83. data/spec/stomper/extensions/events_spec.rb +78 -0
  84. data/spec/stomper/extensions/heartbeat_spec.rb +103 -0
  85. data/spec/stomper/extensions/scoping_spec.rb +21 -0
  86. data/spec/stomper/frame_serializer_1.8_spec.rb +318 -0
  87. data/spec/stomper/frame_serializer_spec.rb +316 -0
  88. data/spec/stomper/frame_spec.rb +36 -0
  89. data/spec/stomper/headers_spec.rb +224 -0
  90. data/spec/stomper/receipt_manager_spec.rb +91 -0
  91. data/spec/stomper/receivers/threaded_spec.rb +116 -0
  92. data/spec/stomper/scopes/header_scope_spec.rb +42 -0
  93. data/spec/stomper/scopes/receipt_scope_spec.rb +51 -0
  94. data/spec/stomper/scopes/transaction_scope_spec.rb +183 -0
  95. data/spec/stomper/sockets_spec.rb +113 -0
  96. data/spec/stomper/subscription_manager_spec.rb +107 -0
  97. data/spec/stomper/support_spec.rb +69 -0
  98. data/spec/stomper/uris_spec.rb +54 -0
  99. data/spec/stomper_spec.rb +9 -0
  100. data/spec/support/custom_argument_matchers.rb +57 -0
  101. data/spec/support/existential_frame_matchers.rb +19 -0
  102. data/spec/support/frame_header_matchers.rb +10 -0
  103. data/stomper.gemspec +30 -0
  104. metadata +272 -97
  105. data/AUTHORS +0 -21
  106. data/CHANGELOG +0 -20
  107. data/README.rdoc +0 -120
  108. data/lib/stomper/client.rb +0 -34
  109. data/lib/stomper/frame_reader.rb +0 -73
  110. data/lib/stomper/frame_writer.rb +0 -21
  111. data/lib/stomper/frames.rb +0 -39
  112. data/lib/stomper/frames/abort.rb +0 -10
  113. data/lib/stomper/frames/ack.rb +0 -25
  114. data/lib/stomper/frames/begin.rb +0 -11
  115. data/lib/stomper/frames/client_frame.rb +0 -89
  116. data/lib/stomper/frames/commit.rb +0 -10
  117. data/lib/stomper/frames/connect.rb +0 -10
  118. data/lib/stomper/frames/connected.rb +0 -30
  119. data/lib/stomper/frames/disconnect.rb +0 -10
  120. data/lib/stomper/frames/error.rb +0 -21
  121. data/lib/stomper/frames/message.rb +0 -48
  122. data/lib/stomper/frames/receipt.rb +0 -19
  123. data/lib/stomper/frames/send.rb +0 -10
  124. data/lib/stomper/frames/server_frame.rb +0 -38
  125. data/lib/stomper/frames/subscribe.rb +0 -42
  126. data/lib/stomper/frames/unsubscribe.rb +0 -19
  127. data/lib/stomper/open_uri_interface.rb +0 -41
  128. data/lib/stomper/receipt_handlers.rb +0 -23
  129. data/lib/stomper/receiptor.rb +0 -38
  130. data/lib/stomper/subscriber.rb +0 -76
  131. data/lib/stomper/subscription.rb +0 -128
  132. data/lib/stomper/subscriptions.rb +0 -95
  133. data/lib/stomper/threaded_receiver.rb +0 -59
  134. data/lib/stomper/transaction.rb +0 -185
  135. data/lib/stomper/transactor.rb +0 -50
  136. data/lib/stomper/uri.rb +0 -55
  137. data/spec/client_spec.rb +0 -29
  138. data/spec/connection_spec.rb +0 -22
  139. data/spec/frame_reader_spec.rb +0 -37
  140. data/spec/frame_writer_spec.rb +0 -27
  141. data/spec/frames/client_frame_spec.rb +0 -66
  142. data/spec/frames/indirect_frame_spec.rb +0 -45
  143. data/spec/frames/server_frame_spec.rb +0 -85
  144. data/spec/open_uri_interface_spec.rb +0 -132
  145. data/spec/receiptor_spec.rb +0 -35
  146. data/spec/shared_connection_examples.rb +0 -79
  147. data/spec/subscriber_spec.rb +0 -77
  148. data/spec/subscription_spec.rb +0 -157
  149. data/spec/subscriptions_spec.rb +0 -145
  150. data/spec/threaded_receiver_spec.rb +0 -33
  151. data/spec/transaction_spec.rb +0 -139
  152. data/spec/transactor_spec.rb +0 -46
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "COMMIT" frame from the Stomp Protocol.
4
- class Commit < Stomper::Frames::ClientFrame
5
- def initialize(transaction_id, headers={})
6
- super(headers.merge({ :transaction => transaction_id }))
7
- end
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "CONNECT" frame from the Stomp Protocol.
4
- class Connect < Stomper::Frames::ClientFrame
5
- def initialize(login='', passcode='', headers={})
6
- super(headers.merge({ :login => login, :passcode => passcode }))
7
- end
8
- end
9
- end
10
- end
@@ -1,30 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "CONNECTED" server side frame for the Stomp Protocol.
4
- class Connected < Stomper::Frames::ServerFrame
5
-
6
- # Builds a Connected frame instance with the supplied
7
- # +headers+ and +body+
8
- def initialize(headers, body)
9
- super(headers, body)
10
- end
11
-
12
- # A convenience method that returns the value of
13
- # the session header, if it is set.
14
- #
15
- # This value can also be accessed as:
16
- # frame.headers[:session]
17
- def session
18
- @headers[:session]
19
- end
20
-
21
- def perform
22
- # TODO: I want the frames, particularly the server frames, to know
23
- # 'what to do' when they are received. For instance, when a CONNECTED
24
- # frame is received, the connection it is received on should be marked
25
- # as being "connected". This way we can get rid of the various conditional
26
- # behavior based on Frame classes in connection and client.
27
- end
28
- end
29
- end
30
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "DISCONNECT" frame from the Stomp Protocol.
4
- class Disconnect < Stomper::Frames::ClientFrame
5
- def initialize(headers={})
6
- super(headers)
7
- end
8
- end
9
- end
10
- end
@@ -1,21 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates an "ERROR" server side frame for the Stomp Protocol.
4
- class Error < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new Error frame with the supplied +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the message responsible for the generation of this Error frame,
12
- # if applicable.
13
- #
14
- # This is a convenience method for:
15
- # frame.headers[:message]
16
- def message
17
- @headers[:message]
18
- end
19
- end
20
- end
21
- end
@@ -1,48 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "MESSAGE" server side frame for the Stomp Protocol.
4
- class Message < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new message frame with the given +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the message id generated by the stomp broker.
12
- #
13
- # This is a convenience method for:
14
- # frame.headers[:'message-id']
15
- def id
16
- @headers[:'message-id']
17
- end
18
-
19
- # Returns the destination from which this message was delivered.
20
- #
21
- # This is a convenience method for:
22
- # frame.headers[:destination]
23
- def destination
24
- @headers[:destination]
25
- end
26
-
27
- # Returns the name of the subscription which is responsible for the
28
- # client having received this message.
29
- #
30
- # This is a convenience method for:
31
- # frame.headers[:subscription]
32
- def subscription
33
- @headers[:subscription]
34
- end
35
-
36
- def perform
37
- # TODO: I want the frames, particularly the server frames, to know
38
- # 'what to do' when they are received. For instance, when a MESSAGE
39
- # frame is received, the message should be applied to all
40
- # stored subscriptions on the receiving connection. This will
41
- # remove the conditional branching in the Client class, and provide
42
- # a more flexible means of adding additional behaviors, instead of
43
- # relying on what is sure to become a giant case statement in Client
44
- # if we must change state on other Frames later.
45
- end
46
- end
47
- end
48
- end
@@ -1,19 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "RECEIPT" server side frame for the Stomp Protocol.
4
- class Receipt < Stomper::Frames::ServerFrame
5
-
6
- # Creates a new Receipt frame with the supplied +headers+ and +body+
7
- def initialize(headers, body)
8
- super(headers, body)
9
- end
10
-
11
- # Returns the 'receipt-id' header of the frame, which
12
- # will correspond to the 'receipt' header of the message
13
- # that caused this receipt to be sent by the stomp broker.
14
- def for
15
- @headers[:'receipt-id']
16
- end
17
- end
18
- end
19
- end
@@ -1,10 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "SEND" frame from the Stomp Protocol.
4
- class Send < Stomper::Frames::ClientFrame
5
- def initialize(destination, body, headers={})
6
- super(headers.merge({ :destination => destination }), body)
7
- end
8
- end
9
- end
10
- end
@@ -1,38 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a server side frame for the Stomp Protocol.
4
- class ServerFrame < IndirectFrame
5
-
6
- # Creates a new server frame corresponding to the
7
- # supplied +command+ with the given +headers+ and +body+.
8
- def initialize(headers={}, body=nil, command = nil)
9
- super
10
- end
11
-
12
- class << self
13
- def inherited(server_frame) #:nodoc:
14
- declared_frames << { :class => server_frame, :command => server_frame.name.split("::").last.downcase.to_sym }
15
- end
16
-
17
- def declared_frames
18
- @declared_frames ||= []
19
- end
20
-
21
- # Builds a new ServerFrame instance by first checking to
22
- # see if some subclass of ServerFrame has registered itself
23
- # as a builder of the particular command. If so, a new
24
- # instance of that subclass is created, otherwise a generic
25
- # ServerFrame instance is created with its +command+ attribute
26
- # set appropriately.
27
- def build(command, headers, body)
28
- com_sym = command.downcase.to_sym
29
- if klass = declared_frames.detect { |frame| com_sym == frame[:command] }
30
- klass[:class].new(headers, body)
31
- else
32
- ServerFrame.new(headers, body, command)
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,42 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates a "SUBSCRIBE" frame from the Stomp Protocol.
4
- class Subscribe < Stomper::Frames::ClientFrame
5
- def initialize(destination, headers={})
6
- super({ :ack => 'auto' }.merge(headers).merge({ :destination => destination }))
7
- end
8
-
9
- # Returns the ack mode of this subscription. (defaults to 'auto')
10
- #
11
- # This is a convenience method, and may also be accessed through
12
- # frame.headers[:ack]
13
- def ack
14
- @headers[:ack]
15
- end
16
-
17
- # Returns the destination to which we are subscribing.
18
- #
19
- # This is a convenience method, and may also be accessed through
20
- # frame.headers[:destination]
21
- def destination
22
- @headers[:destination]
23
- end
24
-
25
- # Returns the id of this subscription, if it has been set.
26
- #
27
- # This is a convenience method, and may also be accessed through
28
- # frame.headers[:id]
29
- def id
30
- @headers[:id]
31
- end
32
-
33
- # Returns the selector header of this subscription, if it has been set.
34
- #
35
- # This is a convenience method, and may also be accessed through
36
- # frame.headers[:selector]
37
- def selector
38
- @headers[:selector]
39
- end
40
- end
41
- end
42
- end
@@ -1,19 +0,0 @@
1
- module Stomper
2
- module Frames
3
- # Encapsulates an "UNSUBSCRIBE" frame from the Stomp Protocol.
4
- class Unsubscribe < Stomper::Frames::ClientFrame
5
- def initialize(destination, headers={})
6
- super(headers.merge({ :destination => destination }))
7
- end
8
-
9
- # Returns the id of the subscription being unsubscribed from, if it
10
- # exists.
11
- #
12
- # This is a convenience method, and may also be accessed through
13
- # frame.headers[:id]
14
- def id
15
- @headers[:id]
16
- end
17
- end
18
- end
19
- end
@@ -1,41 +0,0 @@
1
- module Stomper
2
- module OpenUriInterface
3
- def put(msg, headers={})
4
- send(default_destination, msg, headers.merge(:generate_content_length => false))
5
- end
6
- alias_method :puts, :put
7
-
8
- def write(msg, headers={})
9
- send(default_destination, msg, headers.merge(:generate_content_length => true))
10
- end
11
-
12
- def first(n=1)
13
- received = []
14
- each do |m|
15
- received << m
16
- break if received.size == n
17
- end
18
- n == 1 ? received.first : received
19
- end
20
- alias_method :get, :first
21
- alias_method :gets, :first
22
- alias_method :read, :first
23
-
24
- # This is the tricky one.
25
- # The subscriber interface is not going to work here, because it is built
26
- # for an entirely different use case (threaded receiving)
27
- # This interface, by contrast, is blocking... fudge.
28
- def each(&block)
29
- subscription = subscribe(default_destination) { |m| m }
30
- loop do
31
- m = receive
32
- yield m if m.is_a?(Stomper::Frames::Message) && subscription.accepts?(m)
33
- end
34
- end
35
-
36
- private
37
- def default_destination
38
- uri.path
39
- end
40
- end
41
- end
@@ -1,23 +0,0 @@
1
- module Stomper
2
- class ReceiptHandlers
3
- def initialize
4
- @recps = {}
5
- @recp_lock = Mutex.new
6
- end
7
-
8
- def add(receipt_id, callback)
9
- @recp_lock.synchronize { @recps[receipt_id] = callback }
10
- end
11
-
12
- def size
13
- @recps.size
14
- end
15
-
16
- def perform(receipt)
17
- @recp_lock.synchronize do
18
- callback = @recps.delete(receipt.for)
19
- callback.call(receipt) if callback
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- module Stomper
2
- module Receiptor
3
- def self.included(base)
4
- if base.method_defined?(:receive)
5
- base.instance_eval do
6
- alias_method :receive_without_receipt_dispatch, :receive
7
- alias_method :receive, :receive_with_receipt_dispatch
8
- end
9
- end
10
- if base.method_defined?(:send)
11
- base.instance_eval do
12
- alias_method :send_without_receipt_handler, :send
13
- alias_method :send, :send_with_receipt_handler
14
- end
15
- end
16
- end
17
-
18
- # Receives a frame and dispatches it to the known receipt handlers, if the
19
- # received frame is a RECEIPT frame.
20
- def receive_with_receipt_dispatch
21
- frame = receive_without_receipt_dispatch
22
- receipt_handlers.perform(frame) if frame.is_a?(::Stomper::Frames::Receipt)
23
- frame
24
- end
25
-
26
- def send_with_receipt_handler(destination, body, headers={}, &block)
27
- if block_given?
28
- headers[:receipt] ||= "rcpt-#{Time.now.to_f}"
29
- receipt_handlers.add(headers[:receipt], block)
30
- end
31
- send_without_receipt_handler(destination, body, headers)
32
- end
33
-
34
- def receipt_handlers
35
- @receipt_handlers ||= ::Stomper::ReceiptHandlers.new
36
- end
37
- end
38
- end
@@ -1,76 +0,0 @@
1
- module Stomper
2
- module Subscriber
3
- def self.included(base)
4
- if base.method_defined?(:receive)
5
- base.instance_eval do
6
- alias_method :receive_without_message_dispatch, :receive
7
- alias_method :receive, :receive_with_message_dispatch
8
- end
9
- end
10
- end
11
-
12
- # Receives a frame and dispatches it to the known subscriptions, if the
13
- # received frame is a MESSAGE frame.
14
- def receive_with_message_dispatch
15
- frame = receive_without_message_dispatch
16
- subscriptions.perform(frame) if frame.is_a?(::Stomper::Frames::Message)
17
- frame
18
- end
19
-
20
- # Subscribes to the specified +destination+, passing along
21
- # the optional +headers+ inside the subscription frame. When a message
22
- # is received for this subscription, the supplied +block+ is
23
- # called with the received message as its argument.
24
- #
25
- # Examples:
26
- #
27
- # client.subscribe("/queue/test") { |msg| puts "Got message: #{msg.body}" }
28
- #
29
- # client.subscribe("/queue/test", :ack => 'client', 'id' => 'subscription-001') do |msg|
30
- # puts "Got message: #{msg.body}"
31
- # end
32
- #
33
- # client.subscribe("/queue/test", :selector => 'cost > 5') do |msg|
34
- # puts "Got message: #{msg.body}"
35
- # end
36
- #
37
- # See also: unsubscribe, Stomper::Subscription
38
- def subscribe(destination, headers={}, &block)
39
- unless destination.is_a?(Subscription)
40
- destination = Subscription.new(headers.merge(:destination => destination), &block)
41
- end
42
- subscriptions << destination
43
- transmit(destination.to_subscribe)
44
- destination
45
- end
46
-
47
- # Unsubscribes from the specified +destination+. The +destination+
48
- # parameter may be either a string, such as "/queue/test", or Stomper::Subscription
49
- # object. If the optional +sub_id+ is supplied, the client will unsubscribe
50
- # from the subscription with an id matching +sub_id+, regardless if the
51
- # +destination+ parameter matches that of the registered subscription. For
52
- # this reason, it is vital that subscription ids, if manually specified, be
53
- # unique.
54
- #
55
- # Examples:
56
- #
57
- # client.unsubscribe("/queue/test")
58
- # # unsubscribes from all "naive" subscriptions for "/queue/test"
59
- #
60
- # client.unsubscribe("/queue/does/not/matter", "sub-0013012031")
61
- # # unsubscribes from all subscriptions with id of "sub-0013012031"
62
- #
63
- # client.unsubscribe(some_subscription)
64
- #
65
- # See also: subscribe, Stomper::Subscription
66
- def unsubscribe(destination, sub_id=nil)
67
- subscriptions.remove(destination, sub_id).each do |unsub|
68
- transmit(unsub.to_unsubscribe)
69
- end
70
- end
71
-
72
- def subscriptions
73
- @subscriptions ||= ::Stomper::Subscriptions.new
74
- end
75
- end
76
- end