marilyn-rpc 0.0.2 → 0.0.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/README.md +39 -4
- data/examples/async/client.rb +0 -5
- data/examples/authentication/client.rb +18 -0
- data/examples/authentication/server.rb +30 -0
- data/lib/marilyn-rpc.rb +1 -1
- data/lib/marilyn-rpc/client.rb +21 -3
- data/lib/marilyn-rpc/error.rb +13 -0
- data/lib/marilyn-rpc/mails.rb +3 -3
- data/lib/marilyn-rpc/server.rb +6 -11
- data/lib/marilyn-rpc/service.rb +86 -11
- data/lib/marilyn-rpc/service_cache.rb +44 -23
- data/lib/marilyn-rpc/version.rb +1 -1
- data/spec/mails_spec.rb +2 -1
- data/spec/server_spec.rb +1 -1
- data/spec/service_cache_spec.rb +140 -0
- data/spec/service_spec.rb +56 -5
- data/spec/spec_helper.rb +5 -0
- metadata +10 -34
- data/doc/MarilynRPC.html +0 -108
- data/doc/MarilynRPC/CallRequestMail.html +0 -578
- data/doc/MarilynRPC/CallResponseMail.html +0 -418
- data/doc/MarilynRPC/Envelope.html +0 -705
- data/doc/MarilynRPC/ExceptionMail.html +0 -338
- data/doc/MarilynRPC/Gentleman.html +0 -658
- data/doc/MarilynRPC/MailFactory.html +0 -284
- data/doc/MarilynRPC/MailHelper.html +0 -489
- data/doc/MarilynRPC/NativeClient.html +0 -579
- data/doc/MarilynRPC/NativeClientProxy.html +0 -303
- data/doc/MarilynRPC/Server.html +0 -406
- data/doc/MarilynRPC/Service.html +0 -599
- data/doc/MarilynRPC/ServiceCache.html +0 -481
- data/doc/_index.html +0 -219
- data/doc/class_list.html +0 -36
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -53
- data/doc/css/style.css +0 -318
- data/doc/file.README.html +0 -154
- data/doc/file_list.html +0 -38
- data/doc/frames.html +0 -13
- data/doc/index.html +0 -154
- data/doc/js/app.js +0 -203
- data/doc/js/full_list.js +0 -149
- data/doc/js/jquery.js +0 -16
- data/doc/method_list.html +0 -467
- data/doc/top-level-namespace.html +0 -88
- data/spec/service_cache.rb +0 -45
data/README.md
CHANGED
@@ -9,10 +9,7 @@ The services are unique per connection, so if you have 50 connections, 50
|
|
9
9
|
service objects will be used, if (and only if) they are requested by the client.
|
10
10
|
|
11
11
|
Since this is a session dedicated to one connection, marilyn has support for per
|
12
|
-
connection caching by using instance variables.
|
13
|
-
enhance the capabilities of marilyn to allow connection based authentication.
|
14
|
-
Like in other protocols (e.g. IMAP) where some methods can be called
|
15
|
-
unauthenticated and some not.
|
12
|
+
connection caching by simply using instance variables.
|
16
13
|
|
17
14
|
Like in IMAP marilyn supports sending of multiple requests to a server over one
|
18
15
|
connection. This feature is called multiplexing supported by the current
|
@@ -122,6 +119,44 @@ The client also simply has to enable a secure connection:
|
|
122
119
|
|
123
120
|
client = MarilynRPC::NativeClient.connect_tcp('localhost', 8008, :secure => true)
|
124
121
|
|
122
|
+
## Authentication
|
123
|
+
|
124
|
+
If some remote service methods only should be called using a username/password
|
125
|
+
protection, one simply has to define which method and what mechanism:
|
126
|
+
|
127
|
+
MarilynRPC::Service.authenticate_with do |username, password|
|
128
|
+
username == "testuserid" && password == "secret"
|
129
|
+
end
|
130
|
+
|
131
|
+
class TestService < MarilynRPC::Service
|
132
|
+
register :test
|
133
|
+
authentication_required :add
|
134
|
+
|
135
|
+
def time # unauthenticated
|
136
|
+
puts session_username
|
137
|
+
puts session_authenticated?
|
138
|
+
Time.now
|
139
|
+
end
|
140
|
+
|
141
|
+
def add(a, b) # authenticated
|
142
|
+
puts session_username
|
143
|
+
puts session_authenticated?
|
144
|
+
a + b
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
The client simple has to do the authentication before start marking calls:
|
149
|
+
|
150
|
+
client = MarilynRPC::NativeClient.connect_tcp('localhost', 8000)
|
151
|
+
TestService = client.for(:test)
|
152
|
+
|
153
|
+
p TestService.time.to_f
|
154
|
+
client.authenticate "testuserid", "secret"
|
155
|
+
p TestService.add(1, 2)
|
156
|
+
|
157
|
+
client.disconnect
|
158
|
+
|
159
|
+
|
125
160
|
## Async Server Example & NativeClient
|
126
161
|
|
127
162
|
As previously said, the server can use the `Gentleman` to issue asynchronous
|
data/examples/async/client.rb
CHANGED
@@ -19,7 +19,6 @@ done = false
|
|
19
19
|
t3 = Thread.new do
|
20
20
|
while !done
|
21
21
|
sleep 1
|
22
|
-
p client
|
23
22
|
STDOUT.print "."
|
24
23
|
STDOUT.flush
|
25
24
|
end
|
@@ -29,10 +28,6 @@ t1.join
|
|
29
28
|
t2.join
|
30
29
|
|
31
30
|
done = true
|
32
|
-
t3.join
|
33
|
-
|
34
|
-
p client
|
35
|
-
|
36
31
|
end_time = Time.now
|
37
32
|
|
38
33
|
puts "#{start_time - end_time}"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.push(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
2
|
+
require "marilyn-rpc"
|
3
|
+
client = MarilynRPC::NativeClient.connect_tcp('localhost', 8000)
|
4
|
+
TestService = client.for(:test)
|
5
|
+
|
6
|
+
begin
|
7
|
+
p TestService.add(1, 2)
|
8
|
+
rescue MarilynRPC::MarilynError => ex
|
9
|
+
puts "PermissionDenied: #{ex.message}"
|
10
|
+
end
|
11
|
+
|
12
|
+
p TestService.time.to_f
|
13
|
+
|
14
|
+
client.authenticate "testuserid", "secret"
|
15
|
+
|
16
|
+
p TestService.add(1, 2)
|
17
|
+
|
18
|
+
client.disconnect
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$:.push(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
2
|
+
require "marilyn-rpc"
|
3
|
+
require "rubygems"
|
4
|
+
require "eventmachine"
|
5
|
+
|
6
|
+
MarilynRPC::Service.authenticate_with do |username, password|
|
7
|
+
username == "testuserid" && password == "secret"
|
8
|
+
end
|
9
|
+
|
10
|
+
class TestService < MarilynRPC::Service
|
11
|
+
register :test
|
12
|
+
authentication_required :add
|
13
|
+
|
14
|
+
def time
|
15
|
+
puts session_username
|
16
|
+
puts session_authenticated?
|
17
|
+
Time.now
|
18
|
+
end
|
19
|
+
|
20
|
+
def add(a, b)
|
21
|
+
puts session_username
|
22
|
+
puts session_authenticated?
|
23
|
+
a + b
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
EM.run {
|
29
|
+
EM.start_server "localhost", 8000, MarilynRPC::Server
|
30
|
+
}
|
data/lib/marilyn-rpc.rb
CHANGED
data/lib/marilyn-rpc/client.rb
CHANGED
@@ -2,11 +2,12 @@ require 'socket'
|
|
2
2
|
require 'thread'
|
3
3
|
|
4
4
|
module MarilynRPC
|
5
|
-
class
|
5
|
+
# A class with nothing but `__send__` and `__id__`
|
6
|
+
class ClientBlankSlate
|
6
7
|
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
7
8
|
end
|
8
9
|
|
9
|
-
class NativeClientProxy <
|
10
|
+
class NativeClientProxy < ClientBlankSlate
|
10
11
|
# Creates a new Native client proxy, were the calls get send to the remote
|
11
12
|
# side.
|
12
13
|
# @param [Object] path the path that is used to identify the service
|
@@ -62,6 +63,16 @@ module MarilynRPC
|
|
62
63
|
def disconnect
|
63
64
|
@socket.close
|
64
65
|
end
|
66
|
+
|
67
|
+
# authenicate the client to call methods that require authentication
|
68
|
+
# @param [String] username the username of the client
|
69
|
+
# @param [String] password the password of the client
|
70
|
+
# @param [Symbol] method the method to use for authentication, currently
|
71
|
+
# only plain is supported. So make sure you are using a secure socket.
|
72
|
+
def authenticate(username, password, method = :plain)
|
73
|
+
execute(MarilynRPC::Service::AUTHENTICATION_PATH,
|
74
|
+
"authenticate_#{method}".to_sym, [username, password])
|
75
|
+
end
|
65
76
|
|
66
77
|
# Creates a new Proxy Object for the connection.
|
67
78
|
# @param [Object] path the path were the service is registered on the remote
|
@@ -139,8 +150,15 @@ module MarilynRPC
|
|
139
150
|
if mail.is_a? MarilynRPC::CallResponseMail
|
140
151
|
mail.result
|
141
152
|
else
|
142
|
-
raise
|
153
|
+
raise MarilynError.new # raise exception to capture the client backtrace
|
143
154
|
end
|
155
|
+
rescue MarilynError => exception
|
156
|
+
# add local and remote trace together and reraise the original exception
|
157
|
+
backtrace = []
|
158
|
+
backtrace += exception.backtrace
|
159
|
+
backtrace += mail.exception.backtrace
|
160
|
+
mail.exception.set_backtrace(backtrace)
|
161
|
+
raise mail.exception
|
144
162
|
end
|
145
163
|
end
|
146
164
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MarilynRPC
|
2
|
+
class MarilynError < StandardError; end
|
3
|
+
|
4
|
+
# Error that occurs, when we recieve an broken envelope
|
5
|
+
class BrokenEnvelopeError < MarilynError; end
|
6
|
+
|
7
|
+
# Error that occurs, when the client tries to call an unknown service
|
8
|
+
class UnknownServiceError < MarilynError; end
|
9
|
+
|
10
|
+
# Error that occurs, when the client tries to call an service, that requires
|
11
|
+
# authentication. The client must be authenticated before that call.
|
12
|
+
class PermissionDeniedError < MarilynError; end
|
13
|
+
end
|
data/lib/marilyn-rpc/mails.rb
CHANGED
@@ -61,11 +61,11 @@ module MarilynRPC
|
|
61
61
|
TYPE = MarilynRPC::MailHelper.type(3)
|
62
62
|
|
63
63
|
def encode
|
64
|
-
TYPE + serialize(self.exception)
|
64
|
+
TYPE + serialize([self.tag, self.exception])
|
65
65
|
end
|
66
66
|
|
67
67
|
def decode(data)
|
68
|
-
self.exception = deserialize(only_data(data))
|
68
|
+
self.tag, self.exception = deserialize(only_data(data))
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
@@ -86,7 +86,7 @@ module MarilynRPC
|
|
86
86
|
when MarilynRPC::ExceptionMail::TYPE
|
87
87
|
mail = MarilynRPC::ExceptionMail.new
|
88
88
|
else
|
89
|
-
raise
|
89
|
+
raise MarilynRPC::BrokenEnvelopeError.new("The passed envelope is broken!")
|
90
90
|
end
|
91
91
|
mail.decode(data)
|
92
92
|
mail
|
data/lib/marilyn-rpc/server.rb
CHANGED
@@ -34,17 +34,12 @@ module MarilynRPC::Server
|
|
34
34
|
|
35
35
|
# was massage parsed successfully?
|
36
36
|
if @envelope.finished?
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
answer =
|
41
|
-
|
42
|
-
|
43
|
-
else
|
44
|
-
answer.connection = self # pass connection for async responses
|
45
|
-
end
|
46
|
-
rescue => exception
|
47
|
-
send_mail(MarilynRPC::ExceptionMail.new(mail.tag, exception))
|
37
|
+
# grep the request
|
38
|
+
answer = @cache.call(@envelope)
|
39
|
+
if answer.is_a? MarilynRPC::Gentleman
|
40
|
+
answer.connection = self # pass connection for async responses
|
41
|
+
else
|
42
|
+
send_mail(answer)
|
48
43
|
end
|
49
44
|
|
50
45
|
# initialize the next envelope
|
data/lib/marilyn-rpc/service.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
# A class with nothing but `__send__`, `__id__`, `class` and `public_methods`.
|
2
|
+
class ServiceBlankSlate
|
3
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|public_methods|class/ }
|
4
|
+
end
|
5
|
+
|
1
6
|
# This class represents the base for all events, it is used for registering
|
2
|
-
# services and defining callbacks for certain service events.
|
7
|
+
# services and defining callbacks for certain service events. It is also
|
8
|
+
# possible to enable an authentication check for methods of the service.
|
9
|
+
# @attr [MarilynRPC::ServiceCache] service_cache the service cache where an
|
10
|
+
# instance lives in
|
3
11
|
# @example a service that makes use of the available helpers
|
4
12
|
# class EventsService < MarilynRPC::Service
|
5
13
|
# register :events
|
6
14
|
# after_connect :connected
|
7
15
|
# after_disconnect :disconnected
|
16
|
+
# authentication_required :notify
|
8
17
|
#
|
9
18
|
# def connected
|
10
19
|
# puts "client connected"
|
@@ -19,7 +28,9 @@
|
|
19
28
|
# end
|
20
29
|
# end
|
21
30
|
#
|
22
|
-
class MarilynRPC::Service
|
31
|
+
class MarilynRPC::Service < ServiceBlankSlate
|
32
|
+
attr_accessor :service_cache
|
33
|
+
|
23
34
|
# registers the class where is was called as a service
|
24
35
|
# @param [String] path the path of the service
|
25
36
|
def self.register(path)
|
@@ -31,7 +42,7 @@ class MarilynRPC::Service
|
|
31
42
|
# @api private
|
32
43
|
# @return [Hash<String, Object>] all registered services with path as key and
|
33
44
|
# the registered service as object
|
34
|
-
def self.
|
45
|
+
def self.__registry__
|
35
46
|
@@registry || {}
|
36
47
|
end
|
37
48
|
|
@@ -39,21 +50,21 @@ class MarilynRPC::Service
|
|
39
50
|
# defined in the class
|
40
51
|
# @param [Array<Symbol>, Array<String>] callbacks the method names
|
41
52
|
def self.after_connect(*callbacks)
|
42
|
-
|
53
|
+
__register_callbacks__ :after_connect, callbacks
|
43
54
|
end
|
44
55
|
|
45
56
|
# register one or more disconnect callbacks, a callback is simply a method
|
46
57
|
# defined in the class
|
47
58
|
# @param [Array<Symbol>, Array<String>] callbacks the method names
|
48
59
|
def self.after_disconnect(*callbacks)
|
49
|
-
|
60
|
+
__register_callbacks__ :after_disconnect, callbacks
|
50
61
|
end
|
51
62
|
|
52
63
|
# registers a callbacks for the service class
|
53
64
|
# @param [Symbol] name the name under which the callbacks should be saved
|
54
65
|
# @param [Array<Symbol>, Array<String>] callbacks the method names
|
55
66
|
# @api private
|
56
|
-
def self.
|
67
|
+
def self.__register_callbacks__(name, callbacks)
|
57
68
|
@_callbacks ||= {} # initialize callbacks
|
58
69
|
@_callbacks[name] ||= [] # initialize specific set
|
59
70
|
@_callbacks[name] += callbacks
|
@@ -64,16 +75,80 @@ class MarilynRPC::Service
|
|
64
75
|
# @return [Array<String>, Array<Symbol>] an array of callback names, or an
|
65
76
|
# empty array
|
66
77
|
# @api private
|
67
|
-
def self.
|
68
|
-
|
78
|
+
def self.__registered_callbacks__(name)
|
79
|
+
@_callbacks ||= {}
|
80
|
+
@_callbacks[name] || []
|
81
|
+
end
|
82
|
+
|
83
|
+
# this generator marks the passed method names to require authentication.
|
84
|
+
# A Method that requires authentication is only callable if the client was
|
85
|
+
# successfully authenticated.
|
86
|
+
# @param [Array<String>, Array<Symbol>] methods the methods names
|
87
|
+
def self.authentication_required(*methods)
|
88
|
+
@_authenticated ||= {} # initalize hash of authenticated methods
|
89
|
+
methods.each { |m| @_authenticated[m.to_sym] = true }
|
90
|
+
end
|
91
|
+
|
92
|
+
# returns all methods of the service that require authentication
|
93
|
+
# @return [Array<Symbol>] methods that require authentication
|
94
|
+
# @api private
|
95
|
+
def self.__methods_with_authentication__
|
96
|
+
@_authenticated ||= {}
|
69
97
|
end
|
70
98
|
|
71
99
|
# calls the defined connect callbacks
|
72
100
|
# @param [Symbol] the name of the callbacks to run
|
73
101
|
# @api private
|
74
|
-
def
|
75
|
-
self.class.
|
76
|
-
self.
|
102
|
+
def __run_callbacks__(name)
|
103
|
+
self.class.__registered_callbacks__(name).each do |callback|
|
104
|
+
self.__send__(callback)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# returns the username if a user is authenticated
|
109
|
+
# @return [String, nil] the username or nil, if no user is authenticated
|
110
|
+
def session_username
|
111
|
+
@service_cache.username
|
112
|
+
end
|
113
|
+
|
114
|
+
# checks if a user is authenticated
|
115
|
+
# @return [Boolean] `true` if a user is authenticated, `false` otherwise
|
116
|
+
def session_authenticated?
|
117
|
+
!@service_cache.username.nil?
|
118
|
+
end
|
119
|
+
|
120
|
+
# the name for the service which will be used to do the authentication
|
121
|
+
AUTHENTICATION_PATH = :__marilyn_rpc_service_authentication
|
122
|
+
|
123
|
+
# define an authentication mechanism using a `lambda` or `Proc` object, or
|
124
|
+
# something else that respond to `call`. The authentication is available for
|
125
|
+
# all serivces of that connection.
|
126
|
+
# @param [Proc] &authenticator the authentication mechanism
|
127
|
+
# @yieldparam [String] username the username of the client
|
128
|
+
# @yieldparam [String] password the password of the client
|
129
|
+
# @yieldreturn [Boolean] To authenticate a user, the passed
|
130
|
+
# block must return `true` otherwise `false`
|
131
|
+
# @example Create a new authentication mechanism for clients using callable
|
132
|
+
# MarilynRPC::Service.authenticate_with do |username, password|
|
133
|
+
# username == "testuserid" && password == "secret"
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
def self.authenticate_with(&authenticator)
|
137
|
+
Class.new(self) do # anonymous class
|
138
|
+
@@authenticator = authenticator
|
139
|
+
register(AUTHENTICATION_PATH)
|
140
|
+
|
141
|
+
# authenticate the user using a plain password method
|
142
|
+
# @param [String] username the username of the client
|
143
|
+
# @param [String] password the password of the client
|
144
|
+
def authenticate_plain(username, password)
|
145
|
+
if @@authenticator.call(username, password)
|
146
|
+
@service_cache.username = username
|
147
|
+
else
|
148
|
+
raise MarilynRPC::PermissionDeniedError.new \
|
149
|
+
"Wrong username or password!"
|
150
|
+
end
|
151
|
+
end
|
77
152
|
end
|
78
153
|
end
|
79
154
|
end
|
@@ -1,33 +1,53 @@
|
|
1
|
-
class
|
1
|
+
# # This class represents a per connection cache of the service instances.
|
2
|
+
# @attr [String, nil] username the username of a authenticated user oder `nil`
|
3
|
+
class MarilynRPC::ServiceCache
|
4
|
+
attr_accessor :username
|
5
|
+
|
2
6
|
# creates the service cache
|
3
7
|
def initialize
|
4
8
|
@services = {}
|
5
9
|
end
|
6
10
|
|
7
11
|
# call a service in the service cache
|
8
|
-
# @param [MarilynRPC::
|
9
|
-
# should be handled
|
12
|
+
# @param [MarilynRPC::Envelope] envelope the envelope that contains the
|
13
|
+
# request subject (mail), that should be handled
|
10
14
|
# @return [MarilynRPC::CallResponseMail, MarilynRPC::Gentleman] either a
|
11
15
|
# Gentleman if the response is async or an direct response.
|
12
|
-
def call(
|
13
|
-
|
14
|
-
|
15
|
-
raise ArgumentError.new("Expected CallRequestMail Object!")
|
16
|
-
end
|
17
|
-
|
18
|
-
# call the service instance using the argument of the mail
|
19
|
-
# puts "call #{mail.method}@#{mail.path} with #{mail.args.inspect}"
|
20
|
-
result = lookup(mail.path).send(mail.method, *mail.args)
|
21
|
-
# puts "result => #{result.inspect}"
|
16
|
+
def call(envelope)
|
17
|
+
mail = MarilynRPC::MailFactory.unpack(envelope)
|
18
|
+
tag = mail.tag
|
22
19
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
if mail.is_a?(MarilynRPC::CallRequestMail) # handle a call request
|
21
|
+
# fetch the service and check if the user has the permission to access the
|
22
|
+
# service
|
23
|
+
service = lookup(mail.path)
|
24
|
+
method = mail.method.to_sym
|
25
|
+
if service.class.__methods_with_authentication__[method] && !@username
|
26
|
+
raise MarilynRPC::PermissionDeniedError.new("No permission to access" \
|
27
|
+
" the #{service.class.name}##{method}")
|
28
|
+
end
|
29
|
+
|
30
|
+
# call the service instance using the argument of the mail
|
31
|
+
#puts "call #{mail.method}@#{mail.path} with #{mail.args.inspect}"
|
32
|
+
result = service.__send__(method, *mail.args)
|
33
|
+
#puts "result => #{result.inspect}"
|
34
|
+
|
35
|
+
# no direct result, register callback
|
36
|
+
if result.is_a? MarilynRPC::Gentleman
|
37
|
+
result.tag = tag # set the correct mail tag for the answer
|
38
|
+
result
|
39
|
+
else
|
40
|
+
MarilynRPC::CallResponseMail.new(tag, result) # direct response
|
41
|
+
end
|
27
42
|
else
|
28
|
-
|
29
|
-
MarilynRPC::CallResponseMail.new(mail.tag, result)
|
43
|
+
raise MarilynRPC::BrokenEnvelopeError.new("Expected CallRequestMail Object!")
|
30
44
|
end
|
45
|
+
rescue MarilynRPC::BrokenEnvelopeError => exception
|
46
|
+
MarilynRPC::ExceptionMail.new(nil, exception)
|
47
|
+
rescue => exception
|
48
|
+
#puts exception
|
49
|
+
#puts exception.backtrace.join("\n ")
|
50
|
+
MarilynRPC::ExceptionMail.new(tag, exception)
|
31
51
|
end
|
32
52
|
|
33
53
|
# get the service from the cache or the service registry
|
@@ -38,12 +58,13 @@ class MarilynRPC::ServiceCache
|
|
38
58
|
if service = @services[path]
|
39
59
|
return service
|
40
60
|
# it's not in the cache, so try lookup in the service registry
|
41
|
-
elsif service = MarilynRPC::Service.
|
61
|
+
elsif service = MarilynRPC::Service.__registry__[path]
|
42
62
|
@services[path] = service.new
|
43
|
-
@services[path].
|
63
|
+
@services[path].service_cache = self
|
64
|
+
@services[path].__run_callbacks__(:after_connect)
|
44
65
|
return @services[path]
|
45
66
|
else
|
46
|
-
raise
|
67
|
+
raise MarilynRPC::UnknownServiceError.new("Service #{path} unknown!")
|
47
68
|
end
|
48
69
|
end
|
49
70
|
|
@@ -52,7 +73,7 @@ class MarilynRPC::ServiceCache
|
|
52
73
|
# @api private
|
53
74
|
def disconnect!
|
54
75
|
@services.each do |path, service|
|
55
|
-
service.
|
76
|
+
service.__run_callbacks__(:after_disconnect)
|
56
77
|
end
|
57
78
|
end
|
58
79
|
end
|