fluffle 0.0.3 → 0.1.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.
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