fluffle 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 122f80facd1bb3cf22f70ff33546da4ae228f72e
4
- data.tar.gz: b7939ddcb8bfc94962cea7d6e2b6257afe29eb73
3
+ metadata.gz: 23e4f19afd95059c202ab4bc0af6272d02a7c9d0
4
+ data.tar.gz: 7ebfc0c7c29f5a947413d870e7652b745636a57a
5
5
  SHA512:
6
- metadata.gz: a90deb17fc848e89a176a47a27050ee30a60b5b59746c149cbdb11b8da025cfca8735e55336c640a968be663e1b73a584663813f2b18486784c34bd05587f0e3
7
- data.tar.gz: d8b0d2d9226d1a5f2e9611c4f448161d30a33f1bb3e38f1458e64e62264678d4cfc6b9aa53b0bc6f059dde5a80d27693efa32f52b32985214b8169063d7c7717
6
+ metadata.gz: b0ce2b422e356596c1dad872f56ce106360f82e0c5079f3372817a586af11de7a0c0409ed6578f43b8ca21b8837b12c33b69469cd20c971e33b98ccb2023355d
7
+ data.tar.gz: 80c7da2ff18ece5e1754a36a595cff627c0f925d84b2c652c352064e891ff52c2209c5eee56f795ee00bc748fbbb78270bbb4ec9de719f1c860b97558c5fa3c2
data/examples/client.rb CHANGED
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  require 'fluffle'
5
5
 
6
- client = Fluffle::Client.new url: 'amqp://localhost'
6
+ client = Fluffle::Client.new url: 'amqp://localhost', confirms: true
7
7
  # You can also pass `connection:` to use an existing Bunny connection:
8
8
  # Fluffle::Client.new(connection: Bunny.new('amqp://localhost', heartbeat: 2))
9
9
 
@@ -9,12 +9,14 @@ module Fluffle
9
9
  class Client
10
10
  include Connectable
11
11
 
12
+ attr_reader :confirms
12
13
  attr_accessor :default_timeout
13
14
  attr_accessor :logger
14
15
 
15
- def initialize(url: nil, connection: nil)
16
+ def initialize(url: nil, connection: nil, confirms: false)
16
17
  self.connect(url || connection)
17
18
 
19
+ @confirms = confirms
18
20
  @default_timeout = 5
19
21
  @logger = Fluffle.logger
20
22
 
@@ -26,9 +28,13 @@ module Fluffle
26
28
  # Used for generating unique message IDs
27
29
  @prng = Random.new
28
30
 
29
- @pending_responses = Concurrent::Map.new
31
+ if confirms
32
+ @pending_confirms = Concurrent::Map.new
33
+ confirm_select
34
+ end
30
35
 
31
- self.subscribe
36
+ @pending_responses = Concurrent::Map.new
37
+ subscribe
32
38
  end
33
39
 
34
40
  def subscribe
@@ -45,6 +51,22 @@ module Fluffle
45
51
  end
46
52
  end
47
53
 
54
+ def confirm_select
55
+ handle_confirm = ->(tag, _multiple, nack) do
56
+ ivar = @pending_confirms.delete tag
57
+
58
+ if ivar
59
+ ivar.set nack
60
+ else
61
+ self.logger.error "Missing confirm IVar: tag=#{tag}"
62
+ end
63
+ end
64
+
65
+ # Set the channel in confirmation mode so that we can receive confirms
66
+ # of published messages
67
+ @channel.confirm_select handle_confirm
68
+ end
69
+
48
70
  # Fetch and set the `IVar` with a response from the server. This method is
49
71
  # called from the reply queue's background thread; the main thread will
50
72
  # normally be waiting for the `IVar` to be set.
@@ -102,25 +124,57 @@ module Fluffle
102
124
  def publish_and_wait(payload, queue:, timeout:)
103
125
  id = payload['id']
104
126
 
105
- ivar = Concurrent::IVar.new
106
- @pending_responses[id] = ivar
107
-
108
- self.publish payload, queue: queue
109
-
110
- response = ivar.value timeout
127
+ response_ivar = Concurrent::IVar.new
128
+ @pending_responses[id] = response_ivar
111
129
 
112
- if ivar.incomplete?
113
- method = payload['method']
114
- arity = (payload['params'] && payload['params'].length) || 0
115
- raise Errors::TimeoutError.new("Timed out waiting for response to `#{method}/#{arity}'")
130
+ if confirms
131
+ with_confirmation(timeout: timeout) { publish payload, queue: queue }
132
+ else
133
+ publish payload, queue: queue
116
134
  end
117
135
 
136
+ response = response_ivar.value timeout
137
+ raise_incomplete(payload, 'response') if response_ivar.incomplete?
138
+
118
139
  return response
119
140
  ensure
120
141
  # Don't leak the `IVar` if it timed out
121
142
  @pending_responses.delete id
122
143
  end
123
144
 
145
+ # Returns a nice formatted description of a payload with its method name
146
+ # and arity
147
+ def describe_payload(payload)
148
+ method = payload['method']
149
+ arity = (payload['params'] && payload['params'].length) || 0
150
+
151
+ "#{method}/#{arity}"
152
+ end
153
+
154
+ # Wraps a block (which should publish a message) with a blocking check
155
+ # that the client received a confirmation from the RabbitMQ server
156
+ # that the message that was received and routed successfully
157
+ def with_confirmation(timeout:)
158
+ tag = @channel.next_publish_seq_no
159
+ confirm_ivar = Concurrent::IVar.new
160
+ @pending_confirms[tag] = confirm_ivar
161
+
162
+ yield
163
+
164
+ nack = confirm_ivar.value timeout
165
+ if confirm_ivar.incomplete?
166
+ raise_incomplete payload, 'confirm'
167
+ elsif nack
168
+ raise Errors::NackError.new('Received nack from confirmation')
169
+ end
170
+ end
171
+
172
+ # event_name - String describing what we timed out waiting for, should
173
+ # be 'response' or 'confirm'
174
+ def raise_incomplete(payload, event_name)
175
+ raise Errors::TimeoutError.new("Timed out waiting for #{event_name} to `#{describe_payload(payload)}'")
176
+ end
177
+
124
178
  def publish(payload, queue:)
125
179
  opts = {
126
180
  routing_key: Fluffle.request_queue_name(queue),
@@ -1,3 +1,3 @@
1
1
  module Fluffle
2
- VERSION = '0.3.1'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluffle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dirk Gadsden