em_remote_call 0.0.6 → 0.1.0
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/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
|