em-promise 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +1,20 @@
1
- Copyright 2012 YOURNAME
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -24,9 +24,7 @@ module EventMachine
24
24
  begin
25
25
  result.resolve(callback.nil? ? value : callback.call(value))
26
26
  rescue => e
27
- #
28
- # TODO:: add debugging output here
29
- #
27
+ warn "\nUnhandled exception: #{e.message}\n#{e.backtrace.join("\n")}\n"
30
28
  result.reject(e);
31
29
  end
32
30
  }
@@ -35,9 +33,7 @@ module EventMachine
35
33
  begin
36
34
  result.resolve(errback.nil? ? Defer.reject(reason) : errback.call(reason))
37
35
  rescue => e
38
- #
39
- # TODO:: add debugging output here
40
- #
36
+ warn "Unhandled exception: #{e.message}\n#{e.backtrace.join("\n")}\n"
41
37
  result.reject(e);
42
38
  end
43
39
  }
@@ -75,6 +71,7 @@ module EventMachine
75
71
 
76
72
  class ResolvedPromise < Promise
77
73
  def initialize(response, error = false)
74
+ raise ArgumentError if error && response.is_a?(Promise)
78
75
  @error = error
79
76
  @response = response
80
77
  end
@@ -111,11 +108,9 @@ module EventMachine
111
108
  @value = ref(val)
112
109
 
113
110
  if callbacks.length > 0
114
- EM.next_tick {
115
- callbacks.each do |callback|
116
- @value.then(callback[0], callback[1])
117
- end
118
- }
111
+ callbacks.each do |callback|
112
+ @value.then(callback[0], callback[1])
113
+ end
119
114
  end
120
115
  end
121
116
  end
@@ -168,14 +163,55 @@ module EventMachine
168
163
  return ResolvedPromise.new( reason, true ) # A resolved failed promise
169
164
  end
170
165
 
166
+ #
167
+ # Combines multiple promises into a single promise that is resolved when all of the input
168
+ # promises are resolved.
169
+ #
170
+ # @param [Promise] a number of promises that will be combined into a single promise
171
+ # @returns [Promise] Returns a single promise that will be resolved with an array of values,
172
+ # each value corresponding to the promise at the same index in the `promises` array. If any of
173
+ # the promises is resolved with a rejection, this resulting promise will be resolved with the
174
+ # same rejection.
175
+ def self.all(*promises)
176
+ deferred = Defer.new
177
+ counter = promises.length
178
+ results = []
179
+
180
+ if counter > 0
181
+ promises.each_index do |index|
182
+ ref(promises[index]).then(proc {|result|
183
+ if results[index].nil?
184
+ results[index] = result
185
+ counter -= 1
186
+ deferred.resolve(results) if counter <= 0
187
+ end
188
+ result
189
+ }, proc {|reason|
190
+ if results[index].nil?
191
+ deferred.reject(reason)
192
+ end
193
+ reason
194
+ })
195
+ end
196
+ else
197
+ deferred.resolve(results)
198
+ end
199
+
200
+ return deferred.promise
201
+ end
202
+
171
203
 
172
204
  protected
173
205
 
174
206
 
175
- def ref(value)
207
+ def self.ref(value)
176
208
  return value if value.is_a?(Promise)
177
209
  return ResolvedPromise.new( value ) # A resolved success promise
178
210
  end
211
+
212
+ def ref(value)
213
+ Defer.ref(value)
214
+ end
179
215
  end
180
216
 
181
217
  end
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  class Defer
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
5
5
  end
@@ -5,26 +5,482 @@ require 'em-promise'
5
5
 
6
6
 
7
7
  describe EventMachine::Defer do
