em_remote_call 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/em_remote_call/client.rb +55 -24
- data/lib/em_remote_call/server.rb +25 -17
- data/lib/em_remote_call/version.rb +1 -1
- data/spec/integration_spec.rb +49 -41
- metadata +3 -3
@@ -1,35 +1,47 @@
|
|
1
|
-
|
2
|
-
def
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
1
|
+
class Class
|
2
|
+
def has_em_remote_class(remote_class_name, opts)
|
3
|
+
extend EM::RemoteCall
|
4
|
+
class << self
|
5
|
+
attr_accessor :remote_connection
|
6
|
+
extend EM::RemoteCall
|
8
7
|
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
opts[:name] ||= :default
|
10
|
+
opts[:remote_class_name] = remote_class_name
|
11
|
+
self.remote_connection = EM::RemoteCall::Client.find(opts[:name]) || EM::RemoteCall::Client.new(opts)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module EM::RemoteCall
|
16
|
+
def remote_method(method_name, opts={})
|
17
|
+
define_method method_name do |*method_args, &callb|
|
18
|
+
return unless remote_connection =
|
19
|
+
(self.class.respond_to?(:remote_connection) && self.class.remote_connection) ||
|
20
|
+
(self.respond_to?(:remote_connection) && self.remote_connection)
|
15
21
|
|
16
22
|
callback = EM::RemoteCall::Deferrable.new
|
17
23
|
callback.callback(&callb) if callb
|
18
24
|
|
19
25
|
call = {
|
20
|
-
:
|
21
|
-
:
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
}
|
26
|
+
:deferrable_id => callback.object_id, # store the callbacks object_id to retrieve it later
|
27
|
+
:debug => opts[:debug], # debugging on client and server
|
28
|
+
:method => opts[:calls] || method_name, # same method name by default
|
29
|
+
:arguments => [*method_args], # all the args
|
30
|
+
:instance => {
|
31
|
+
:class => remote_connection.remote_class_name || self.class.to_s, # own class name by default
|
32
|
+
:id => !opts[:server_class_method] && send(remote_connection.instance_finder) # when it's nil, it's considered a class method.
|
33
|
+
}}
|
28
34
|
|
35
|
+
puts "On Client: #{call}" if opts[:debug]
|
29
36
|
remote_connection.call call
|
30
37
|
return callback
|
31
38
|
end
|
32
39
|
end
|
40
|
+
|
41
|
+
# a convinience wrapper for class methods:
|
42
|
+
def remote_class_method(method_name, opts={})
|
43
|
+
(class << self; self; end).remote_method method_name, opts.merge({:server_class_method => true})
|
44
|
+
end
|
33
45
|
end
|
34
46
|
|
35
47
|
class EM::RemoteCall::Deferrable
|
@@ -42,17 +54,36 @@ class EM::RemoteCall::Deferrable
|
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
45
|
-
module EM::RemoteCall::
|
57
|
+
module EM::RemoteCall::ClientConnection
|
46
58
|
include EM::JsonConnection::Client
|
47
59
|
|
48
60
|
def json_parsed(hash)
|
49
|
-
|
50
|
-
|
61
|
+
puts "From server: #{hash}" if hash[:debug]
|
62
|
+
|
63
|
+
deffr = EM::RemoteCall::Deferrable.find hash[:deferrable_id]
|
51
64
|
deffr.succeed hash[:success] if hash.has_key? :success
|
52
65
|
deffr.fail hash[:error] if hash.has_key? :error
|
53
66
|
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class EM::RemoteCall::Client
|
70
|
+
attr_reader :name, :remote_class_name, :instance_finder
|
71
|
+
is_a_collection :name
|
72
|
+
|
73
|
+
def initialize(opts)
|
74
|
+
@name = opts[:name] ||= :default
|
75
|
+
@remote_class_name = opts[:remote_class_name]
|
76
|
+
@socket = opts[:socket]
|
77
|
+
@port = opts[:port]
|
78
|
+
@instance_finder = opts[:instance_finder] ||= :id
|
79
|
+
end
|
54
80
|
|
55
81
|
def call(call)
|
56
|
-
|
82
|
+
unless @connection && @connection.connected?
|
83
|
+
@connection = EM::RemoteCall::ClientConnection.connect_to *[@socket, @port].compact
|
84
|
+
end
|
85
|
+
|
86
|
+
@connection.send_data call
|
57
87
|
end
|
88
|
+
|
58
89
|
end
|
@@ -2,15 +2,15 @@ module EM::RemoteCall; end
|
|
2
2
|
|
3
3
|
class EM::RemoteCall::Call
|
4
4
|
class Error < StandardError; end
|
5
|
-
class NoInstanceError < Error
|
5
|
+
class NoInstanceError < Error; end
|
6
6
|
class NoMethodGivenError < Error; end
|
7
7
|
class NoMethodOfInstanceError < Error; end
|
8
8
|
|
9
|
-
def initialize(instance_opts, method,
|
10
|
-
@
|
11
|
-
@method
|
12
|
-
@instance
|
13
|
-
@instance.respond_to?(@method)
|
9
|
+
def initialize(instance_opts, method, arguments)
|
10
|
+
@arguments = arguments
|
11
|
+
@method = method or raise NoMethodError.new method
|
12
|
+
@instance = find_instance(instance_opts) or raise NoInstanceError.new instance_opts
|
13
|
+
@instance.respond_to?(@method) or raise NoMethodOfInstanceError.new "#{@instance}##{method}"
|
14
14
|
end
|
15
15
|
|
16
16
|
def find_instance(args)
|
@@ -22,8 +22,15 @@ class EM::RemoteCall::Call
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
def takes_block?
|
26
|
+
method = @instance.method(@method)
|
27
|
+
return unless method.respond_to?(:parameters)
|
28
|
+
params = method.parameters
|
29
|
+
params.last && params.last.first == :block
|
30
|
+
end
|
31
|
+
|
25
32
|
def call(&callb)
|
26
|
-
@
|
33
|
+
@arguments.empty? ? @instance.__send__(@method, &callb) : @instance.__send__(@method, *@arguments, &callb)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -31,23 +38,24 @@ module EM::RemoteCall::Server
|
|
31
38
|
include EM::JsonConnection::Server
|
32
39
|
|
33
40
|
def json_parsed(hash)
|
34
|
-
remote_call = EM::RemoteCall::Call.new hash[:instance], hash[:method].to_sym, hash[:
|
41
|
+
remote_call = EM::RemoteCall::Call.new hash[:instance], hash[:method].to_sym, hash[:arguments]
|
42
|
+
puts "On Server: #{remote_call}: #{hash}" if hash[:debug]
|
35
43
|
|
36
|
-
|
37
|
-
send_data
|
44
|
+
if remote_call.takes_block?
|
45
|
+
remote_call.call{ |result| send_data :deferrable_id => hash[:deferrable_id], :method_type => :block, :success => result }
|
46
|
+
return
|
38
47
|
end
|
39
48
|
|
49
|
+
ret = remote_call.call
|
40
50
|
if ret.is_a? EM::Deferrable
|
41
|
-
ret.callback
|
42
|
-
|
43
|
-
|
44
|
-
ret
|
45
|
-
send_data({:deferrable_id => hash[:deferrable_id], :error => result})
|
46
|
-
end
|
51
|
+
ret.callback{ |result| send_data :deferrable_id => hash[:deferrable_id], :debug => hash[:debug], :method_type => :deferrable, :success => result }
|
52
|
+
ret.errback { |result| send_data :deferrable_id => hash[:deferrable_id], :debug => hash[:debug], :method_type => :deferrable, :error => result }
|
53
|
+
else
|
54
|
+
send_data :deferrable_id => hash[:deferrable_id], :success => ret, :method_type => :returned, :debug => hash[:debug]
|
47
55
|
end
|
48
56
|
|
49
57
|
rescue => e
|
50
58
|
puts "#{e}: #{e.message}"
|
51
|
-
send_data
|
59
|
+
send_data :deferrable_id => hash[:deferrable_id], :error => {:class => e.class.name, :message => e.message}, :method_type => :rescue, :debug => hash[:debug]
|
52
60
|
end
|
53
61
|
end
|
data/spec/integration_spec.rb
CHANGED
@@ -15,43 +15,54 @@ class Track
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
TEST_SOCKET = File.join( File.expand_path(File.dirname(__FILE__)), 'test_socket' )
|
19
|
+
|
18
20
|
class ServerTrack < Track
|
19
21
|
is_a_collection
|
20
22
|
|
21
|
-
def initialize(opts, &callb)
|
22
|
-
callb.call
|
23
|
+
def initialize(opts={}, &callb)
|
24
|
+
callb.call if callb
|
23
25
|
super
|
24
26
|
end
|
25
27
|
|
28
|
+
# takes a block:
|
26
29
|
def play(&callb)
|
27
30
|
callb.call "finished #{id}"
|
28
31
|
"started #{id}"
|
29
32
|
end
|
33
|
+
|
34
|
+
# raises:
|
30
35
|
def raise_hell
|
31
36
|
raise 'foobar'
|
32
37
|
end
|
38
|
+
|
39
|
+
# returns a deferrable:
|
33
40
|
def with_deferrable(outcome = 'succeed') # does not take a block
|
34
41
|
d = EventMachine::DefaultDeferrable.new
|
35
42
|
d.send outcome
|
36
43
|
return d
|
37
44
|
end
|
38
45
|
|
39
|
-
|
40
|
-
|
46
|
+
# doesn't take a block, just returns a hash:
|
47
|
+
def as_hash
|
48
|
+
{:title => title, :artist => artist}
|
49
|
+
end
|
50
|
+
|
51
|
+
# a class method:
|
52
|
+
def self.some_class_meth(&blk)
|
53
|
+
blk.call
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
44
57
|
class ClientTrack < Track
|
45
|
-
|
46
|
-
remote_method :init_track_on_server, :class_name => 'ServerTrack', :calls => :new
|
47
|
-
remote_method :play, :class_name => 'ServerTrack', :find_by => :id
|
48
|
-
remote_method :raise_hell, :class_name => 'ServerTrack', :find_by => :id
|
49
|
-
remote_method :with_deferrable, :class_name => 'ServerTrack', :find_by => :id
|
58
|
+
has_em_remote_class 'ServerTrack', :socket => TEST_SOCKET
|
50
59
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
remote_method :init_track_on_server, :calls => :new, :server_class_method => true
|
61
|
+
remote_method :play
|
62
|
+
remote_method :raise_hell
|
63
|
+
remote_method :with_deferrable
|
64
|
+
remote_method :as_hash
|
65
|
+
remote_class_method :some_class_meth
|
55
66
|
end
|
56
67
|
|
57
68
|
class EMController
|
@@ -60,14 +71,11 @@ class EMController
|
|
60
71
|
end
|
61
72
|
|
62
73
|
def test_on_client
|
63
|
-
socket = File.join( File.expand_path(File.dirname(__FILE__)), 'test_socket' )
|
64
|
-
|
65
74
|
server_pid = EM.fork_reactor do
|
66
|
-
EM::RemoteCall::Server.start_at
|
75
|
+
EM::RemoteCall::Server.start_at TEST_SOCKET
|
67
76
|
end
|
68
77
|
sleep 0.1
|
69
78
|
EM.run do
|
70
|
-
ClientTrack.remote_connection = EM::RemoteCall::Client.connect_to socket
|
71
79
|
yield
|
72
80
|
EM.add_timer 0.1 do
|
73
81
|
Process.kill 'HUP', server_pid
|
@@ -82,39 +90,39 @@ describe EM::RemoteCall do
|
|
82
90
|
test_on_client do
|
83
91
|
callb = mock(:callb)
|
84
92
|
callb.should_receive(:foo)
|
85
|
-
ClientTrack.new.init_track_on_server
|
93
|
+
ClientTrack.new.init_track_on_server{callb.foo}
|
86
94
|
end
|
87
95
|
end
|
88
96
|
end
|
89
|
-
describe "
|
90
|
-
it "should
|
97
|
+
describe "class method callbacks too" do
|
98
|
+
it "should work twice" do
|
91
99
|
test_on_client do
|
92
100
|
callb = mock(:callb)
|
93
|
-
callb.should_receive(:foo).
|
94
|
-
|
95
|
-
|
96
|
-
c.play{|a| callb.foo a}
|
101
|
+
callb.should_receive(:foo).twice
|
102
|
+
ClientTrack.new.init_track_on_server{callb.foo}
|
103
|
+
ClientTrack.new.init_track_on_server{callb.foo}
|
97
104
|
end
|
98
105
|
end
|
99
106
|
end
|
100
|
-
describe "
|
101
|
-
it "should
|
107
|
+
describe "client side class method" do
|
108
|
+
it "should work :)" do
|
102
109
|
test_on_client do
|
103
110
|
callb = mock(:callb)
|
104
|
-
callb.should_receive(:foo)
|
105
|
-
|
106
|
-
c.init_track_on_server(:title => 'a', :artist => 'b')
|
107
|
-
play_call = c.raise_hell
|
108
|
-
play_call.errback{|a| callb.foo a}
|
111
|
+
callb.should_receive(:foo)
|
112
|
+
ClientTrack.some_class_meth{callb.foo}
|
109
113
|
end
|
110
114
|
end
|
111
115
|
end
|
112
|
-
describe "
|
113
|
-
it "should
|
116
|
+
describe "without block or deferrable" do
|
117
|
+
it "should return just the return value" do
|
114
118
|
test_on_client do
|
115
119
|
callb = mock(:callb)
|
116
|
-
|
117
|
-
|
120
|
+
properties = {:artist => 'a', :title => 't'}
|
121
|
+
callb.should_receive(:foo).with(properties)
|
122
|
+
c = ClientTrack.new properties
|
123
|
+
c.init_track_on_server properties do
|
124
|
+
c.as_hash{|r| callb.foo(r)}
|
125
|
+
end
|
118
126
|
end
|
119
127
|
end
|
120
128
|
end
|
@@ -124,9 +132,9 @@ describe EM::RemoteCall do
|
|
124
132
|
it "should use the block as callback" do
|
125
133
|
test_on_client do
|
126
134
|
callb = mock(:callb)
|
127
|
-
callb.should_receive(:foo)
|
135
|
+
callb.should_receive(:foo)
|
128
136
|
c = ClientTrack.new
|
129
|
-
c.init_track_on_server
|
137
|
+
c.init_track_on_server
|
130
138
|
c.with_deferrable(:succeed){ callb.foo }
|
131
139
|
end
|
132
140
|
end
|
@@ -135,9 +143,9 @@ describe EM::RemoteCall do
|
|
135
143
|
it "should use callback" do
|
136
144
|
test_on_client do
|
137
145
|
callb = mock(:callb)
|
138
|
-
callb.should_receive(:foo)
|
146
|
+
callb.should_receive(:foo)
|
139
147
|
c = ClientTrack.new
|
140
|
-
c.init_track_on_server
|
148
|
+
c.init_track_on_server
|
141
149
|
play_call = c.with_deferrable(:succeed)
|
142
150
|
play_call.callback{ callb.foo }
|
143
151
|
end
|
@@ -148,9 +156,9 @@ describe EM::RemoteCall do
|
|
148
156
|
it "should use errback" do
|
149
157
|
test_on_client do
|
150
158
|
callb = mock(:callb)
|
151
|
-
callb.should_receive(:foo)
|
159
|
+
callb.should_receive(:foo)
|
152
160
|
c = ClientTrack.new
|
153
|
-
c.init_track_on_server
|
161
|
+
c.init_track_on_server
|
154
162
|
play_call = c.with_deferrable(:fail)
|
155
163
|
play_call.errback{ callb.foo }
|
156
164
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.6
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Niko Dittmann
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-12-
|
17
|
+
date: 2010-12-27 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|