Skip to content

Commit 592ff0b

Browse files
committed
Introduce ConnectionFactory
In the future we would like to use multiple threads. Every thread will obtain requests from separate `ZMQ` connection as it is recommended in the tutorial. Because of that we will have to create the sockets for every thread. For that reason `Handler` takes now in constructor a factory capable of creating new `Connection` instead of the `Connection` itself. `Connection#connection` method is introduced so that `Connection` has interface compatible with `ConnectionFactory` and can be used instead returning always `self` whenever `connection` is needed. That should work fine for simplest testing case or in single-threaded environment.
1 parent d5cf37b commit 592ff0b

12 files changed

Lines changed: 129 additions & 44 deletions

example/http_0mq.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ def process(request)
3939
pull_port = "tcp://127.0.0.1:9999"
4040
pub_port = "tcp://127.0.0.1:9998"
4141

42-
handler = Http0MQHandler.for(sender_id, pull_port, pub_port)
42+
handler = Http0MQHandler.new(M2R::ConnectionFactory.new(sender_id, pull_port, pub_port))
4343
handler.listen
4444

lib/m2r.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ def zmq_context(threads = 1)
1818
require 'm2r/request'
1919
require 'm2r/response'
2020
require 'm2r/connection'
21+
require 'm2r/connection_factory'
2122
require 'm2r/handler'

lib/m2r/connection.rb

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,19 @@
33
module M2R
44
class Connection
55

6-
def initialize(request_socket, response_socket)
6+
def initialize(request_socket, response_socket, request_parser = Request)
77
@request_socket = request_socket
88
@response_socket = response_socket
9+
@request_parser = request_parser
910
end
1011

11-
def self.for(sender_id, request_addr, response_addr, context = M2R.zmq_context)
12-
request_socket = context.socket(ZMQ::PULL)
13-
request_socket.connect(request_addr)
14-
15-
response_socket = context.socket(ZMQ::PUB)
16-
response_socket.connect(response_addr)
17-
response_socket.setsockopt(ZMQ::IDENTITY, sender_id)
18-
19-
new(request_socket, response_socket)
12+
def connection
13+
self
2014
end
2115

2216
def receive
2317
@request_socket.recv_string(msg = "")
24-
Request.parse(msg)
18+
@request_parser.parse(msg)
2519
end
2620

2721
def reply(request, response_or_string)

lib/m2r/connection_factory.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'm2r'
2+
3+
module M2R
4+
class ConnectionFactory
5+
6+
def initialize(sender_id, request_addr, response_addr, request_parser = Request, context = M2R.zmq_context)
7+
@sender_id = sender_id.to_s
8+
@request_addr = request_addr.to_s
9+
@response_addr = response_addr.to_s
10+
@request_parser = request_parser
11+
@context = context
12+
end
13+
14+
def connection
15+
request_socket = @context.socket(ZMQ::PULL)
16+
request_socket.connect(@request_addr)
17+
18+
response_socket = @context.socket(ZMQ::PUB)
19+
response_socket.connect(@response_addr)
20+
response_socket.setsockopt(ZMQ::IDENTITY, @sender_id)
21+
22+
Connection.new(request_socket, response_socket, @request_parser)
23+
end
24+
25+
end
26+
end

lib/m2r/handler.rb

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ module M2R
44
class Handler
55
attr_accessor :connection
66

7-
def initialize(connection)
8-
@connection = connection
9-
end
10-
11-
def self.for(sender_uuid, subscribe_address, publish_address)
12-
new(Connection.for(sender_uuid, subscribe_address, publish_address))
7+
def initialize(connection_factory)
8+
@connection = connection_factory.connection
139
end
1410

1511
# Callback for when the handler is waiting for a request

lib/m2r/rack_handler.rb

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@ module M2R
55
class RackHandler < Handler
66
attr_accessor :app
77

8-
def initialize(app, connection)
8+
def initialize(app, connection_factory)
99
@app = app
10-
super(connection)
10+
super(connection_factory)
1111

1212
trap('INT') { stop }
1313
end
1414

15-
def self.for(app, sender_uuid, subscribe_address, publish_address)
16-
new(app, Connection.for(sender_uuid, subscribe_address, publish_address))
17-
end
18-
1915
def process(request)
2016
script_name = request.pattern.split('(', 2).first.gsub(/\/$/, '')
2117

lib/rack/handler/mongrel2.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
require 'rack/handler'
22
require 'm2r/rack_handler'
33
require 'securerandom'
4+
require 'ostruct'
45

56
module Rack
67
module Handler
78
class Mongrel2
89
DEFAULT_OPTIONS = {
9-
:recv_addr => 'tcp://127.0.0.1:9997',
10-
:send_addr => 'tcp://127.0.0.1:9996',
11-
:sender_id => SecureRandom.uuid
10+
'recv_addr' => 'tcp://127.0.0.1:9997',
11+
'send_addr' => 'tcp://127.0.0.1:9996',
12+
'sender_id' => SecureRandom.uuid
1213
}
1314

