ezmobius-nanite 0.4.0 → 0.4.1.1
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.rdoc +70 -20
- data/Rakefile +1 -1
- data/bin/nanite-agent +34 -8
- data/bin/nanite-mapper +18 -8
- data/lib/nanite.rb +71 -0
- data/lib/nanite/actor.rb +60 -0
- data/lib/nanite/actor_registry.rb +24 -0
- data/lib/nanite/admin.rb +138 -0
- data/lib/nanite/agent.rb +250 -0
- data/lib/nanite/amqp.rb +47 -0
- data/lib/nanite/cluster.rb +203 -0
- data/lib/nanite/config.rb +102 -0
- data/lib/nanite/console.rb +39 -0
- data/lib/nanite/daemonize.rb +13 -0
- data/lib/nanite/dispatcher.rb +90 -0
- data/lib/nanite/identity.rb +16 -0
- data/lib/nanite/job.rb +104 -0
- data/lib/nanite/local_state.rb +34 -0
- data/lib/nanite/log.rb +64 -0
- data/lib/nanite/log/formatter.rb +39 -0
- data/lib/nanite/mapper.rb +277 -0
- data/lib/nanite/mapper_proxy.rb +56 -0
- data/lib/nanite/packets.rb +231 -0
- data/lib/nanite/pid_file.rb +52 -0
- data/lib/nanite/reaper.rb +38 -0
- data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
- data/lib/nanite/security/certificate.rb +55 -0
- data/lib/nanite/security/certificate_cache.rb +66 -0
- data/lib/nanite/security/distinguished_name.rb +34 -0
- data/lib/nanite/security/encrypted_document.rb +46 -0
- data/lib/nanite/security/rsa_key_pair.rb +53 -0
- data/lib/nanite/security/secure_serializer.rb +67 -0
- data/lib/nanite/security/signature.rb +40 -0
- data/lib/nanite/security/static_certificate_store.rb +35 -0
- data/lib/nanite/security_provider.rb +47 -0
- data/lib/nanite/serializer.rb +52 -0
- data/lib/nanite/state.rb +164 -0
- data/lib/nanite/streaming.rb +125 -0
- data/lib/nanite/util.rb +51 -0
- data/spec/actor_registry_spec.rb +62 -0
- data/spec/actor_spec.rb +59 -0
- data/spec/agent_spec.rb +235 -0
- data/spec/cached_certificate_store_proxy_spec.rb +34 -0
- data/spec/certificate_cache_spec.rb +49 -0
- data/spec/certificate_spec.rb +27 -0
- data/spec/cluster_spec.rb +300 -0
- data/spec/dispatcher_spec.rb +136 -0
- data/spec/distinguished_name_spec.rb +24 -0
- data/spec/encrypted_document_spec.rb +21 -0
- data/spec/job_spec.rb +219 -0
- data/spec/local_state_spec.rb +112 -0
- data/spec/packet_spec.rb +218 -0
- data/spec/rsa_key_pair_spec.rb +33 -0
- data/spec/secure_serializer_spec.rb +41 -0
- data/spec/serializer_spec.rb +107 -0
- data/spec/signature_spec.rb +30 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/static_certificate_store_spec.rb +30 -0
- data/spec/util_spec.rb +63 -0
- metadata +63 -2
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::Certificate do
|
4
|
+
|
5
|
+
include SpecHelpers
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@certificate, key = issue_cert
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should save' do
|
12
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
13
|
+
@certificate.save(filename)
|
14
|
+
File.size(filename).should be > 0
|
15
|
+
File.delete(filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should load' do
|
19
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
20
|
+
@certificate.save(filename)
|
21
|
+
cert = Nanite::Certificate.load(filename)
|
22
|
+
File.delete(filename)
|
23
|
+
cert.should_not be_nil
|
24
|
+
cert.data.should == @certificate.data
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::Cluster do
|
4
|
+
|
5
|
+
describe "Intialization" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@fanout = mock("fanout")
|
9
|
+
@binding = mock("binding", :subscribe => true)
|
10
|
+
@queue = mock("queue", :bind => @binding)
|
11
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
12
|
+
@serializer = mock("Serializer")
|
13
|
+
@reaper = mock("Reaper")
|
14
|
+
@mapper = mock("Mapper")
|
15
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "of Heartbeat (Queue)" do
|
19
|
+
|
20
|
+
it "should setup the heartbeat (queue) for id" do
|
21
|
+
@amq.should_receive(:queue).with("heartbeat-the_identity", anything()).and_return(@queue)
|
22
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should make the heartbeat (queue) exclusive" do
|
26
|
+
@amq.should_receive(:queue).with("heartbeat-the_identity", { :exclusive => true }).and_return(@queue)
|
27
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should bind the heartbeat (queue) to 'heartbeat' fanout" do
|
31
|
+
@amq.should_receive(:fanout).with("heartbeat", { :durable => true }).and_return(@fanout)
|
32
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
33
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
34
|
+
end
|
35
|
+
|
36
|
+
end # of Heartbeat (Queue)
|
37
|
+
|
38
|
+
|
39
|
+
describe "of Registration (Queue)" do
|
40
|
+
|
41
|
+
it "should setup the registration (queue) for id" do
|
42
|
+
@amq.should_receive(:queue).with("registration-the_identity", anything()).and_return(@queue)
|
43
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should make the registration (queue) exclusive" do
|
47
|
+
@amq.should_receive(:queue).with("registration-the_identity", { :exclusive => true }).and_return(@queue)
|
48
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should bind the registration (queue) to 'registration' fanout" do
|
52
|
+
@amq.should_receive(:fanout).with("registration", { :durable => true }).and_return(@fanout)
|
53
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
54
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
55
|
+
end
|
56
|
+
|
57
|
+
end # of Registration (Queue)
|
58
|
+
|
59
|
+
describe "of Request (Queue)" do
|
60
|
+
|
61
|
+
it "should setup the request (queue) for id" do
|
62
|
+
@amq.should_receive(:queue).with("request-the_identity", anything()).and_return(@queue)
|
63
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should make the request (queue) exclusive" do
|
67
|
+
@amq.should_receive(:queue).with("request-the_identity", { :exclusive => true }).and_return(@queue)
|
68
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should bind the request (queue) to 'request' fanout" do
|
72
|
+
@amq.should_receive(:fanout).with("request", { :durable => true }).and_return(@fanout)
|
73
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
74
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
75
|
+
end
|
76
|
+
|
77
|
+
end # of Request (Queue)
|
78
|
+
|
79
|
+
|
80
|
+
describe "Reaper" do
|
81
|
+
|
82
|
+
it "should be created" do
|
83
|
+
Nanite::Reaper.should_receive(:new).with(anything()).and_return(@reaper)
|
84
|
+
cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should use the agent timeout" do
|
88
|
+
Nanite::Reaper.should_receive(:new).with(443).and_return(@reaper)
|
89
|
+
cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
|
90
|
+
end
|
91
|
+
|
92
|
+
end # Reaper
|
93
|
+
|
94
|
+
end # Intialization
|
95
|
+
|
96
|
+
|
97
|
+
describe "Target Selection" do
|
98
|
+
|
99
|
+
before(:each) do
|
100
|
+
@fanout = mock("fanout")
|
101
|
+
@binding = mock("binding", :subscribe => true)
|
102
|
+
@queue = mock("queue", :bind => @binding)
|
103
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
104
|
+
@serializer = mock("Serializer")
|
105
|
+
@reaper = mock("Reaper")
|
106
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
107
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should return array containing targets for request" do
|
111
|
+
target = mock("Supplied Target")
|
112
|
+
request = mock("Request", :target => target)
|
113
|
+
@cluster.targets_for(request).should be_instance_of(Array)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should use target from request" do
|
117
|
+
target = mock("Supplied Target")
|
118
|
+
request = mock("Request", :target => target)
|
119
|
+
@cluster.targets_for(request).should == [target]
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should use targets choosen by least loaded selector (:least_loaded)" do
|
123
|
+
targets = { "target 3" => 3 }
|
124
|
+
request = mock("Request", :target => nil, :selector => :least_loaded, :type => "service", :tags => [])
|
125
|
+
@cluster.should_receive(:least_loaded).with("service", []).and_return(targets)
|
126
|
+
@cluster.targets_for(request).should == ["target 3"]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should use targets choosen by all selector (:all)" do
|
130
|
+
targets = { "target 1" => 1, "target 2" => 2, "target 3" => 3 }
|
131
|
+
request = mock("Request", :target => nil, :selector => :all, :type => "service", :tags => [])
|
132
|
+
@cluster.should_receive(:all).with("service", []).and_return(targets)
|
133
|
+
@cluster.targets_for(request).should == ["target 1", "target 2", "target 3"]
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should use targets choosen by random selector (:random)" do
|
137
|
+
targets = { "target 3" => 3 }
|
138
|
+
request = mock("Request", :target => nil, :selector => :random, :type => "service", :tags => [])
|
139
|
+
@cluster.should_receive(:random).with("service", []).and_return(targets)
|
140
|
+
@cluster.targets_for(request).should == ["target 3"]
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should use targets choosen by round-robin selector (:rr)" do
|
144
|
+
targets = { "target 2" => 2 }
|
145
|
+
request = mock("Request", :target => nil, :selector => :rr, :type => "service", :tags => [])
|
146
|
+
@cluster.should_receive(:rr).with("service", []).and_return(targets)
|
147
|
+
@cluster.targets_for(request).should == ["target 2"]
|
148
|
+
end
|
149
|
+
|
150
|
+
end # Target Selection
|
151
|
+
|
152
|
+
|
153
|
+
describe "Nanite Registration" do
|
154
|
+
|
155
|
+
before(:each) do
|
156
|
+
@fanout = mock("fanout")
|
157
|
+
@binding = mock("binding", :subscribe => true)
|
158
|
+
@queue = mock("queue", :bind => @binding)
|
159
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
160
|
+
@serializer = mock("Serializer")
|
161
|
+
@reaper = mock("Reaper", :timeout => true)
|
162
|
+
Nanite::Log.stub!(:info)
|
163
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
164
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
165
|
+
@register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should add the Nanite to the nanites map" do
|
169
|
+
@cluster.register(@register_packet)
|
170
|
+
@cluster.nanites['nanite_id'].should_not be_nil
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should use hash of the Nanite's services and status as value" do
|
174
|
+
@cluster.register(@register_packet)
|
175
|
+
@cluster.nanites['nanite_id'].keys.size == 2
|
176
|
+
@cluster.nanites['nanite_id'].keys.should include(:services)
|
177
|
+
@cluster.nanites['nanite_id'].keys.should include(:status)
|
178
|
+
@cluster.nanites['nanite_id'][:services].should == ["the_nanite_services"]
|
179
|
+
@cluster.nanites['nanite_id'][:status].should == "nanite_status"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should add nanite to reaper" do
|
183
|
+
@reaper.should_receive(:timeout).with('nanite_id', 33)
|
184
|
+
@cluster.register(@register_packet)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should log info message that nanite was registered" do
|
188
|
+
Nanite::Log.should_receive(:info)
|
189
|
+
@cluster.register(@register_packet)
|
190
|
+
end
|
191
|
+
|
192
|
+
end # Nanite Registration
|
193
|
+
|
194
|
+
|
195
|
+
describe "Route" do
|
196
|
+
|
197
|
+
before(:each) do
|
198
|
+
@fanout = mock("fanout")
|
199
|
+
@binding = mock("binding", :subscribe => true)
|
200
|
+
@queue = mock("queue", :bind => @binding)
|
201
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
202
|
+
@serializer = mock("Serializer")
|
203
|
+
@reaper = mock("Reaper")
|
204
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
205
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
206
|
+
@request = mock("Request")
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should publish request to all targets" do
|
210
|
+
target1 = mock("Target 1")
|
211
|
+
target2 = mock("Target 2")
|
212
|
+
@cluster.should_receive(:publish).with(@request, target1)
|
213
|
+
@cluster.should_receive(:publish).with(@request, target2)
|
214
|
+
EM.run {
|
215
|
+
@cluster.route(@request, [target1, target2])
|
216
|
+
EM.stop
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
end # Route
|
221
|
+
|
222
|
+
|
223
|
+
describe "Publish" do
|
224
|
+
|
225
|
+
before(:each) do
|
226
|
+
@fanout = mock("fanout")
|
227
|
+
@binding = mock("binding", :subscribe => true)
|
228
|
+
@queue = mock("queue", :bind => @binding, :publish => true)
|
229
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
230
|
+
@serializer = mock("Serializer", :dump => "dumped_value")
|
231
|
+
@reaper = mock("Reaper")
|
232
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
233
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
234
|
+
@request = mock("Request", :persistent => true)
|
235
|
+
@target = mock("Target of Request")
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should serialize request before publishing it" do
|
239
|
+
@request.should_receive(:target=).with(@target)
|
240
|
+
@request.should_receive(:target=)
|
241
|
+
@request.should_receive(:target)
|
242
|
+
@serializer.should_receive(:dump).with(@request).and_return("serialized_request")
|
243
|
+
@cluster.publish(@request, @target)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should publish request to target queue" do
|
247
|
+
@request.should_receive(:target=).with(@target)
|
248
|
+
@request.should_receive(:target=)
|
249
|
+
@request.should_receive(:target)
|
250
|
+
@queue.should_receive(:publish).with("dumped_value", anything())
|
251
|
+
@cluster.publish(@request, @target)
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should persist request based on request setting" do
|
255
|
+
@request.should_receive(:target=).with(@target)
|
256
|
+
@request.should_receive(:target=)
|
257
|
+
@request.should_receive(:target)
|
258
|
+
@request.should_receive(:persistent).and_return(false)
|
259
|
+
@queue.should_receive(:publish).with(anything(), { :persistent => false })
|
260
|
+
@cluster.publish(@request, @target)
|
261
|
+
end
|
262
|
+
|
263
|
+
end # Publish
|
264
|
+
|
265
|
+
describe "Agent Request Handling" do
|
266
|
+
|
267
|
+
before(:each) do
|
268
|
+
@fanout = mock("fanout")
|
269
|
+
@binding = mock("binding", :subscribe => true)
|
270
|
+
@queue = mock("queue", :bind => @binding, :publish => true)
|
271
|
+
@amq = mock("AMPQueue", :queue => @queue, :fanout => @fanout)
|
272
|
+
@serializer = mock("Serializer", :dump => "dumped_value")
|
273
|
+
@target = mock("Target of Request")
|
274
|
+
@reaper = mock("Reaper")
|
275
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
276
|
+
@request_without_target = mock("Request", :target => nil, :token => "Token",
|
277
|
+
:reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity")
|
278
|
+
@request_with_target = mock("Request", :target => "Target", :token => "Token",
|
279
|
+
:reply_to => "Reply To", :from => "From", :persistent => true)
|
280
|
+
@mapper_with_target = mock("Mapper", :identity => "id")
|
281
|
+
@mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
|
282
|
+
@cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
|
283
|
+
@cluster_without_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_without_target)
|
284
|
+
Nanite::Cluster.stub!(:mapper).and_return(@mapper)
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should forward requests with targets" do
|
288
|
+
@mapper_with_target.should_receive(:send_request).with(@request_with_target, anything())
|
289
|
+
@cluster_with_target.__send__(:handle_request, @request_with_target)
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should reply back with nil results for requests with no target when offline queue is disabled" do
|
293
|
+
@mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
|
294
|
+
Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
|
295
|
+
@cluster_without_target.__send__(:handle_request, @request_without_target)
|
296
|
+
end
|
297
|
+
|
298
|
+
end # Agent Request Handling
|
299
|
+
|
300
|
+
end # Nanite::Cluster
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
include Nanite::Actor
|
5
|
+
expose :bar, :index, :i_kill_you
|
6
|
+
on_exception :handle_exception
|
7
|
+
|
8
|
+
def index(payload)
|
9
|
+
bar(payload)
|
10
|
+
end
|
11
|
+
|
12
|
+
def bar(payload)
|
13
|
+
['hello', payload]
|
14
|
+
end
|
15
|
+
|
16
|
+
def bar2(payload, deliverable)
|
17
|
+
deliverable
|
18
|
+
end
|
19
|
+
|
20
|
+
def i_kill_you(payload)
|
21
|
+
raise RuntimeError.new('I kill you!')
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_exception(method, deliverable, error)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Bar
|
29
|
+
include Nanite::Actor
|
30
|
+
expose :i_kill_you
|
31
|
+
on_exception do |method, deliverable, error|
|
32
|
+
@scope = self
|
33
|
+
@called_with = [method, deliverable, error]
|
34
|
+
end
|
35
|
+
|
36
|
+
def i_kill_you(payload)
|
37
|
+
raise RuntimeError.new('I kill you!')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# No specs, simply ensures multiple methods for assigning on_exception callback,
|
42
|
+
# on_exception raises exception when called with an invalid argument.
|
43
|
+
class Doomed
|
44
|
+
include Nanite::Actor
|
45
|
+
on_exception do
|
46
|
+
end
|
47
|
+
on_exception lambda {}
|
48
|
+
on_exception :doh
|
49
|
+
end
|
50
|
+
|
51
|
+
# Mock the EventMachine deferrer.
|
52
|
+
class EMMock
|
53
|
+
def self.defer(op = nil, callback = nil)
|
54
|
+
callback.call(op.call)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "Nanite::Dispatcher" do
|
59
|
+
|
60
|
+
before(:each) do
|
61
|
+
Nanite::Log.stub!(:info)
|
62
|
+
Nanite::Log.stub!(:error)
|
63
|
+
amq = mock('amq', :queue => mock('queue', :publish => nil))
|
64
|
+
@actor = Foo.new
|
65
|
+
@registry = Nanite::ActorRegistry.new
|
66
|
+
@registry.register(@actor, nil)
|
67
|
+
@dispatcher = Nanite::Dispatcher.new(amq, @registry, Nanite::Serializer.new(:marshal), '0xfunkymonkey', {})
|
68
|
+
@dispatcher.evmclass = EMMock
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should dispatch a request" do
|
72
|
+
req = Nanite::Request.new('/foo/bar', 'you')
|
73
|
+
res = @dispatcher.dispatch(req)
|
74
|
+
res.should(be_kind_of(Nanite::Result))
|
75
|
+
res.token.should == req.token
|
76
|
+
res.results.should == ['hello', 'you']
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should dispatch the deliverable to actions that accept it" do
|
80
|
+
req = Nanite::Request.new('/foo/bar2', 'you')
|
81
|
+
res = @dispatcher.dispatch(req)
|
82
|
+
res.should(be_kind_of(Nanite::Result))
|
83
|
+
res.token.should == req.token
|
84
|
+
res.results.should == req
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should dispatch a request to the default action" do
|
88
|
+
req = Nanite::Request.new('/foo', 'you')
|
89
|
+
res = @dispatcher.dispatch(req)
|
90
|
+
res.should(be_kind_of(Nanite::Result))
|
91
|
+
res.token.should == req.token
|
92
|
+
res.results.should == ['hello', 'you']
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should handle custom prefixes" do
|
96
|
+
@registry.register(Foo.new, 'umbongo')
|
97
|
+
req = Nanite::Request.new('/umbongo/bar', 'you')
|
98
|
+
res = @dispatcher.dispatch(req)
|
99
|
+
res.should(be_kind_of(Nanite::Result))
|
100
|
+
res.token.should == req.token
|
101
|
+
res.results.should == ['hello', 'you']
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should call the on_exception callback if something goes wrong" do
|
105
|
+
req = Nanite::Request.new('/foo/i_kill_you', nil)
|
106
|
+
@actor.should_receive(:handle_exception).with(:i_kill_you, req, duck_type(:exception, :backtrace))
|
107
|
+
@dispatcher.dispatch(req)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should call on_exception Procs defined in a subclass with the correct arguments" do
|
111
|
+
actor = Bar.new
|
112
|
+
@registry.register(actor, nil)
|
113
|
+
req = Nanite::Request.new('/bar/i_kill_you', nil)
|
114
|
+
@dispatcher.dispatch(req)
|
115
|
+
called_with = actor.instance_variable_get("@called_with")
|
116
|
+
called_with[0].should == :i_kill_you
|
117
|
+
called_with[1].should == req
|
118
|
+
called_with[2].should be_kind_of(RuntimeError)
|
119
|
+
called_with[2].message.should == 'I kill you!'
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should call on_exception Procs defined in a subclass in the scope of the actor" do
|
123
|
+
actor = Bar.new
|
124
|
+
@registry.register(actor, nil)
|
125
|
+
req = Nanite::Request.new('/bar/i_kill_you', nil)
|
126
|
+
@dispatcher.dispatch(req)
|
127
|
+
actor.instance_variable_get("@scope").should == actor
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should log error if something goes wrong" do
|
131
|
+
Nanite::Log.should_receive(:error)
|
132
|
+
req = Nanite::Request.new('/foo/i_kill_you', nil)
|
133
|
+
@dispatcher.dispatch(req)
|
134
|
+
end
|
135
|
+
|
136
|
+
end # Nanite::Dispatcher
|