8
+
9
+ before :each do
10
+ @deferred = EM::Defer.new
11
+ @promise = @deferred.promise
12
+ @log = []
13
+ @default_fail = proc { |reason|
14
+ fail(reason)
15
+ EM.stop
16
+ }
17
+ end
18
+
19
+
20
+ describe 'resolve' do
21
+
22
+
23
+ it "should call the callback in the next turn" do
24
+ deferred2 = EM::Defer.new
25
+ EventMachine.run {
26
+ @promise.then(proc {|result|
27
+ @log << result
28
+ }, @default_fail)
29
+
30
+ @deferred.resolve(:foo)
31
+
32
+ EM.next_tick do
33
+ @log.should == [:foo]
34
+ EM.stop
35
+ end
36
+ }
37
+ end
38
+
39
+
40
+ it "should fulfill success callbacks in the registration order" do
41
+ EventMachine.run {
42
+ @promise.then(proc {|result|
43
+ @log << :first
44
+ }, @default_fail)
45
+
46
+ @promise.then(proc {|result|
47
+ @log << :second
48
+ }, @default_fail)
49
+
50
+ @deferred.resolve(:foo)
51
+
52
+ EM.next_tick do
53
+ @log.should == [:first, :second]
54
+ EM.stop
55
+ end
56
+ }
57
+ end
58
+
59
+
60
+ it "should do nothing if a promise was previously resolved" do
61
+ EventMachine.run {
62
+ @promise.then(proc {|result|
63
+ @log << result
64
+ @log.should == [:foo]
65
+ @deferred.resolve(:bar)
66
+ }, @default_fail)
67
+
68
+ @deferred.resolve(:foo)
69
+ @deferred.reject(:baz)
70
+
71
+ #
72
+ # 4 ticks should detect any errors
73
+ #
74
+ EM.next_tick do
75
+ EM.next_tick do
76
+ EM.next_tick do
77
+ EM.next_tick do
78
+ @log.should == [:foo]
79
+ EM.stop
80
+ end
81
+ end
82
+ end
83
+ end
84
+ }
85
+ end
86
+
87
+
88
+ it "should allow deferred resolution with a new promise" do
89
+ deferred2 = EM::Defer.new
90
+ EventMachine.run {
91
+ @promise.then(proc {|result|
92
+ result.should == :foo
93
+ EM.stop
94
+ }, @default_fail)
95
+
96
+ @deferred.resolve(deferred2.promise)
97
+ deferred2.resolve(:foo)
98
+ }
99
+ end
100
+
101
+
102
+ it "should not break if a callbacks registers another callback" do
103
+ EventMachine.run {
104
+ @promise.then(proc {|result|
105
+ @log << :outer
106
+ @promise.then(proc {|result|
107
+ @log << :inner
108
+ }, @default_fail)
109
+ }, @default_fail)
110
+
111
+ @deferred.resolve(:foo)
112
+
113
+ EM.next_tick do
114
+ EM.next_tick do
115
+ @log.should == [:outer, :inner]
116
+ EM.stop
117
+ end
118
+ end
119
+ }
120
+ end
121
+
122
+
123
+
124
+ it "can modify the result of a promise before returning" do
125
+ EventMachine.run {
126
+ proc { |name|
127
+ EM.defer { @deferred.resolve("Hello #{name}") }
128
+ @promise.then(proc {|result|
129
+ result.should == 'Hello Robin Hood'
130
+ result += "?"
131
+ result
132
+ })
133
+ }.call('Robin Hood').then(proc { |greeting|
134
+ greeting.should == 'Hello Robin Hood?'
135
+ EM.stop
136
+ }, @default_fail)
137
+ }
138
+ end
139
+
140
+ end
141
+
142
+
143
+ describe 'reject' do
144
+
145
+ it "should reject the promise and execute all error callbacks" do
146
+ EventMachine.run {
147
+ @promise.then(@default_fail, proc {|result|
148
+ @log << :first
149
+ })
150
+ @promise.then(@default_fail, proc {|result|
151
+ @log << :second
152
+ })
153
+
154
+ @deferred.reject(:foo)
155
+
156
+ EM.next_tick do
157
+ @log.should == [:first, :second]
158
+ EM.stop
159
+ end
160
+ }
161
+ end
162
+
163
+
164
+ it "should do nothing if a promise was previously rejected" do
165
+ EventMachine.run {
166
+ @promise.then(@default_fail, proc {|result|
167
+ @log << result
168
+ @log.should == [:baz]
169
+ @deferred.resolve(:bar)
170
+ })
171
+
172
+ @deferred.reject(:baz)
173
+ @deferred.resolve(:foo)
174
+
175
+ #
176
+ # 4 ticks should detect any errors
177
+ #
178
+ EM.next_tick do
179
+ EM.next_tick do
180
+ EM.next_tick do
181
+ EM.next_tick do
182
+ @log.should == [:baz]
183
+ EM.stop
184
+ end
185
+ end
186
+ end
187
+ end
188
+ }
189
+ end
190
+
191
+
192
+ it "should not defer rejection with a new promise" do
193
+ deferred2 = EM::Defer.new
194
+ EventMachine.run {
195
+ @promise.then(@default_fail, @default_fail)
196
+ begin
197
+ @deferred.reject(deferred2.promise)
198
+ rescue => e
199
+ e.is_a?(ArgumentError).should == true
200
+ EM.stop
201
+ end
202
+ }
203
+ end
204
+
205
+ end
206
+
207
+
208
+ describe EventMachine::Defer::Promise do
209
+
210
+ describe 'then' do
211
+
212
+ it "should allow registration of a success callback without an errback and resolve" do
213
+ EventMachine.run {
214
+ @promise.then(proc {|result|
215
+ @log << result
216
+ })
217
+
218
+ @deferred.resolve(:foo)
219
+
220
+ EM.next_tick do
221
+ @log.should == [:foo]
222
+ EM.stop
223
+ end
224
+ }
225
+ end
226
+
227
+
228
+ it "should allow registration of a success callback without an errback and reject" do
229
+ EventMachine.run {
230
+ @promise.then(proc {|result|
231
+ @log << result
232
+ })
233
+
234
+ @deferred.reject(:foo)
235
+
236
+ EM.next_tick do
237
+ @log.should == []
238
+ EM.stop
239
+ end
240
+ }
241
+ end
242
+
243
+
244
+ it "should allow registration of an errback without a success callback and reject" do
245
+ EventMachine.run {
246
+ @promise.then(nil, proc {|reason|
247
+ @log << reason
248
+ })
249
+
250
+ @deferred.reject(:foo)
251
+
252
+ EM.next_tick do
253
+ @log.should == [:foo]
254
+ EM.stop
255
+ end
256
+ }
257
+ end
258
+
259
+
260
+ it "should allow registration of an errback without a success callback and resolve" do
261
+ EventMachine.run {
262
+ @promise.then(nil, proc {|reason|
263
+ @log << reason
264
+ })
8
265
 
266
+ @deferred.resolve(:foo)
267
+
268
+ EM.next_tick do
269
+ @log.should == []
270
+ EM.stop
271
+ end
272
+ }
273
+ end
274
+
275
+
276
+ it "should resolve all callbacks with the original value" do
277
+ EventMachine.run {
278
+ @promise.then(proc {|result|
279
+ @log << result
280
+ :alt1
281
+ }, @default_fail)
282
+ @promise.then(proc {|result|
283
+ @log << result
284
+ 'ERROR'
285
+ }, @default_fail)
286
+ @promise.then(proc {|result|
287
+ @log << result
288
+ EM::Defer.reject('some reason')
289
+ }, @default_fail)
290
+ @promise.then(proc {|result|
291
+ @log << result
292
+ :alt2
293
+ }, @default_fail)
294
+
295
+ @deferred.resolve(:foo)
296
+
297
+ EM.next_tick do
298
+ @log.should == [:foo, :foo, :foo, :foo]
299
+ EM.stop
300
+ end
301
+ }
302
+ end
303
+
304
+
305
+ it "should reject all callbacks with the original reason" do
306
+ EventMachine.run {
307
+ @promise.then(@default_fail, proc {|result|
308
+ @log << result
309
+ :alt1
310
+ })
311
+ @promise.then(@default_fail, proc {|result|
312
+ @log << result
313
+ 'ERROR'
314
+ })
315
+ @promise.then(@default_fail, proc {|result|
316
+ @log << result
317
+ EM::Defer.reject('some reason')
318
+ })
319
+ @promise.then(@default_fail, proc {|result|
320
+ @log << result
321
+ :alt2
322
+ })
323
+
324
+ @deferred.reject(:foo)
325
+
326
+ EM.next_tick do
327
+ @log.should == [:foo, :foo, :foo, :foo]
328
+ EM.stop
329
+ end
330
+ }
331
+ end
332
+
333
+
334
+ it "should propagate resolution and rejection between dependent promises" do
335
+ EventMachine.run {
336
+ @promise.then(proc {|result|
337
+ @log << result
338
+ :bar
339
+ }, @default_fail).then(proc {|result|
340
+ @log << result
341
+ raise 'baz'
342
+ }, @default_fail).then(@default_fail, proc {|result|
343
+ @log << result.message
344
+ raise 'bob'
345
+ }).then(@default_fail, proc {|result|
346
+ @log << result.message
347
+ :done
348
+ }).then(proc {|result|
349
+ @log << result
350
+ }, @default_fail)
351
+
352
+ @deferred.resolve(:foo)
353
+
354
+ EM.next_tick do
355
+ EM.next_tick do
356
+ EM.next_tick do
357
+ EM.next_tick do
358
+ EM.next_tick do
359
+ @log.should == [:foo, :bar, 'baz', 'bob', :done]
360
+ EM.stop
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end
366
+ }
367
+ end
368
+
369
+
370
+ it "should call error callback in the next turn even if promise is already rejected" do
371
+ EventMachine.run {
372
+ @deferred.reject(:foo)
373
+
374
+ @promise.then(nil, proc {|reason|
375
+ @log << reason
376
+ })
377
+
378
+ EM.next_tick do
379
+ @log.should == [:foo]
380
+ EM.stop
381
+ end
382
+ }
383
+ end
384
+
385
+
386
+ end
387
+
388
+ end
389
+
9
390
 
