fluffle 0.0.3 → 0.1.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: 77c72df240211039c526d501f7f34af3bb36330c
4
- data.tar.gz: f71668dca91239e6b211be0239d43f5a72dd51d4
3
+ metadata.gz: a7267d1c8c0b56d933eb3e755e15541901aa4a5f
4
+ data.tar.gz: 0413ba9a5100acd9dbfef940c61f18ef8df4d0d6
5
5
  SHA512:
6
- metadata.gz: e195c331ce7a3817cef21c76f05f2253b46929b1355b4fad38c617c2b15ab53c7b05ff74c2a9c4296bf0ca5d375a0598b6c9bfcd5f19392595c00edb8dc25997
7
- data.tar.gz: d1c08745886f2031b8582890b1e28f09783e66b9f93d45e176b3707d8cb6012eabd46d70967bd2c58149b0d9007cc65c38f36949cfda72d15296660a42211e97
6
+ metadata.gz: 04044d6fab23dc039b32b1fba003f18c070c77d07eddb1785de5b44296af97299f99d9e48eb8d189e719c9316bbe8dd7dd1e20f9d2ff2318287ebde5aae19157
7
+ data.tar.gz: 9bea8e8e0029b92eb4f97cc56b4ca1f8612627ab88a6bb95cbea524a9cacaabd3c71c181677f675292594941af2ec0b2130a32dd1caa162880c4679ef0ed4629
@@ -0,0 +1,23 @@
1
+ lib = File.join File.dirname(__FILE__), '..', 'lib'
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'fluffle'
5
+ require 'fluffle/testing'
6
+
7
+ server = Fluffle::Server.new url: 'amqp://localhost'
8
+
9
+ server.drain do |dispatcher|
10
+ dispatcher.handle('foo') { 'bar' }
11
+ end
12
+
13
+ server.start
14
+
15
+ client = Fluffle::Client.new url: 'amqp://localhost'
16
+
17
+ timings = 10.times.map do
18
+ t0 = Time.now
19
+ client.call('foo').inspect
20
+ Time.now - t0
21
+ end
22
+
23
+ puts timings
@@ -83,16 +83,22 @@ module Fluffle
83
83
  end
84
84
  end
85
85
 
86
- protected
87
-
88
86
  # Publish a payload to the server and wait (block) for the response
89
87
  #
90
- # Returns a Hash from the parsed JSON response from the server
88
+ # It creates an `IVar` future for the response, stores that in
89
+ # `@pending_responses`, and then publishes the payload to the server.
90
+ # After publishing it waits for the `IVar` to be set with the response.
91
+ # It also clears that `IVar` if it times out to avoid leaking.
92
+ #
93
+ # Returns a Hash from the JSON response from the server
91
94
  # Raises TimeoutError if server failed to respond in time
92
95
  def publish_and_wait(payload, queue:, timeout:)
93
96
  id = payload['id']
94
97
 
95
- ivar = self.publish payload, queue: queue
98
+ ivar = Concurrent::IVar.new
99
+ @pending_responses[id] = ivar
100
+
101
+ self.publish payload, queue: queue
96
102
 
97
103
  response = ivar.value timeout
98
104
 
@@ -108,27 +114,18 @@ module Fluffle
108
114
  @pending_responses.delete id
109
115
  end
110
116
 
111
- # Create an `IVar` future for the response, store that in
112
- # `@pending_responses`, and finally publish the payload to the server
113
- #
114
- # Returns the `IVar`
115
117
  def publish(payload, queue:)
116
- id = payload['id']
117
-
118
118
  opts = {
119
119
  routing_key: Fluffle.request_queue_name(queue),
120
- correlation_id: id,
120
+ correlation_id: payload['id'],
121
121
  reply_to: @reply_queue.name
122
122
  }
123
123
 
124
- ivar = Concurrent::IVar.new
125
- @pending_responses[id] = ivar
126
-
127
124
  @exchange.publish Oj.dump(payload), opts
128
-
129
- ivar
130
125
  end
131
126
 
127
+ protected
128
+
132
129
  def random_bytes_as_hex(bytes)
133
130
  # Adapted from `SecureRandom.hex`
134
131
  @prng.bytes(bytes).unpack('H*')[0]
@@ -1,8 +1,6 @@
1
1
  module Fluffle
2
2
  class Server
3
- class << self
4
- attr_accessor :default_server
5
- end
3
+ include Connectable
6
4
 
7
5
  attr_reader :connection, :handlers
8
6
 
@@ -15,15 +13,8 @@ module Fluffle
15
13
  self.class.default_server ||= self
16
14
  end
17
15
 