1415
def self.run(app, options = {})
15-
options = DEFAULT_OPTIONS.merge(options)
16-
adapter = M2R::RackHandler.for(app, options[:sender_id], options[:recv_addr], options[:send_addr])
16+
options = OpenStruct.new( DEFAULT_OPTIONS.merge(options) )
17+
factory = M2R::ConnectionFactory.new(options.sender_id, options.recv_addr, options.send_addr)
18+
adapter = M2R::RackHandler.new(app, factory)
1719
adapter.listen
1820
end
1921

test/connection_factory_test.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'test_helper'
2+
require 'securerandom'
3+
4+
module M2R
5+
class ConnectionFactoryTest < MiniTest::Unit::TestCase
6+
def test_factory
7+
sender_id = "sid"
8+
request_addr = "req"
9+
response_addr = "req"
10+
request_parser = Object.new
11+
12+
pull = stub(:pull)
13+
pub = stub(:pub)
14+
context = stub(:context)
15+
16+
context.expects(:socket).with(ZMQ::PULL).returns(pull)
17+
context.expects(:socket).with(ZMQ::PUB).returns(pub)
18+
19+
pull.expects(:connect).with(request_addr)
20+
21+
pub.expects(:connect).with(response_addr)
22+
pub.expects(:setsockopt).with(ZMQ::IDENTITY, sender_id)
23+
24+
Connection.expects(:new).with(pull, pub, request_parser)
25+
cf = ConnectionFactory.new sender_id, request_addr, response_addr, request_parser, context
26+
cf.connection
27+
end
28+
end
29+
end

test/connection_test.rb

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,49 @@
11
require 'test_helper'
2+
require 'securerandom'
23

34
module M2R
45
class ConnectionTest < MiniTest::Unit::TestCase
5-
def test_receive_message
6-
request_addr = "inproc://requests"
7-
response_addr = "inproc://responses"
86

9-
push = M2R.zmq_context.socket(ZMQ::PUSH)
10-
assert_equal 0, push.bind(request_addr), "Could not bind push socket in tests"
7+
def setup
8+
@request_addr = "inproc://requests"
9+
@response_addr = "inproc://responses"
10+
11+
@push = M2R.zmq_context.socket(ZMQ::PUSH)
12+
assert_equal 0, @push.bind(@request_addr), "Could not bind push socket in tests"
13+
14+
@sub = M2R.zmq_context.socket(ZMQ::SUB)
15+
assert_equal 0, @sub.bind(@response_addr), "Could not bind sub socket in tests"
1116

12-
sub = M2R.zmq_context.socket(ZMQ::SUB)
13-
assert_equal 0, sub.bind(response_addr), "Could not bind sub socket in tests"
1417

15-
connection = Connection.for("a65c2d89-96ee-4bc9-971e-59b38ae24645", request_addr, response_addr)
18+
@request_socket = M2R.zmq_context.socket(ZMQ::PULL)
19+
@request_socket.connect(@request_addr)
20+
21+
@response_socket = M2R.zmq_context.socket(ZMQ::PUB)
22+
@response_socket.connect(@response_addr)
23+
@response_socket.setsockopt(ZMQ::IDENTITY, @sender_id = SecureRandom.uuid)
24+
end
1625

17-
push.send_string("1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
26+
def teardown
27+
@request_socket.close
28+
@response_socket.close
29+
@push.close
30+
@sub.close
31+
end
1832

33+
def test_receive_message
34+
connection = Connection.new(@request_socket, @response_socket)
35+
@push.send_string("1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
1936
assert_instance_of Request, connection.receive
2037
end
38+
39+
def test_different_parser
40+
msg = "1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,"
41+
parser = stub(:parser)
42+
parser.expects(:parse).with(msg).returns(request = Object.new)
43+
connection = Connection.new(@request_socket, @response_socket, parser)
44+
@push.send_string(msg = "1c5fd481-1121-49d8-a706-69127975db1a ebb407b2-49aa-48a5-9f96-9db121051484 / 2:{},0:,", ZMQ::NOBLOCK)
45+
assert_equal request, connection.receive
46+
end
47+
2148
end
2249
end

test/handler_test.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ module M2R
44
class HandlerTest < MiniTest::Unit::TestCase
55
def test_lifecycle_for_disconnect
66
connection = stub(:receive => disconnect_request)
7+
connection.stubs(:connection).returns(connection)
78
h = TestHandler.new(connection)
89
h.listen
910
assert_equal [:wait, :request, :disconnect], h.called_methods
1011
end
1112

1213
def test_lifecycle_for_upload_start
1314
connection = stub(:receive => upload_start_request)
15+
connection.stubs(:connection).returns(connection)
1416
h = TestHandler.new(connection)
1517
h.listen
1618
assert_equal [:wait, :request, :start], h.called_methods
1719
end
1820

1921
def test_lifecycle_for_upload_done
2022
connection = stub(:receive => upload_done_request, :reply => nil)
23+
connection.stubs(:connection).returns(connection)
2124
h = TestHandler.new(connection)
2225
h.listen
2326
assert_equal [:wait, :request, :done, :process, :after, :reply], h.called_methods

0 commit comments

Comments
 (0)