zack 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -0
- data/README +1 -1
- data/Rakefile +1 -1
- data/example/client.rb +2 -1
- data/example/server.rb +1 -2
- data/lib/zack.rb +0 -3
- data/lib/zack/client.rb +60 -9
- data/lib/zack/server.rb +97 -11
- data/spec/acceptance/exception_handling_spec.rb +78 -0
- data/spec/{integration → acceptance}/regression_spec.rb +9 -8
- data/spec/{integration → acceptance}/server_runner_spec.rb +11 -0
- data/spec/lib/zack/server_spec.rb +4 -3
- data/spec/spec_helper.rb +2 -0
- metadata +21 -22
- data/example/pubsub.rb +0 -35
- data/lib/zack/target.rb +0 -68
- data/lib/zack/transparent_proxy.rb +0 -22
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
= 0.3.3 / 29Nov2011
|
2
|
+
+ Updates to the cod rewrite, this eliminates the dependency on
|
3
|
+
beanstalk-client.
|
4
|
+
|
5
|
+
+ Works and is designed only for beanstalk now. This allows usage of some
|
6
|
+
advanced queuing features.
|
7
|
+
|
8
|
+
+ Breaking change: Default server is now 'localhost:11300', not a server on
|
9
|
+
the LAN we might not even own.
|
10
|
+
|
11
|
+
+ Breaking change: Exception block now has two arguments.
|
12
|
+
|
1
13
|
= 0.3.2 / 31Aug2011
|
2
14
|
|
3
15
|
. gemspec fix
|
data/README
CHANGED
data/Rakefile
CHANGED
data/example/client.rb
CHANGED
data/example/server.rb
CHANGED
data/lib/zack.rb
CHANGED
data/lib/zack/client.rb
CHANGED
@@ -12,21 +12,19 @@ module Zack
|
|
12
12
|
# :server :: beanstalkd server location url
|
13
13
|
# :only :: ignores all messages not in this hash
|
14
14
|
# :with_answer :: these messages wait for an answer from the service
|
15
|
+
# :timeout :: How long to wait for an answer
|
15
16
|
#
|
16
17
|
def initialize(tube_name, opts={})
|
17
|
-
server = opts[:server] || 'beanstalk:11300'
|
18
18
|
# Only respond to these messages
|
19
19
|
@only = opts[:only] || proc { true }
|
20
20
|
# These have answers (wait for the server to answer)
|
21
21
|
@with_answer = opts[:with_answer] || []
|
22
|
+
@timeout = opts[:timeout]
|
23
|
+
|
24
|
+
@tube_name = tube_name
|
25
|
+
@server = opts[:server] || 'localhost:11300'
|
22
26
|
|
23
|
-
|
24
|
-
unless @with_answer.empty?
|
25
|
-
@incoming = Cod.beanstalk(server,
|
26
|
-
UniqueName.new(tube_name))
|
27
|
-
end
|
28
|
-
|
29
|
-
@service = Cod::Client.new(@outgoing, @incoming, 1)
|
27
|
+
connect
|
30
28
|
end
|
31
29
|
|
32
30
|
def respond_to?(msg)
|
@@ -37,6 +35,59 @@ module Zack
|
|
37
35
|
@with_answer.include?(sym.to_sym)
|
38
36
|
end
|
39
37
|
|
40
|
-
|
38
|
+
def method_missing(sym, *args, &block)
|
39
|
+
super unless respond_to?(sym)
|
40
|
+
|
41
|
+
raise ArgumentError, "Can't call methods remotely with a block" if block
|
42
|
+
|
43
|
+
if has_answer?(sym)
|
44
|
+
with_timeout do
|
45
|
+
return service.call([sym, args])
|
46
|
+
end
|
47
|
+
else
|
48
|
+
service.notify [sym, args]
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
rescue Timeout::Error => ex
|
52
|
+
raise Zack::ServiceTimeout, "The service took too long to answer (>#{@timeout || 1}s)."
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
@service.close
|
57
|
+
end
|
58
|
+
private
|
59
|
+
def with_timeout
|
60
|
+
if @timeout
|
61
|
+
begin
|
62
|
+
timeout(@timeout) do
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
rescue Timeout::Error
|
66
|
+
# The timeout might have occurred at any place at all. This means
|
67
|
+
# that the connection is probably in an invalid state at this point.
|
68
|
+
reconnect
|
69
|
+
raise Zack::ServiceTimeout,
|
70
|
+
"The server took longer than #{@timeout} seconds to respond."
|
71
|
+
end
|
72
|
+
else
|
73
|
+
yield
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def reconnect
|
78
|
+
@service.close
|
79
|
+
connect
|
80
|
+
end
|
81
|
+
def connect
|
82
|
+
@outgoing = Cod.beanstalk(@tube_name, @server)
|
83
|
+
|
84
|
+
unless @with_answer.empty?
|
85
|
+
@incoming = Cod.beanstalk(
|
86
|
+
UniqueName.new(@tube_name),
|
87
|
+
@server)
|
88
|
+
end
|
89
|
+
|
90
|
+
@service = @outgoing.client(@incoming)
|
91
|
+
end
|
41
92
|
end
|
42
93
|
end
|
data/lib/zack/server.rb
CHANGED
@@ -2,22 +2,108 @@
|
|
2
2
|
module Zack
|
3
3
|
# Server side for RPC calls.
|
4
4
|
#
|
5
|
-
class Server
|
5
|
+
class Server
|
6
6
|
attr_reader :service
|
7
|
-
|
7
|
+
attr_reader :factory
|
8
|
+
attr_reader :server
|
9
|
+
|
10
|
+
# Initializes a zack server. To specify which class should be the target
|
11
|
+
# of the RPC call, you must either give the :factory or the :simple
|
12
|
+
# argument.
|
13
|
+
#
|
14
|
+
# :simple expects a class. This class will be constructed each time a
|
15
|
+
# request is made. Then the method will be called on the class.
|
16
|
+
#
|
17
|
+
# :factory expects a callable (a block or something that has #call) and is
|
18
|
+
# passed the control object for the request (see Cod for an explanation of
|
19
|
+
# this). You can chose to ignore the control and just use the block to
|
20
|
+
# produce an object that is linked to the rest of your program. Or you can
|
21
|
+
# link to the rest of the program and the control at the same time.
|
22
|
+
#
|
23
|
+
# Note that in any case, one object instance _per call_ is created. This
|
24
|
+
# is to discourage creating stateful servers. If you still want to do
|
25
|
+
# that, well you will just have to code around the limitation, now won't
|
26
|
+
# you.
|
27
|
+
#
|
8
28
|
def initialize(tube_name, opts={})
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
29
|
+
@server = opts[:server]
|
30
|
+
|
31
|
+
if opts.has_key? :factory
|
32
|
+
@factory = opts[:factory]
|
33
|
+
elsif opts.has_key? :simple
|
34
|
+
klass = opts[:simple]
|
35
|
+
@factory = lambda { |ctl| klass.new }
|
36
|
+
else
|
37
|
+
raise ArgumentError, "Either :factory or :simple argument must be given."
|
38
|
+
end
|
39
|
+
|
40
|
+
channel = Cod.beanstalk(tube_name, server)
|
41
|
+
@service = channel.service
|
42
|
+
end
|
43
|
+
|
15
44
|
# Handles exactly one request.
|
16
45
|
#
|
17
|
-
def handle_request
|
18
|
-
service.one { |(sym, args)|
|
19
|
-
|
46
|
+
def handle_request(exception_handler=nil)
|
47
|
+
service.one { |(sym, args), control|
|
48
|
+
exception_handling(exception_handler, control) do
|
49
|
+
# p [sym, args]
|
50
|
+
process_request(control, sym, args)
|
51
|
+
end
|
20
52
|
}
|
21
53
|
end
|
54
|
+
|
55
|
+
# Processes exactly one request, but doesn't define how the request gets
|
56
|
+
# here.
|
57
|
+
#
|
58
|
+
def process_request(control, sym, args)
|
59
|
+
instance = factory.call(control)
|
60
|
+
|
61
|
+
instance.send(sym, *args)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Runs the server and keeps running until the world ends (or the process,
|
65
|
+
# whichever comes first). If you pass a non-nil messages argument, the
|
66
|
+
# server will process that many messages and then quit. (Maybe you will
|
67
|
+
# want to respawn the server from time to time?)
|
68
|
+
#
|
69
|
+
# Any exception that is raised inside the RPC code will be passed to the
|
70
|
+
# exception_handler block:
|
71
|
+
#
|
72
|
+
# server.run do |exception, control|
|
73
|
+
# # control is the service control object from cod. You can exercise
|
74
|
+
# # fine grained message control using this.
|
75
|
+
# log.fatal exception
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# If you don't reraise exceptions from the exception handler block, they
|
79
|
+
# will be caught and the server will stay running.
|
80
|
+
#
|
81
|
+
def run(messages=nil, &exception_handler)
|
82
|
+
loop do
|
83
|
+
handle_request(exception_handler)
|
84
|
+
|
85
|
+
if messages
|
86
|
+
messages -= 1
|
87
|
+
break if messages <= 0
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
# Defines how the server handles exception.
|
94
|
+
#
|
95
|
+
def exception_handling(exception_handler, control)
|
96
|
+
begin
|
97
|
+
yield
|
98
|
+
rescue => exception
|
99
|
+
# If we have an exception handler, it gets handed all the exceptions.
|
100
|
+
# No exceptions stop the operation.
|
101
|
+
if exception_handler
|
102
|
+
exception_handler.call(exception, control)
|
103
|
+
else
|
104
|
+
raise
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
22
108
|
end
|
23
109
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Exception handling" do
|
4
|
+
# Clear old messages (test isolation)
|
5
|
+
before(:each) {
|
6
|
+
conn = Beanstalk::Connection.new(BEANSTALK_CONNECTION, 'exceptions')
|
7
|
+
while conn.peek_ready
|
8
|
+
conn.reserve.delete
|
9
|
+
end
|
10
|
+
conn.close
|
11
|
+
}
|
12
|
+
|
13
|
+
SimpleService = Struct.new(:control, :retry_log) do
|
14
|
+
# Used for the exception block specs
|
15
|
+
def do_something
|
16
|
+
fail
|
17
|
+
end
|
18
|
+
|
19
|
+
# Used for the handler specs
|
20
|
+
def retry_message
|
21
|
+
retries = (retry_log[control.msg_id] += 1)
|
22
|
+
if retries < 2
|
23
|
+
control.retry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let!(:retry_log) { Hash.new(0) }
|
29
|
+
|
30
|
+
let(:client) { Zack::Client.new('exceptions',
|
31
|
+
server: BEANSTALK_CONNECTION) }
|
32
|
+
let(:server) { Zack::Server.new('exceptions',
|
33
|
+
server: BEANSTALK_CONNECTION,
|
34
|
+
factory: proc { |c| SimpleService.new(c, retry_log) }) }
|
35
|
+
|
36
|
+
describe 'block given to #run' do
|
37
|
+
it "can retry the message" do
|
38
|
+
client.do_something
|
39
|
+
|
40
|
+
retried = false
|
41
|
+
server.run(2) do |exception, control|
|
42
|
+
retried ? control.delete : control.retry
|
43
|
+
retried = true
|
44
|
+
end
|
45
|
+
|
46
|
+
retried.should == true
|
47
|
+
end
|
48
|
+
it "can use msg_id to retry n times" do
|
49
|
+
client.do_something
|
50
|
+
client.do_something
|
51
|
+
|
52
|
+
retry_per_msg = Hash.new(0)
|
53
|
+
|
54
|
+
server.run(4) do |exception, control|
|
55
|
+
retries = (retry_per_msg[control.msg_id] += 1)
|
56
|
+
retries < 2 ? control.retry : control.delete
|
57
|
+
end
|
58
|
+
|
59
|
+
retry_per_msg.should have(2).messages
|
60
|
+
retry_per_msg.each do |msg_id, retries|
|
61
|
+
msg_id.should >0
|
62
|
+
retries.should == 2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
describe 'using the control parameter to the factory' do
|
67
|
+
it "can retry the message from within the handler" do
|
68
|
+
client.retry_message
|
69
|
+
server.run(2)
|
70
|
+
|
71
|
+
retry_log.should have(1).message
|
72
|
+
|
73
|
+
msg_id, retries = retry_log.first
|
74
|
+
msg_id.should > 0
|
75
|
+
retries.should == 2
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -32,7 +32,8 @@ describe "Regression: " do
|
|
32
32
|
'regression',
|
33
33
|
:timeout => timeout,
|
34
34
|
:server => BEANSTALK_CONNECTION,
|
35
|
-
:with_answer => with_answer)
|
35
|
+
:with_answer => with_answer).tap { |client|
|
36
|
+
self.class.after(:each) { client.close } }
|
36
37
|
end
|
37
38
|
|
38
39
|
def print_stats
|
@@ -50,7 +51,7 @@ describe "Regression: " do
|
|
50
51
|
connection.close
|
51
52
|
end
|
52
53
|
|
53
|
-
describe "
|
54
|
+
describe "long running message, followed by a short running one (bug)" do
|
54
55
|
class Regression1Server
|
55
56
|
def reader; 42; end
|
56
57
|
def long_running; sleep 0.1 end
|
@@ -98,11 +99,11 @@ describe "Regression: " do
|
|
98
99
|
end
|
99
100
|
|
100
101
|
it "should timeout a blocking call" do
|
101
|
-
|
102
|
-
|
102
|
+
expect {
|
103
|
+
expect {
|
103
104
|
client.crash_and_burn
|
104
|
-
}.
|
105
|
-
}.
|
105
|
+
}.not_to take_long
|
106
|
+
}.to raise_error(Zack::ServiceTimeout)
|
106
107
|
end
|
107
108
|
end
|
108
109
|
describe "server that takes a long time, timeout in client stops the operation" do
|
@@ -119,7 +120,7 @@ describe "Regression: " do
|
|
119
120
|
it "should pass a sanity check" do
|
120
121
|
client.long_running(0, 42).should == 42
|
121
122
|
end
|
122
|
-
context "
|
123
|
+
context "message ordering" do
|
123
124
|
before(:each) {
|
124
125
|
begin
|
125
126
|
client.long_running(1.1, 10)
|
@@ -127,7 +128,7 @@ describe "Regression: " do
|
|
127
128
|
end
|
128
129
|
}
|
129
130
|
|
130
|
-
it "should
|
131
|
+
it "should be preserved" do
|
131
132
|
client.long_running(0, 42).should == 42
|
132
133
|
end
|
133
134
|
end
|
@@ -10,6 +10,17 @@ describe "Server#run" do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
# Isolates the specs by clearing the 'server_run' tube before use.
|
14
|
+
before(:each) {
|
15
|
+
conn = Beanstalk::Connection.new(BEANSTALK_CONNECTION, 'server_run')
|
16
|
+
|
17
|
+
while conn.peek_ready
|
18
|
+
conn.reserve.delete
|
19
|
+
end
|
20
|
+
|
21
|
+
conn.close
|
22
|
+
}
|
23
|
+
|
13
24
|
let(:server) { Zack::Server.new(
|
14
25
|
'server_run',
|
15
26
|
:simple => RunServerRun,
|
@@ -10,6 +10,7 @@ describe Zack::Server do
|
|
10
10
|
end
|
11
11
|
context "instance" do
|
12
12
|
let(:implementation) { flexmock(:implementation) }
|
13
|
+
let(:control) { flexmock(:control) }
|
13
14
|
|
14
15
|
# Replacing the Cod::Service with a mock
|
15
16
|
let(:service) { flexmock(:service) }
|
@@ -18,7 +19,7 @@ describe Zack::Server do
|
|
18
19
|
context "with a factory" do
|
19
20
|
# A small factory that always returns instance.
|
20
21
|
class ImplFactory < Struct.new(:instance)
|
21
|
-
def call
|
22
|
+
def call(control)
|
22
23
|
instance
|
23
24
|
end
|
24
25
|
end
|
@@ -37,7 +38,7 @@ describe Zack::Server do
|
|
37
38
|
|
38
39
|
it "should call the right message on implementation" do
|
39
40
|
service.should_receive(:one).
|
40
|
-
|
41
|
+
yields([:foobar, [123, '123', :a123]], control)
|
41
42
|
|
42
43
|
implementation.
|
43
44
|
should_receive(:foobar).
|
@@ -64,7 +65,7 @@ describe Zack::Server do
|
|
64
65
|
|
65
66
|
it "should call the right message on implementation" do
|
66
67
|
service.should_receive(:one).
|
67
|
-
|
68
|
+
yields([:foobar, [123, '123', :a123]], control)
|
68
69
|
|
69
70
|
implementation.
|
70
71
|
should_receive(:foobar).
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,42 +10,41 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-11-29 00:00:00.000000000Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: cod
|
18
|
-
requirement: &
|
17
|
+
requirement: &70152213379200 !ruby/object:Gem::Requirement
|
19
18
|
none: false
|
20
19
|
requirements:
|
21
20
|
- - ~>
|
22
21
|
- !ruby/object:Gem::Version
|
23
|
-
version: '0.
|
22
|
+
version: '0.4'
|
24
23
|
type: :runtime
|
25
24
|
prerelease: false
|
26
|
-
version_requirements: *
|
25
|
+
version_requirements: *70152213379200
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
29
|
-
requirement: &
|
27
|
+
name: uuid
|
28
|
+
requirement: &70152213378720 !ruby/object:Gem::Requirement
|
30
29
|
none: false
|
31
30
|
requirements:
|
32
31
|
- - ~>
|
33
32
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
33
|
+
version: '2.3'
|
35
34
|
type: :runtime
|
36
35
|
prerelease: false
|
37
|
-
version_requirements: *
|
36
|
+
version_requirements: *70152213378720
|
38
37
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
40
|
-
requirement: &
|
38
|
+
name: beanstalk-client
|
39
|
+
requirement: &70152213378240 !ruby/object:Gem::Requirement
|
41
40
|
none: false
|
42
41
|
requirements:
|
43
42
|
- - ~>
|
44
43
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
46
|
-
type: :
|
44
|
+
version: '1.0'
|
45
|
+
type: :development
|
47
46
|
prerelease: false
|
48
|
-
version_requirements: *
|
47
|
+
version_requirements: *70152213378240
|
49
48
|
description:
|
50
49
|
email:
|
51
50
|
- kaspar.schiess@absurd.li
|
@@ -61,19 +60,16 @@ files:
|
|
61
60
|
- README
|
62
61
|
- lib/zack/client.rb
|
63
62
|
- lib/zack/server.rb
|
64
|
-
- lib/zack/target.rb
|
65
|
-
- lib/zack/transparent_proxy.rb
|
66
63
|
- lib/zack/unique_name.rb
|
67
64
|
- lib/zack.rb
|
68
|
-
- spec/
|
69
|
-
- spec/
|
65
|
+
- spec/acceptance/exception_handling_spec.rb
|
66
|
+
- spec/acceptance/regression_spec.rb
|
67
|
+
- spec/acceptance/server_runner_spec.rb
|
70
68
|
- spec/lib/zack/client_spec.rb
|
71
69
|
- spec/lib/zack/server_spec.rb
|
72
70
|
- spec/spec_helper.rb
|
73
71
|
- example/client.rb
|
74
|
-
- example/pubsub.rb
|
75
72
|
- example/server.rb
|
76
|
-
has_rdoc: true
|
77
73
|
homepage: http://github.com/kschiess/zack
|
78
74
|
licenses: []
|
79
75
|
post_install_message:
|
@@ -88,6 +84,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
84
|
- - ! '>='
|
89
85
|
- !ruby/object:Gem::Version
|
90
86
|
version: '0'
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
hash: 2009193566488392526
|
91
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
91
|
none: false
|
93
92
|
requirements:
|
@@ -96,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
95
|
version: '0'
|
97
96
|
requirements: []
|
98
97
|
rubyforge_project:
|
99
|
-
rubygems_version: 1.
|
98
|
+
rubygems_version: 1.8.10
|
100
99
|
signing_key:
|
101
100
|
specification_version: 3
|
102
101
|
summary: Ruby RPC calls via Cod
|
data/example/pubsub.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
|
2
|
-
class Handler
|
3
|
-
def foo
|
4
|
-
puts "Foo was called."
|
5
|
-
end
|
6
|
-
def bar
|
7
|
-
Process.pid
|
8
|
-
end
|
9
|
-
def shutdown
|
10
|
-
exit 0
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
source = Zack::Notifier.new(
|
15
|
-
'football',
|
16
|
-
server: 'localhost:11300',
|
17
|
-
with_answer: [:bar])
|
18
|
-
|
19
|
-
%w(foo bar).each do |filter|
|
20
|
-
fork do
|
21
|
-
handler = Zack::Listener.new(
|
22
|
-
'football',
|
23
|
-
simple: Handler,
|
24
|
-
server: 'localhost:11300')
|
25
|
-
|
26
|
-
handler.run
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
source.foo
|
31
|
-
p source.bar
|
32
|
-
|
33
|
-
source.shutdown
|
34
|
-
|
35
|
-
Process.waitall
|
data/lib/zack/target.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
|
2
|
-
module Zack
|
3
|
-
# Abstract base class for everything that is an RPC target. This implements
|
4
|
-
# some common mechanisms like a run loop, exception handling and argument
|
5
|
-
# handling.
|
6
|
-
#
|
7
|
-
class Target
|
8
|
-
attr_reader :factory
|
9
|
-
attr_reader :server
|
10
|
-
|
11
|
-
# Initializes #factory and #server.
|
12
|
-
#
|
13
|
-
def initialize(tube_name, opts={})
|
14
|
-
@server = opts[:server] || 'beanstalk:11300'
|
15
|
-
|
16
|
-
if opts.has_key? :factory
|
17
|
-
@factory = opts[:factory]
|
18
|
-
elsif opts.has_key? :simple
|
19
|
-
klass = opts[:simple]
|
20
|
-
@factory = lambda { klass.new }
|
21
|
-
else
|
22
|
-
raise ArgumentError, "Either :factory or :simple argument must be given."
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Processes exactly one request, but doesn't define how the request gets
|
27
|
-
# here.
|
28
|
-
#
|
29
|
-
def process_request(sym, args)
|
30
|
-
instance = factory.call
|
31
|
-
|
32
|
-
instance.send(sym, *args)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Handles one request. This is specific to implementors.
|
36
|
-
#
|
37
|
-
def handle_request
|
38
|
-
raise NotImplementedError,
|
39
|
-
"Abstract base class doesn't implement #handle_request."
|
40
|
-
end
|
41
|
-
|
42
|
-
# Runs the server and keeps running until the world ends (or the process,
|
43
|
-
# whichever comes first).
|
44
|
-
#
|
45
|
-
def run(&block)
|
46
|
-
loop do
|
47
|
-
exception_handling(block) do
|
48
|
-
handle_request
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
# Defines how the server handles exception.
|
55
|
-
#
|
56
|
-
def exception_handling(exception_handler)
|
57
|
-
if exception_handler
|
58
|
-
begin
|
59
|
-
yield
|
60
|
-
rescue => exception
|
61
|
-
exception_handler.call(exception)
|
62
|
-
end
|
63
|
-
else
|
64
|
-
yield
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
|
2
|
-
# A method missing implementation that will use respond_to? to see wether a
|
3
|
-
# message should be answered. If yes, it delegates the message to service,
|
4
|
-
# which is supposed to return one of Cods RPC client primitives. Depending on
|
5
|
-
# the value of has_answer?(symbol), either #call or #notify is used.
|
6
|
-
#
|
7
|
-
module Zack::TransparentProxy
|
8
|
-
def method_missing(sym, *args, &block)
|
9
|
-
super unless respond_to?(sym)
|
10
|
-
|
11
|
-
raise ArgumentError, "Can't call methods remotely with a block" if block
|
12
|
-
|
13
|
-
if has_answer?(sym)
|
14
|
-
return service.call([sym, args])
|
15
|
-
else
|
16
|
-
service.notify [sym, args]
|
17
|
-
return nil
|
18
|
-
end
|
19
|
-
rescue Cod::Channel::TimeoutError
|
20
|
-
raise Zack::ServiceTimeout, "No response from server in the allowed time."
|
21
|
-
end
|
22
|
-
end
|