18
- def connect(*args)
19
- self.stop if self.connected?
20
-
21
- @connection = Bunny.new *args
22
- @connection.start
23
- end
24
-
25
- def connected?
26
- @connection&.connected?
16
+ class << self
17
+ attr_accessor :default_server
27
18
  end
28
19
 
29
20
  def drain(queue: 'default', handler: nil, &block)
@@ -0,0 +1,134 @@
1
+ require 'concurrent'
2
+
3
+ module Fluffle
4
+ module Testing
5
+ # Patch in a new `#connect` method that injects the loopback
6
+ module Connectable
7
+ def self.included(klass)
8
+ klass.class_eval do
9
+ alias_method :original_connect, :connect
10
+
11
+ def connect(*args)
12
+ @connection = Loopback.instance.connection
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.inject_connectable
19
+ [Fluffle::Client, Fluffle::Server].each do |mod|
20
+ mod.include Connectable
21
+ end
22
+ end
23
+
24
+ # Fake RabbitMQ server presented through a subset of the `Bunny`
25
+ # library's interface
26
+ class Loopback
27
+ # Singleton server instance that lives in the process
28
+ def self.instance
29
+ @instance ||= self.new
30
+ end
31
+
32
+ def initialize
33
+ @queues = Concurrent::Map.new
34
+ end
35
+
36
+ def connection
37
+ Connection.new self
38
+ end
39
+
40
+ def add_queue_subscriber(queue_name, block)
41
+ subscribers = (@queues[queue_name] ||= Concurrent::Array.new)
42
+
43
+ subscribers << block
44
+ end
45
+
46
+ def publish(payload, opts)
47
+ queue_name = opts[:routing_key]
48
+ raise "Missing `:routing_key' in `#publish' opts" unless queue_name
49
+
50
+ delivery_info = nil
51
+
52
+ properties = {
53
+ reply_to: opts[:reply_to],
54
+ correlation_id: opts[:correlation_id]
55
+ }
56
+
57
+ subscribers = @queues[queue_name]
58
+
59
+ if subscribers.nil? || subscribers.empty?
60
+ $stderr.puts "No subscribers active for queue '#{queue_name}'"
61
+ return nil
62
+ end
63
+
64
+ subscribers.each do |subscriber|
65
+ Thread.new do
66
+ subscriber.call(delivery_info, properties, payload)
67
+ end
68
+ end
69
+ end
70
+
71
+ class Connection
72
+ def initialize(server)
73
+ @server = server
74
+ end
75
+
76
+ def create_channel
77
+ Channel.new(@server)
78
+ end
79
+ end
80
+
81
+ class Channel
82
+ def initialize(server)
83
+ @server = server
84
+ end
85
+
86
+ def default_exchange
87
+ @default_exchange ||= Exchange.new(@server)
88
+ end
89
+
90
+ def work_pool
91
+ @work_pool ||= WorkPool.new
92
+ end
93
+
94
+ def queue(name, **opts)
95
+ opts = opts.merge server: @server
96
+
97
+ Queue.new name, opts
98
+ end
99
+ end
100
+
101
+ class Exchange
102
+ def initialize(server)
103
+ @server = server
104
+ end
105
+
106
+ def publish(payload, opts)
107
+ @server.publish(payload, opts)
108
+ end
109
+ end
110
+
111
+ class Queue
112
+ attr_reader :name
113
+
114
+ def initialize(name, server:, **opts)
115
+ @name = name
116
+ @server = server
117
+ end
118
+
119
+ def subscribe(&block)
120
+ @server.add_queue_subscriber @name, block
121
+ end
122
+ end
123
+
124
+ class WorkPool
125
+ # No-op in testing
126
+ def join
127
+ end
128
+ end
129
+
130
+ end # class LoopbackServer
131
+ end # module Testing
132
+ end # module Fluffle
133
+
134
+ Fluffle::Testing.inject_connectable
@@ -1,3 +1,3 @@
1
1
  module Fluffle
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.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.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dirk Gadsden
@@ -123,6 +123,7 @@ files:
123
123
  - Rakefile
124
124
  - examples/client.rb
125
125
  - examples/server.rb
126
+ - examples/testing.rb
126
127
  - fluffle.gemspec
127
128
  - fluffle.jpg
128
129
  - lib/fluffle.rb
@@ -133,6 +134,7 @@ files:
133
134
  - lib/fluffle/handlers/delegator.rb
134
135
  - lib/fluffle/handlers/dispatcher.rb
135
136
  - lib/fluffle/server.rb
137
+ - lib/fluffle/testing.rb
136
138
  - lib/fluffle/version.rb
137
139
  - spec/client_spec.rb
138
140
  - spec/server_spec.rb