10
- it "should fulfill the promise and execute all success callbacks in the registration order" do
11
- EventMachine.run {
12
- proc { |name|
13
- deferred = EM::Defer.new
14
- EM.defer { deferred.resolve("Hello #{name}") }
15
- deferred.promise.then(proc {|result|
16
- result += "?"
17
- result
391
+
392
+ describe 'reject' do
393
+
394
+ it "should package a string into a rejected promise" do
395
+ EventMachine.run {
396
+ rejectedPromise = EM::Defer.reject('not gonna happen')
397
+
398
+ @promise.then(nil, proc {|reason|
399
+ @log << reason
18
400
  })
19
- }.call('Robin Hood').then(proc { |greeting|
20
- greeting.should == 'Hello Robin Hood?'
21
- EventMachine.stop
22
- }, proc { |reason|
23
- fail(reason)
24
- EventMachine.stop
25
- })
26
- }
401
+
402
+ @deferred.resolve(rejectedPromise)
403
+
404
+ EM.next_tick do
405
+ @log.should == ['not gonna happen']
406
+ EM.stop
407
+ end
408
+ }
409
+ end
410
+
411
+
412
+ it "should return a promise that forwards callbacks if the callbacks are missing" do
413
+ EventMachine.run {
414
+ rejectedPromise = EM::Defer.reject('not gonna happen')
415
+
416
+ @promise.then(nil, proc {|reason|
417
+ @log << reason
418
+ })
419
+
420
+ @deferred.resolve(rejectedPromise.then())
421
+
422
+ EM.next_tick do
423
+ EM.next_tick do
424
+ @log.should == ['not gonna happen']
425
+ EM.stop
426
+ end
427
+ end
428
+ }
429
+ end
430
+
27
431
  end
432
+
433
+
434
+
435
+ describe 'all' do
436
+
437
+ it "should resolve all of nothing" do
438
+ EventMachine.run {
439
+ EM::Defer.all().then(proc {|result|
440
+ @log << result
441
+ }, @default_fail)
442
+
443
+ EM.next_tick do
444
+ @log.should == [[]]
445
+ EM.stop
446
+ end
447
+ }
448
+ end
449
+
450
+ it "should take an array of promises and return a promise for an array of results" do
451
+ EventMachine.run {
452
+ deferred1 = EM::Defer.new
453
+ deferred2 = EM::Defer.new
454
+
455
+ EM::Defer.all(@promise, deferred1.promise, deferred2.promise).then(proc {|result|
456
+ result.should == [:foo, :bar, :baz]
457
+ EM.stop
458
+ }, @default_fail)
459
+
460
+ EM.defer { @deferred.resolve(:foo) }
461
+ EM.defer { deferred2.resolve(:baz) }
462
+ EM.defer { deferred1.resolve(:bar) }
463
+ }
464
+ end
465
+
466
+
467
+ it "should reject the derived promise if at least one of the promises in the array is rejected" do
468
+ EventMachine.run {
469
+ deferred1 = EM::Defer.new
470
+ deferred2 = EM::Defer.new
471
+
472
+ EM::Defer.all(@promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
473
+ reason.should == :baz
474
+ EM.stop
475
+ })
476
+
477
+ EM.defer { @deferred.resolve(:foo) }
478
+ EM.defer { deferred2.reject(:baz) }
479
+ }
480
+ end
481
+
482
+ end
483
+
28
484
 
29
485
 
30
486
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-promise
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-21 00:00:00.000000000 Z
12
+ date: 2012-10-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine