rubarb 0.2.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/.gitignore +7 -0
- data/README +77 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/examples/client.rb +32 -0
- data/examples/server.rb +34 -0
- data/lib/dkbrpc.rb +4 -0
- data/lib/rubarb.rb +2 -0
- data/lib/rubarb/connection.rb +170 -0
- data/lib/rubarb/connection_error.rb +11 -0
- data/lib/rubarb/connection_id.rb +11 -0
- data/lib/rubarb/default.rb +15 -0
- data/lib/rubarb/fast_message_protocol.rb +109 -0
- data/lib/rubarb/id.rb +13 -0
- data/lib/rubarb/incoming_connection.rb +25 -0
- data/lib/rubarb/insecure_method_call_error.rb +11 -0
- data/lib/rubarb/outgoing_connection.rb +27 -0
- data/lib/rubarb/remote_call.rb +12 -0
- data/lib/rubarb/responder.rb +18 -0
- data/lib/rubarb/server.rb +185 -0
- data/spec/rubarb/connection_failure_spec.rb +71 -0
- data/spec/rubarb/connection_spec.rb +187 -0
- data/spec/rubarb/id_spec.rb +26 -0
- data/spec/rubarb/incoming_connection_spec.rb +79 -0
- data/spec/rubarb/integration_spec.rb +174 -0
- data/spec/rubarb/outgoing_connection_spec.rb +103 -0
- data/spec/rubarb/remote_call_spec.rb +48 -0
- data/spec/rubarb/responder_spec.rb +35 -0
- data/spec/rubarb/server_failure_spec.rb +261 -0
- data/spec/rubarb/server_spec.rb +201 -0
- data/spec/spec_helper.rb +32 -0
- metadata +146 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'rubarb/id'
|
3
|
+
|
4
|
+
include Rubarb
|
5
|
+
|
6
|
+
describe Id do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@id = Id.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "starts generator at 00000001" do
|
13
|
+
@id.next.should == "00000001"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "increments counter by 1 when next is called" do
|
17
|
+
@id.next
|
18
|
+
@id.next.should == "00000002"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "resets counter to one if id is 99999999" do
|
22
|
+
@id.instance_eval{ @id = 99999999 }
|
23
|
+
@id.next
|
24
|
+
@id.next.should == "00000001"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require "rubarb/incoming_connection"
|
4
|
+
require "rubarb/remote_call"
|
5
|
+
require "rubarb/default"
|
6
|
+
|
7
|
+
describe Rubarb::IncomingConnection do
|
8
|
+
include Rubarb::RemoteCall
|
9
|
+
include Rubarb::IncomingConnection
|
10
|
+
attr_reader :api
|
11
|
+
|
12
|
+
class TestApi
|
13
|
+
attr_accessor :dodo
|
14
|
+
def amethod(responder, dodo)
|
15
|
+
@dodo = dodo
|
16
|
+
end
|
17
|
+
def ==(rhs)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
@api = TestApi.new
|
23
|
+
@insecure_methods = Rubarb::Default::INSECURE_METHODS
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should receive message" do
|
27
|
+
receive_message(marshal_call("00000001", :amethod, "goo"))
|
28
|
+
@api.dodo.should == "goo"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should receive reply with exception when a non-existent method is called" do
|
32
|
+
should_receive(:reply).with do |id, exception|
|
33
|
+
id.should == "0"
|
34
|
+
exception.message.include?("undefined method").should == true
|
35
|
+
end
|
36
|
+
receive_message(marshal_call("00000001", :not_a_method, "foo"))
|
37
|
+
@api.should_not_receive(:send)
|
38
|
+
end
|
39
|
+
|
40
|
+
def blocks_method_in_receive_message(method)
|
41
|
+
@method = method
|
42
|
+
should_receive(:reply).with do |id, exception|
|
43
|
+
id.should == "0"
|
44
|
+
exception.message.should == "Remote client attempts to call method #{@method}, but was denied."
|
45
|
+
end
|
46
|
+
receive_message(marshal_call("00000001", method, "foo"))
|
47
|
+
@api.should_not_receive(:send)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "blocks :== in receive_message" do
|
51
|
+
blocks_method_in_receive_message(:==)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "blocks any insecure method in receive_message" do
|
55
|
+
Rubarb::Default::INSECURE_METHODS.each do |method|
|
56
|
+
blocks_method_in_receive_message(method)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "receives message with id, method, and *args" do
|
61
|
+
message = marshal_call("00000001", :amethod, "foo")
|
62
|
+
receive_message(message)
|
63
|
+
@api.dodo.should == "foo"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "replies message with id and args" do
|
67
|
+
message = marshal_call("00000001", :amethod, "foo")
|
68
|
+
receive_message(message)
|
69
|
+
self.should_receive(:send_message).with(marshal_call(["00000001", "result"]))
|
70
|
+
reply("00000001", "result")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "replies message with id 00000002 and args" do
|
74
|
+
message = marshal_call("00000002", :amethod, "foo")
|
75
|
+
receive_message(message)
|
76
|
+
self.should_receive(:send_message).with(marshal_call(["00000002", "result"]))
|
77
|
+
reply("00000002", "result")
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'rubarb/server'
|
4
|
+
require 'rubarb/connection'
|
5
|
+
|
6
|
+
describe "Server to Client communication and response" do
|
7
|
+
|
8
|
+
class TestClientApi
|
9
|
+
def name(responder)
|
10
|
+
responder.reply("Doug")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
@reactor = start_reactor
|
16
|
+
@server_api = mock("server")
|
17
|
+
@client_api = TestClientApi.new
|
18
|
+
|
19
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, @server_api)
|
20
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, @client_api)
|
21
|
+
end
|
22
|
+
|
23
|
+
after(:each) do
|
24
|
+
stop_reactor(@reactor)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should communicate with the client" do
|
28
|
+
@callback_called = false
|
29
|
+
|
30
|
+
@server.start do |new_client|
|
31
|
+
new_client.name do|result|
|
32
|
+
result.should == "Doug"
|
33
|
+
@callback_called = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@connection.start
|
38
|
+
|
39
|
+
wait_for {@callback_called}
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "Client to Server communication and response" do
|
46
|
+
class TestReply
|
47
|
+
attr_reader :message
|
48
|
+
|
49
|
+
def initialize(name)
|
50
|
+
@message = "How are you #{name}, my friend?"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class TestServerApi
|
55
|
+
attr_reader :hi_called
|
56
|
+
def hi(responder)
|
57
|
+
responder.reply("how are you?")
|
58
|
+
@hi_called = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def hello(responder, name)
|
62
|
+
responder.reply("how are you #{name}?")
|
63
|
+
end
|
64
|
+
|
65
|
+
def hello_friend(responder, name)
|
66
|
+
responder.reply(TestReply.new(name))
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :conn_id
|
70
|
+
def save_id(responder)
|
71
|
+
@conn_id = responder.conn_id
|
72
|
+
responder.reply(nil)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
@reactor = start_reactor
|
78
|
+
@server_api = TestServerApi.new
|
79
|
+
@client_api = mock("client")
|
80
|
+
|
81
|
+
@server = Rubarb::Server.new("127.0.0.1", 9441, @server_api)
|
82
|
+
@connection = Rubarb::Connection.new("127.0.0.1", 9441, @client_api)
|
83
|
+
end
|
84
|
+
|
85
|
+
after(:each) do
|
86
|
+
stop_reactor(@reactor)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "without a callback" do
|
90
|
+
@server.start
|
91
|
+
|
92
|
+
@connection.start do
|
93
|
+
@connection.hi
|
94
|
+
end
|
95
|
+
|
96
|
+
wait_for {@server_api.hi_called}
|
97
|
+
end
|
98
|
+
|
99
|
+
it "without parameters" do
|
100
|
+
@callback_called = false
|
101
|
+
@server.start
|
102
|
+
|
103
|
+
@connection.start do
|
104
|
+
@connection.hi do |response|
|
105
|
+
response.should == "how are you?"
|
106
|
+
@callback_called = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
wait_for {@callback_called}
|
111
|
+
end
|
112
|
+
|
113
|
+
it "with parameters" do
|
114
|
+
@callback_called = false
|
115
|
+
@server.start
|
116
|
+
@connection.start do
|
117
|
+
@connection.hello("Doug") do |response|
|
118
|
+
@callback_called = true
|
119
|
+
response.should == "how are you Doug?"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
wait_for {@callback_called}
|
123
|
+
end
|
124
|
+
|
125
|
+
it "with complex response" do
|
126
|
+
@callback_called = false
|
127
|
+
@server.start
|
128
|
+
@connection.start do
|
129
|
+
@connection.hello_friend("Doug") do |response|
|
130
|
+
@callback_called = true
|
131
|
+
response.message.should == "How are you Doug, my friend?"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
wait_for {@callback_called}
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can get connection id on connection" do
|
139
|
+
@callback_called = false
|
140
|
+
@new_connection_id = nil
|
141
|
+
@server.start do |new_connection|
|
142
|
+
@new_connection_id = new_connection.conn_id
|
143
|
+
end
|
144
|
+
|
145
|
+
@connection.start do
|
146
|
+
@connection.save_id do |response|
|
147
|
+
@callback_called = true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
wait_for {@callback_called}
|
152
|
+
@server_api.conn_id.should == @new_connection_id
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should close from server side" do
|
156
|
+
@server_side_client_proxy = nil
|
157
|
+
@server.start do |new_connection|
|
158
|
+
@server_side_client_proxy = new_connection
|
159
|
+
end
|
160
|
+
|
161
|
+
@connection.errback do
|
162
|
+
@client_side_closed = true
|
163
|
+
end
|
164
|
+
@connection.start
|
165
|
+
|
166
|
+
wait_for {!@server_side_client_proxy.nil?}
|
167
|
+
@server_side_client_proxy.stop
|
168
|
+
|
169
|
+
wait_for {@client_side_closed}
|
170
|
+
@client_side_closed.should == true
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require "rubarb/outgoing_connection"
|
4
|
+
require "rubarb/remote_call"
|
5
|
+
|
6
|
+
describe Rubarb::OutgoingConnection do
|
7
|
+
include Rubarb::RemoteCall
|
8
|
+
include Rubarb::OutgoingConnection
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@callback = {}
|
12
|
+
set_id("00000001")
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_id(id)
|
16
|
+
id_generator = mock("id")
|
17
|
+
id_generator.stub!(:next).and_return(id)
|
18
|
+
@msg_id_generator = id_generator
|
19
|
+
end
|
20
|
+
|
21
|
+
it "sets callback" do
|
22
|
+
block = Proc.new { puts "Hello" }
|
23
|
+
self.should_receive(:send_message)
|
24
|
+
remote_call(:amethod, "asdf", &block)
|
25
|
+
@callback["00000001"].should == block
|
26
|
+
end
|
27
|
+
|
28
|
+
it "executes callback block when receive_message is called" do
|
29
|
+
block = Proc.new do |v|
|
30
|
+
@value = v
|
31
|
+
end
|
32
|
+
should_receive(:send_message)
|
33
|
+
remote_call(:amethod, "asdf", &block)
|
34
|
+
receive_message(marshal_call("00000001", "value"))
|
35
|
+
@value.should == "value"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not execute callback block when receive_message is called with an exception" do
|
39
|
+
block = Proc.new { puts "Hello" }
|
40
|
+
should_receive(:send_message)
|
41
|
+
remote_call(:amethod, "asdf", &block)
|
42
|
+
should_receive(:call_errbacks)
|
43
|
+
@callback.should_not_receive(:call)
|
44
|
+
receive_message(marshal_call("00000001", Exception.new))
|
45
|
+
end
|
46
|
+
|
47
|
+
it "saves error message when receive_message is called with an exception" do
|
48
|
+
exception = Exception.new("Hello")
|
49
|
+
error = nil
|
50
|
+
@errbacks = [Proc.new { |e| error = e }]
|
51
|
+
block = Proc.new { puts "Hello" }
|
52
|
+
|
53
|
+
should_receive(:send_message)
|
54
|
+
remote_call(:amethod, "asdf", &block)
|
55
|
+
stub!(:call_errbacks).and_return(@errbacks.first.call(exception))
|
56
|
+
@callback.stub!(:call)
|
57
|
+
|
58
|
+
receive_message(marshal_call("00000001", exception))
|
59
|
+
|
60
|
+
error.class.should == Exception
|
61
|
+
error.message.should == (exception.message)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "has a message id argument for marshal_call" do
|
65
|
+
block = Proc.new { puts "Hello" }
|
66
|
+
should_receive(:marshal_call).with("00000001", :amethod, "asdf")
|
67
|
+
self.should_receive(:send_message)
|
68
|
+
remote_call(:amethod, "asdf", &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "generates a message id" do
|
72
|
+
set_id("00000002")
|
73
|
+
block = Proc.new { puts "Hello" }
|
74
|
+
should_receive(:marshal_call).with("00000002", :amethod, "asdf")
|
75
|
+
should_receive(:send_message)
|
76
|
+
remote_call(:amethod, "asdf", &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "pushes callback block to callback hash with id as key" do
|
80
|
+
@callback = {}
|
81
|
+
set_id("00000001")
|
82
|
+
block = Proc.new { puts "Hello" }
|
83
|
+
stub!(:send_message)
|
84
|
+
remote_call(:amethod, "asdf", &block)
|
85
|
+
@callback["00000001"].should == block
|
86
|
+
end
|
87
|
+
|
88
|
+
it "calls the corresponding block with given id" do
|
89
|
+
block = mock("block")
|
90
|
+
block.should_receive(:call).with("asdf")
|
91
|
+
@callback = { "00000002" => block }
|
92
|
+
receive_message(marshal_call("00000002", "asdf"))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "removes callback from hash after call" do
|
96
|
+
block = mock("block")
|
97
|
+
block.should_receive(:call).with("asdf")
|
98
|
+
@callback = { "00000002" => block }
|
99
|
+
receive_message(marshal_call("00000002", "asdf"))
|
100
|
+
@callback.should have(0).items
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
require 'rubarb/remote_call'
|
4
|
+
describe Rubarb::RemoteCall do
|
5
|
+
include Rubarb::RemoteCall
|
6
|
+
|
7
|
+
it "should marshal a call" do
|
8
|
+
identity_test(:foo, "barr", "none")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should marshal with no args" do
|
12
|
+
identity_test(:food)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should work with multiple return values" do
|
16
|
+
serialized = marshal_call(:eat, "potatos", "carrots")
|
17
|
+
recovered_method, arg1, arg2 = unmarshal_call(serialized)
|
18
|
+
recovered_method.should == :eat
|
19
|
+
arg1.should == "potatos"
|
20
|
+
arg2.should == "carrots"
|
21
|
+
end
|
22
|
+
|
23
|
+
class Wine
|
24
|
+
attr_accessor :age, :content
|
25
|
+
def initialize(age, content)
|
26
|
+
@age = age
|
27
|
+
@content = content
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
return false unless self.age == other.age
|
32
|
+
return false unless self.content == other.content
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should handle complexy objects" do
|
38
|
+
identity_test(:make_grapes, Wine.new(15, 0.15), "grapes")
|
39
|
+
end
|
40
|
+
|
41
|
+
def identity_test(method, *args)
|
42
|
+
serialized = marshal_call(method, *args)
|
43
|
+
recovered_method, *recovered_args = unmarshal_call(serialized)
|
44
|
+
recovered_method.should == method
|
45
|
+
recovered_args.should == args
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require "rubarb/responder"
|
3
|
+
require "rubarb/incoming_connection"
|
4
|
+
require "rubarb/server"
|
5
|
+
|
6
|
+
include Rubarb
|
7
|
+
|
8
|
+
class MockHandler
|
9
|
+
include Listener
|
10
|
+
include IncomingConnection
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Responder do
|
14
|
+
before(:each) do
|
15
|
+
@responder = Responder.new(MockHandler.new, "00000001")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "sets id on initialize" do
|
19
|
+
@responder.message_id.should == "00000001"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets handler on initialize" do
|
23
|
+
@responder.handler.class.should == MockHandler
|
24
|
+
end
|
25
|
+
|
26
|
+
it "calls handler#reply with stored message_id" do
|
27
|
+
@responder.handler.should_receive(:reply).with("00000001", "Hello Doug")
|
28
|
+
@responder.reply("Hello Doug")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "calls handler's conn_id" do
|
32
|
+
@responder.handler.should_receive(:conn_id)
|
33
|
+
@responder.conn_id
|
34
|
+
end
|
35
|
+
end
|