zack 0.3.2 → 0.3.3
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.
- 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
|