jamesgolick-ASS 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.
@@ -0,0 +1,25 @@
1
+ # class Topic
2
+ # def initialize(name,opts={})
3
+ # @exchange = MQ.topic(name,opts)
4
+ # end
5
+
6
+ # def publish(key,payload,opts={})
7
+ # @exchange.publish(::Marshal.dump(payload),opts.merge(:routing_key => key))
8
+ # end
9
+
10
+ # def subscribe(matcher,opts={},&block)
11
+ # ack = opts.delete(:ack)
12
+ # uid = "#{@exchange.name}.topic.#{rand 999_999_999_999}"
13
+ # q = MQ.queue(uid,opts)
14
+ # q.bind(@exchange.name,:key => matcher)
15
+ # q.subscribe(:ack => ack) { |info,payload|
16
+ # payload = ::Marshal.load(payload)
17
+ # if block.arity == 2
18
+ # block.call(info,payload)
19
+ # else
20
+ # block.call(payload)
21
+ # end
22
+ # }
23
+ # q
24
+ # end
25
+ # end
@@ -0,0 +1,84 @@
1
+ require 'rubygems'
2
+ require "lib/ass"
3
+ require "spec"
4
+ require 'rant/spec'
5
+ require 'thread'
6
+
7
+ require 'eventmachine'
8
+ EM.threadpool_size = 1
9
+
10
+ describe "Actor" do
11
+ before do
12
+ q = Queue.new
13
+ @server = nil
14
+ @thread = Thread.new {ASS.start {
15
+ q << :ready
16
+ }}
17
+ @thread.abort_on_exception = true
18
+ q.pop.should == :ready
19
+ end
20
+
21
+ after do
22
+ ASS.stop
23
+ @thread.join
24
+ end
25
+
26
+ it "should respond by dispatch to methods" do
27
+ q = Queue.new
28
+ actor = ASS.actor("spec") {
29
+ define_method(:foo) do |i|
30
+ r = [:foo,i]
31
+ q << r
32
+ r
33
+ end
34
+
35
+ define_method(:bar) do |i|
36
+ r = [:bar,i]
37
+ q << r
38
+ r
39
+ end
40
+ }
41
+ #a.cast("spec",:foo,1)
42
+ actor.cast("spec",:foo,1)
43
+ q.pop.should == [:foo,1]
44
+ actor.cast("spec",:bar,2)
45
+ q.pop.should == [:bar,2]
46
+
47
+ actor.call("spec",:bar,3) # calling to self
48
+ q.pop.should == [:bar,3] # input
49
+ q.pop.should == [:bar,[:bar,3]] # output
50
+ end
51
+
52
+ it "should handle error by calling on_error" do
53
+ q = Queue.new
54
+ actor = ASS.actor("spec") {
55
+ define_method(:on_error) do |e,data|
56
+ q << [e,data]
57
+ end
58
+ }
59
+ actor.cast("spec",:foo,1)
60
+ e, data = q.pop
61
+ e.should be_a(NoMethodError)
62
+ data.should == 1
63
+ end
64
+
65
+ it "should be able to make service calls" do
66
+ q = Queue.new
67
+ a1 = ASS.actor("spec") {
68
+ define_method(:foo) do |i|
69
+ q << [:foo,i]
70
+ cast("spec2",:bar,i+1)
71
+ end
72
+ }
73
+ a2 = ASS.actor("spec2") do
74
+ define_method(:bar) do |i|
75
+ q << [:bar,i]
76
+ end
77
+ end
78
+ a1.cast("spec",:foo,1)
79
+ q.pop.should == [:foo,1]
80
+ q.pop.should == [:bar,2]
81
+ end
82
+
83
+
84
+ end
@@ -0,0 +1,291 @@
1
+ require 'rubygems'
2
+ require "lib/ass"
3
+ require "spec"
4
+ require 'rant/spec'
5
+ require 'thread'
6
+
7
+ require 'eventmachine'
8
+ EM.threadpool_size = 1
9
+
10
+ describe "ASS" do
11
+ include Rant::Check
12
+
13
+ def server(*args,&block)
14
+ name = args.grep(String).first || "spec"
15
+ opts = args.grep(Hash).first || {}
16
+ ASS.server(name,opts) {
17
+ define_method(:on_call,&block)
18
+ }
19
+ end
20
+
21
+ def call(data,opts={},meta=nil)
22
+ ASS.call("spec",nil,data,{
23
+ :reply_to => "spec"
24
+ }.merge(opts),meta)
25
+ end
26
+
27
+ def cast(data,opts={},meta=nil)
28
+ ASS.cast("spec",nil,data,opts,meta)
29
+ end
30
+
31
+
32
+ it "should start and stop" do
33
+ 30.times do
34
+ begin
35
+ q = Queue.new
36
+ s = nil
37
+ thread = Thread.new {
38
+ ASS.start {
39
+ s = server("spec") do |i|
40
+ q << :ok
41
+ end
42
+ q << :ready
43
+ }
44
+ q << :done
45
+ }
46
+ thread.abort_on_exception = true
47
+ q.pop.should == :ready
48
+ cast(0)
49
+ q.pop.should == :ok
50
+ ASS.stop
51
+ q.pop.should == :done
52
+ rescue => e
53
+ ASS.stop
54
+ raise e
55
+ end
56
+ end
57
+ end
58
+
59
+ it "should stop ASS if server does not respond to on_error" do
60
+ begin
61
+ old_stderr = $stderr
62
+ error_output = StringIO.new
63
+ $stderr = error_output
64
+ q = Queue.new
65
+ s = nil
66
+ t = Thread.new {
67
+ ASS.start {
68
+ s = server("spec") do |i|
69
+ raise "ouch" if i == :die
70
+ q << :ok
71
+ i
72
+ end
73
+ q << :ready
74
+ }
75
+ q << :died
76
+ }
77
+ q.pop.should == :ready
78
+ cast(1)
79
+ q.pop.should == :ok
80
+ call(:die)
81
+ q.pop.should == :died
82
+ $stderr.string.should_not be_empty
83
+ EM.reactor_running?.should == false
84
+ rescue => e
85
+ ASS.stop
86
+ raise e
87
+ ensure
88
+ $stderr = old_stderr
89
+ end
90
+ end
91
+
92
+
93
+ it "should resend a message if server did not ack a message" do
94
+ begin
95
+ old_stderr = $stderr
96
+ error_output = StringIO.new
97
+ $stderr = error_output
98
+ q = Queue.new
99
+ s = nil
100
+ t1 = Thread.new {
101
+ ASS.start {
102
+ s = ASS.server("spec").react(:ack => true) do
103
+ define_method(:on_call) do |i|
104
+ raise "ouch" if i == :die
105
+ q << [header,i]
106
+ i
107
+ end
108
+ end
109
+ q << :ready
110
+ }
111
+ q << :died
112
+ }
113
+ q.pop.should == :ready
114
+ cast(1)
115
+ header, msg = q.pop
116
+ msg.should == 1
117
+ header.redelivered != true
118
+ cast(:die)
119
+ q.pop.should == :died
120
+ t1.join
121
+ $stderr.string.should_not be_empty
122
+ EM.reactor_running?.should == false
123
+
124
+ # restart server to get have the message resent
125
+ t2 = Thread.new {
126
+ ASS.start {
127
+ s = server { |msg|
128
+ q << [header,msg]
129
+ }
130
+ }
131
+ }
132
+ header, msg = q.pop
133
+ msg.should == :die
134
+ header.redelivered == true
135
+ ASS.stop
136
+ t2.join
137
+ ensure
138
+ $stderr = old_stderr
139
+ ASS.stop
140
+ end
141
+ end
142
+
143
+ describe "server" do
144
+ before do
145
+ q = Queue.new
146
+ @server = nil
147
+ @thread = Thread.new {ASS.start {
148
+ q << :ready
149
+ }}
150
+ @thread.abort_on_exception = true
151
+ q.pop.should == :ready
152
+ end
153
+
154
+ after do
155
+ ASS.stop
156
+ @thread.join
157
+ end
158
+
159
+ it "should process message with on_call" do
160
+ input = Queue.new
161
+ output = Queue.new
162
+ s = server("spec") do |i|
163
+ input << i if i == 0
164
+ output << i if i == 1 # this is the response to self
165
+ i + 1
166
+ end
167
+ # note that when calling self we have the
168
+ # wierdness of the response sending back to
169
+ # self.
170
+ 10.times { call(0) }
171
+ 10.times.map { input.pop }.uniq.should == [0]
172
+ 10.times.map { output.pop }.uniq.should == [1]
173
+ end
174
+
175
+ it "should handle error with on_error" do
176
+ errors = Queue.new
177
+ msgs = Queue.new
178
+ s = ASS.server("spec") {
179
+ def on_call(i)
180
+ raise "aieee"
181
+ end
182
+
183
+ define_method(:on_error) do |e,msg|
184
+ msgs << msg
185
+ errors << e
186
+ end
187
+ }
188
+ 10.times { call(0) }
189
+ 10.times {
190
+ msgs.pop.should == 0
191
+ #errors.pop.is_a?(Exception).should == true
192
+ errors.pop.should be_a(RuntimeError)
193
+ }
194
+ end
195
+
196
+ it "should have access to magic service methods" do
197
+ q = Queue.new
198
+ s = server { |i|
199
+ q << [header,method,data,meta]
200
+ }
201
+ cast(1,{},:meta)
202
+ header,method,data,meta = q.pop
203
+ header.should be_an(MQ::Header)
204
+ method.should be_nil
205
+ data.should == 1
206
+ meta.should == :meta
207
+ end
208
+
209
+
210
+ it "should use a new callback instance to process each request" do
211
+ q = Queue.new
212
+ s = server { |i|
213
+ q << self
214
+ i
215
+ }
216
+ 2000.times {|i|
217
+ begin
218
+ cast(1)
219
+ rescue => e
220
+ p "broken at #{i}"
221
+ raise e
222
+ end
223
+ }
224
+ # we need to hold on to the saved pointers,
225
+ # otherwise objects would get garbage
226
+ # collected and their object_ids
227
+ # reused. This is the case with C-ruby.
228
+ saved_pointers = []
229
+ ids = (0...2000).map {
230
+ o = q.pop
231
+ saved_pointers << o
232
+ o.object_id
233
+ }
234
+ #pp s.objs
235
+ ids.uniq.length.should == ids.length
236
+ end
237
+
238
+ it "should receive messages in order from a connection" do
239
+ q = Queue.new
240
+ s = server do |i|
241
+ q << i
242
+ i
243
+ end
244
+ 100.times { |i|
245
+ cast(i)
246
+ }
247
+ 100.times { |i|
248
+ q.pop == i
249
+ }
250
+ end
251
+
252
+ it "should resend message" do
253
+ i = 0
254
+ q = Queue.new
255
+ s = server do |data|
256
+ q << [header,method,data,meta]
257
+ # requeue the first 100 messages
258
+ i += 1
259
+ if i <= 100
260
+ resend
261
+ end
262
+ true
263
+ end
264
+ 100.times { |i|
265
+ cast(i,{ :message_id => i }, i)
266
+ }
267
+ # the first 100 should be the same message as the messages to 200
268
+
269
+ msgs100 = 100.times.map { q.pop }
270
+ msgs200 = 100.times.map { q.pop }
271
+ msgs100.zip(msgs200) { |m1,m2|
272
+ header1,method1,data1,meta1 = m1
273
+ header2,method2,data2,meta2 = m2
274
+
275
+ # everything should be the same except the delivery_tag
276
+ header2.delivery_tag.should_not == header1.delivery_tag
277
+ # requeue is different from AMQP's redelivery
278
+ ## it should resend the message as another one.
279
+ header1.redelivered.should_not == true
280
+ header2.redelivered.should_not == true
281
+
282
+ header2.message_id.should == header2.message_id
283
+ method2.should == method1
284
+ data2.should == data1
285
+ meta1.should == meta2
286
+ }
287
+ end
288
+
289
+ end
290
+
291
+ end
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require "lib/ass"
3
+ require "spec"
4
+ require 'rant/spec'
5
+ require 'thread'
6
+
7
+ require 'eventmachine'
8
+ EM.threadpool_size = 1
9
+
10
+ describe "RPC" do
11
+ before do
12
+ q = Queue.new
13
+ @server = nil
14
+ @thread = Thread.new {ASS.start(:logging => false) {
15
+ q << :ready
16
+ }}
17
+ @thread.abort_on_exception = true
18
+ q.pop.should == :ready
19
+ end
20
+
21
+ after do
22
+ ASS.stop
23
+ @thread.join
24
+ end
25
+
26
+ it "should make synchronized rpc call" do
27
+ ASS.actor("spec") do
28
+ def foo(i)
29
+ i
30
+ end
31
+ end
32
+ c = ASS.client
33
+ c.call("spec",:foo,1).wait.should == 1
34
+ end
35
+
36
+ it "should do cast" do
37
+ q = Queue.new
38
+ ASS.actor("spec") do
39
+ define_method(:foo) do |i|
40
+ q << i
41
+ i
42
+ end
43
+ end
44
+ c = ASS.client
45
+ c.cast("spec",:foo,1)
46
+ q.pop.should == 1
47
+ end
48
+ end
49
+
50
+