em-promise 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +8 -5
- data/lib/em-promise.rb +1 -1
- data/lib/em-promise/{defer.rb → q.rb} +111 -50
- data/lib/em-promise/version.rb +1 -1
- data/spec/defer_spec.rb +15 -15
- metadata +3 -3
data/README.textile
CHANGED
@@ -8,11 +8,10 @@ From the perspective of dealing with error handling, deferred and promise apis a
|
|
8
8
|
|
9
9
|
<pre><code class="ruby">
|
10
10
|
require 'rubygems'
|
11
|
-
require 'eventmachine'
|
12
11
|
require 'em-promise'
|
13
12
|
|
14
13
|
def asyncGreet(name)
|
15
|
-
deferred = EM::
|
14
|
+
deferred = EM::Q.defer
|
16
15
|
|
17
16
|
EM::Timer.new(5) do
|
18
17
|
EM.defer do
|
@@ -26,13 +25,16 @@ end
|
|
26
25
|
|
27
26
|
EventMachine.run do
|
28
27
|
|
29
|
-
|
30
|
-
promise.then(proc { |greeting|
|
28
|
+
asyncGreet('Robin Hood').then(proc { |greeting|
|
31
29
|
p "Success: #{greeting}"
|
32
30
|
}, proc { |reason|
|
33
31
|
p "Failed: #{reason}"
|
34
32
|
})
|
35
33
|
|
34
|
+
asyncGreet('The Dude').then do |greeting|
|
35
|
+
p "Jeff '#{greeting}' Lebowski"
|
36
|
+
end
|
37
|
+
|
36
38
|
end
|
37
39
|
|
38
40
|
</code></pre>
|
@@ -40,4 +42,5 @@ end
|
|
40
42
|
|
41
43
|
h2. Start using it now
|
42
44
|
|
43
|
-
|
45
|
+
# Read the "Documentation":http://rubydoc.info/gems/em-promise/EventMachine/Q
|
46
|
+
# Then @gem install em-promise@
|
data/lib/em-promise.rb
CHANGED
@@ -1,28 +1,46 @@
|
|
1
1
|
|
2
2
|
|
3
3
|
module EventMachine
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
|
5
|
+
|
6
|
+
class Q
|
7
|
+
private_class_method :new
|
8
|
+
|
8
9
|
|
10
|
+
# @abstract
|
9
11
|
class Promise
|
12
|
+
private_class_method :new
|
10
13
|
end
|
11
14
|
|
15
|
+
|
16
|
+
#
|
17
|
+
# A new promise instance is created when a deferred instance is created and can be
|
18
|
+
# retrieved by calling deferred.promise
|
19
|
+
#
|
12
20
|
class DeferredPromise < Promise
|
21
|
+
public_class_method :new
|
22
|
+
|
13
23
|
def initialize(defer)
|
24
|
+
raise ArgumentError unless defer.is_a?(Deferred)
|
25
|
+
super()
|
26
|
+
|
14
27
|
@defer = defer
|
15
28
|
end
|
16
29
|
|
17
|
-
|
30
|
+
#
|
31
|
+
# regardless of when the promise was or will be resolved / rejected, calls one of
|
32
|
+
# the success or error callbacks asynchronously as soon as the result is available.
|
33
|
+
# The callbacks are called with a single argument, the result or rejection reason.
|
34
|
+
#
|
35
|
+
# @param [Proc, Proc, &blk] callbacks success, error, success_block
|
18
36
|
def then(callback = nil, errback = nil, &blk)
|
19
|
-
result =
|
37
|
+
result = Q.defer
|
20
38
|
|
21
39
|
callback ||= blk
|
22
40
|
|
23
|
-
wrappedCallback = proc { |
|
41
|
+
wrappedCallback = proc { |val|
|
24
42
|
begin
|
25
|
-
result.resolve(callback.nil? ?
|
43
|
+
result.resolve(callback.nil? ? val : callback.call(val))
|
26
44
|
rescue => e
|
27
45
|
warn "\nUnhandled exception: #{e.message}\n#{e.backtrace.join("\n")}\n"
|
28
46
|
result.reject(e);
|
@@ -31,7 +49,7 @@ module EventMachine
|
|
31
49
|
|
32
50
|
wrappedErrback = proc { |reason|
|
33
51
|
begin
|
34
|
-
result.resolve(errback.nil? ?
|
52
|
+
result.resolve(errback.nil? ? Q.reject(reason) : errback.call(reason))
|
35
53
|
rescue => e
|
36
54
|
warn "Unhandled exception: #{e.message}\n#{e.backtrace.join("\n")}\n"
|
37
55
|
result.reject(e);
|
@@ -39,8 +57,8 @@ module EventMachine
|
|
39
57
|
}
|
40
58
|
|
41
59
|
#
|
42
|
-
# Schedule as we are touching
|
43
|
-
#
|
60
|
+
# Schedule as we are touching shared state
|
61
|
+
# Everything else is locally scoped
|
44
62
|
#
|
45
63
|
EM.schedule do
|
46
64
|
pending_array = pending
|
@@ -56,7 +74,7 @@ module EventMachine
|
|
56
74
|
end
|
57
75
|
|
58
76
|
|
59
|
-
|
77
|
+
private
|
60
78
|
|
61
79
|
|
62
80
|
def pending
|
@@ -69,21 +87,26 @@ module EventMachine
|
|
69
87
|
end
|
70
88
|
|
71
89
|
|
90
|
+
|
72
91
|
class ResolvedPromise < Promise
|
92
|
+
public_class_method :new
|
93
|
+
|
73
94
|
def initialize(response, error = false)
|
74
95
|
raise ArgumentError if error && response.is_a?(Promise)
|
96
|
+
super()
|
97
|
+
|
75
98
|
@error = error
|
76
99
|
@response = response
|
77
100
|
end
|
78
101
|
|
79
102
|
def then(callback = nil, errback = nil, &blk)
|
80
|
-
result =
|
103
|
+
result = Q.defer
|
81
104
|
|
82
105
|
callback ||= blk
|
83
106
|
|
84
107
|
EM.next_tick {
|
85
108
|
if @error
|
86
|
-
result.resolve(errback.nil? ?
|
109
|
+
result.resolve(errback.nil? ? Q.reject(@response) : errback.call(@response))
|
87
110
|
else
|
88
111
|
result.resolve(callback.nil? ? @response : callback.call(@response))
|
89
112
|
end
|
@@ -94,36 +117,13 @@ module EventMachine
|
|
94
117
|
end
|
95
118
|
|
96
119
|
|
97
|
-
def initialize
|
98
|
-
@pending = []
|
99
|
-
@value = nil
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
def resolve(val = nil)
|
104
|
-
EM.schedule do
|
105
|
-
if !!@pending
|
106
|
-
callbacks = @pending
|
107
|
-
@pending = nil
|
108
|
-
@value = ref(val)
|
109
|
-
|
110
|
-
if callbacks.length > 0
|
111
|
-
callbacks.each do |callback|
|
112
|
-
@value.then(callback[0], callback[1])
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
def reject(reason = nil)
|
121
|
-
resolve(Defer.reject(reason))
|
122
|
-
end
|
123
|
-
|
124
120
|
|
125
|
-
|
126
|
-
|
121
|
+
#
|
122
|
+
# Creates a Deferred object which represents a task which will finish in the future.
|
123
|
+
#
|
124
|
+
# @return [Deferred] Returns a new instance of Deferred
|
125
|
+
def self.defer
|
126
|
+
return Deferred.new
|
127
127
|
end
|
128
128
|
|
129
129
|
|
@@ -143,7 +143,7 @@ module EventMachine
|
|
143
143
|
# #!/usr/bin/env ruby
|
144
144
|
#
|
145
145
|
# require 'rubygems' # or use Bundler.setup
|
146
|
-
# require '
|
146
|
+
# require 'em-promise'
|
147
147
|
#
|
148
148
|
# promiseB = promiseA.then(lambda {|result|
|
149
149
|
# # success: do something and resolve promiseB with the old or a new result
|
@@ -155,10 +155,11 @@ module EventMachine
|
|
155
155
|
# # handle the error and recover
|
156
156
|
# return newPromiseOrValue
|
157
157
|
# end
|
158
|
-
# return
|
158
|
+
# return Q.reject(reason)
|
159
159
|
# })
|
160
160
|
#
|
161
161
|
# @param [Object] reason constant, message, exception or an object representing the rejection reason.
|
162
|
+
# @return [Promise] Returns a promise that was already resolved as rejected with the reason
|
162
163
|
def self.reject(reason = nil)
|
163
164
|
return ResolvedPromise.new( reason, true ) # A resolved failed promise
|
164
165
|
end
|
@@ -168,12 +169,12 @@ module EventMachine
|
|
168
169
|
# promises are resolved.
|
169
170
|
#
|
170
171
|
# @param [Promise] a number of promises that will be combined into a single promise
|
171
|
-
# @
|
172
|
+
# @return [Promise] Returns a single promise that will be resolved with an array of values,
|
172
173
|
# each value corresponding to the promise at the same index in the `promises` array. If any of
|
173
174
|
# the promises is resolved with a rejection, this resulting promise will be resolved with the
|
174
175
|
# same rejection.
|
175
176
|
def self.all(*promises)
|
176
|
-
deferred =
|
177
|
+
deferred = Q.defer
|
177
178
|
counter = promises.length
|
178
179
|
results = []
|
179
180
|
|
@@ -198,19 +199,79 @@ module EventMachine
|
|
198
199
|
end
|
199
200
|
|
200
201
|
return deferred.promise
|
201
|
-
end
|
202
|
+
end
|
202
203
|
|
203
204
|
|
204
|
-
|
205
|
+
private
|
205
206
|
|
206
207
|
|
207
208
|
def self.ref(value)
|
208
209
|
return value if value.is_a?(Promise)
|
209
210
|
return ResolvedPromise.new( value ) # A resolved success promise
|
210
211
|
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
#
|
216
|
+
# The purpose of the deferred object is to expose the associated Promise instance as well
|
217
|
+
# as APIs that can be used for signalling the successful or unsuccessful completion of a task.
|
218
|
+
#
|
219
|
+
class Q::Deferred < Q
|
220
|
+
public_class_method :new
|
221
|
+
private_class_method :defer
|
222
|
+
private_class_method :reject
|
223
|
+
private_class_method :all
|
224
|
+
|
225
|
+
def initialize
|
226
|
+
super()
|
227
|
+
|
228
|
+
@pending = []
|
229
|
+
@value = nil
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# resolves the derived promise with the value. If the value is a rejection constructed via
|
234
|
+
# Q.reject, the promise will be rejected instead.
|
235
|
+
#
|
236
|
+
# @param [Object] val constant, message or an object representing the result.
|
237
|
+
def resolve(val = nil)
|
238
|
+
EM.schedule do
|
239
|
+
if not @pending.nil?
|
240
|
+
callbacks = @pending
|
241
|
+
@pending = nil
|
242
|
+
@value = ref(val)
|
243
|
+
|
244
|
+
if callbacks.length > 0
|
245
|
+
callbacks.each do |callback|
|
246
|
+
@value.then(callback[0], callback[1])
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# rejects the derived promise with the reason. This is equivalent to resolving it with a rejection
|
255
|
+
# constructed via Q.reject.
|
256
|
+
#
|
257
|
+
# @param [Object] reason constant, message, exception or an object representing the rejection reason.
|
258
|
+
def reject(reason = nil)
|
259
|
+
resolve(Q.reject(reason))
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Creates a promise object associated with this deferred
|
264
|
+
#
|
265
|
+
def promise
|
266
|
+
DeferredPromise.new( self )
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
private
|
271
|
+
|
211
272
|
|
212
273
|
def ref(value)
|
213
|
-
|
274
|
+
Deferred.ref(value) # Defined in Q (DRY)
|
214
275
|
end
|
215
276
|
end
|
216
277
|
|
data/lib/em-promise/version.rb
CHANGED
data/spec/defer_spec.rb
CHANGED
@@ -4,10 +4,10 @@ require 'bundler/setup'
|
|
4
4
|
require 'em-promise'
|
5
5
|
|
6
6
|
|
7
|
-
describe EventMachine::
|
7
|
+
describe EventMachine::Q do
|
8
8
|
|
9
9
|
before :each do
|
10
|
-
@deferred = EM::
|
10
|
+
@deferred = EM::Q.defer
|
11
11
|
@promise = @deferred.promise
|
12
12
|
@log = []
|
13
13
|
@default_fail = proc { |reason|
|
@@ -86,7 +86,7 @@ describe EventMachine::Defer do
|
|
86
86
|
|
87
87
|
|
88
88
|
it "should allow deferred resolution with a new promise" do
|
89
|
-
deferred2 = EM::
|
89
|
+
deferred2 = EM::Q.defer
|
90
90
|
EventMachine.run {
|
91
91
|
@promise.then(proc {|result|
|
92
92
|
result.should == :foo
|
@@ -190,7 +190,7 @@ describe EventMachine::Defer do
|
|
190
190
|
|
191
191
|
|
192
192
|
it "should not defer rejection with a new promise" do
|
193
|
-
deferred2 = EM::
|
193
|
+
deferred2 = EM::Q.defer
|
194
194
|
EventMachine.run {
|
195
195
|
@promise.then(@default_fail, @default_fail)
|
196
196
|
begin
|
@@ -205,7 +205,7 @@ describe EventMachine::Defer do
|
|
205
205
|
end
|
206
206
|
|
207
207
|
|
208
|
-
describe EventMachine::
|
208
|
+
describe EventMachine::Q::Promise do
|
209
209
|
|
210
210
|
describe 'then' do
|
211
211
|
|
@@ -314,7 +314,7 @@ describe EventMachine::Defer do
|
|
314
314
|
})
|
315
315
|
@promise.then(@default_fail, proc {|result|
|
316
316
|
@log << result
|
317
|
-
EM::
|
317
|
+
EM::Q.reject('some reason')
|
318
318
|
})
|
319
319
|
@promise.then(@default_fail, proc {|result|
|
320
320
|
@log << result
|
@@ -393,7 +393,7 @@ describe EventMachine::Defer do
|
|
393
393
|
|
394
394
|
it "should package a string into a rejected promise" do
|
395
395
|
EventMachine.run {
|
396
|
-
rejectedPromise = EM::
|
396
|
+
rejectedPromise = EM::Q.reject('not gonna happen')
|
397
397
|
|
398
398
|
@promise.then(nil, proc {|reason|
|
399
399
|
@log << reason
|
@@ -411,7 +411,7 @@ describe EventMachine::Defer do
|
|
411
411
|
|
412
412
|
it "should return a promise that forwards callbacks if the callbacks are missing" do
|
413
413
|
EventMachine.run {
|
414
|
-
rejectedPromise = EM::
|
414
|
+
rejectedPromise = EM::Q.reject('not gonna happen')
|
415
415
|
|
416
416
|
@promise.then(nil, proc {|reason|
|
417
417
|
@log << reason
|
@@ -436,7 +436,7 @@ describe EventMachine::Defer do
|
|
436
436
|
|
437
437
|
it "should resolve all of nothing" do
|
438
438
|
EventMachine.run {
|
439
|
-
EM::
|
439
|
+
EM::Q.all.then(proc {|result|
|
440
440
|
@log << result
|
441
441
|
}, @default_fail)
|
442
442
|
|
@@ -449,10 +449,10 @@ describe EventMachine::Defer do
|
|
449
449
|
|
450
450
|
it "should take an array of promises and return a promise for an array of results" do
|
451
451
|
EventMachine.run {
|
452
|
-
deferred1 = EM::
|
453
|
-
deferred2 = EM::
|
452
|
+
deferred1 = EM::Q.defer
|
453
|
+
deferred2 = EM::Q.defer
|
454
454
|
|
455
|
-
EM::
|
455
|
+
EM::Q.all(@promise, deferred1.promise, deferred2.promise).then(proc {|result|
|
456
456
|
result.should == [:foo, :bar, :baz]
|
457
457
|
EM.stop
|
458
458
|
}, @default_fail)
|
@@ -466,10 +466,10 @@ describe EventMachine::Defer do
|
|
466
466
|
|
467
467
|
it "should reject the derived promise if at least one of the promises in the array is rejected" do
|
468
468
|
EventMachine.run {
|
469
|
-
deferred1 = EM::
|
470
|
-
deferred2 = EM::
|
469
|
+
deferred1 = EM::Q.defer
|
470
|
+
deferred2 = EM::Q.defer
|
471
471
|
|
472
|
-
EM::
|
472
|
+
EM::Q.all(@promise, deferred1.promise, deferred2.promise).then(@default_fail, proc {|reason|
|
473
473
|
reason.should == :baz
|
474
474
|
EM.stop
|
475
475
|
})
|
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
|
4
|
+
version: 1.1.0
|
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-11-
|
12
|
+
date: 2012-11-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -50,7 +50,7 @@ executables: []
|
|
50
50
|
extensions: []
|
51
51
|
extra_rdoc_files: []
|
52
52
|
files:
|
53
|
-
- lib/em-promise/
|
53
|
+
- lib/em-promise/q.rb
|
54
54
|
- lib/em-promise/version.rb
|
55
55
|
- lib/em-promise.rb
|
56
56
|
- MIT-LICENSE
|