em-promise 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.textile +32 -0
- data/Rakefile +10 -0
- data/lib/em-promise.rb +3 -0
- data/lib/em-promise/defer.rb +181 -0
- data/lib/em-promise/version.rb +5 -0
- data/spec/defer.rb +30 -0
- metadata +12 -4
data/MIT-LICENSE
ADDED
@@ -0,0 +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.
|
data/README.textile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
h1. EM-Promise
|
2
|
+
|
3
|
+
A promise/deferred implementation inspired by "AngularJS":http://docs.angularjs.org/api/ng.$q see this documentation for use cases.
|
4
|
+
|
5
|
+
From the perspective of dealing with error handling, deferred and promise apis are to asynchronous programing what try, catch and throw keywords are to synchronous programming.
|
6
|
+
|
7
|
+
<pre><code class="ruby">
|
8
|
+
def asyncGreet(name)
|
9
|
+
deferred = EM::Defer.new
|
10
|
+
|
11
|
+
EM::Timer.new(5) do
|
12
|
+
EM.defer do
|
13
|
+
deferred.resolve("Hello #{name}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
deferred.promise
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
EventMachine.run do
|
22
|
+
|
23
|
+
promise = asyncGreet('Robin Hood')
|
24
|
+
promise.then(proc { |greeting|
|
25
|
+
p "Success: #{greeting}"
|
26
|
+
}, proc { |reason|
|
27
|
+
p "Failed: #{reason}"
|
28
|
+
})
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
</code></pre>
|
data/Rakefile
ADDED
data/lib/em-promise.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
#
|
5
|
+
# Creates a 'Deferred' object which represents a task which will finish in the future.
|
6
|
+
#
|
7
|
+
class Defer
|
8
|
+
|
9
|
+
class Promise
|
10
|
+
end
|
11
|
+
|
12
|
+
class DeferredPromise < Promise
|
13
|
+
def initialize(defer)
|
14
|
+
@defer = defer
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def then(callback = nil, errback = nil, &blk)
|
19
|
+
result = Defer.new
|
20
|
+
|
21
|
+
callback ||= blk
|
22
|
+
|
23
|
+
wrappedCallback = proc { |value|
|
24
|
+
begin
|
25
|
+
result.resolve(callback.nil? ? value : callback.call(value))
|
26
|
+
rescue => e
|
27
|
+
#
|
28
|
+
# TODO:: add debugging output here
|
29
|
+
#
|
30
|
+
result.reject(e);
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
wrappedErrback = proc { |reason|
|
35
|
+
begin
|
36
|
+
result.resolve(errback.nil? ? Defer.reject(reason) : errback.call(reason))
|
37
|
+
rescue => e
|
38
|
+
#
|
39
|
+
# TODO:: add debugging output here
|
40
|
+
#
|
41
|
+
result.reject(e);
|
42
|
+
end
|
43
|
+
}
|
44
|
+
|
45
|
+
#
|
46
|
+
# Schedule as we are touching arrays
|
47
|
+
# => Everything else is locally scoped
|
48
|
+
#
|
49
|
+
EM.schedule do
|
50
|
+
pending_array = pending
|
51
|
+
|
52
|
+
if pending_array.nil?
|
53
|
+
value.then(wrappedCallback, wrappedErrback)
|
54
|
+
else
|
55
|
+
pending_array << [wrappedCallback, wrappedErrback]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
result.promise
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
|
66
|
+
def pending
|
67
|
+
@defer.instance_eval { @pending }
|
68
|
+
end
|
69
|
+
|
70
|
+
def value
|
71
|
+
@defer.instance_eval { @value }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
class ResolvedPromise < Promise
|
77
|
+
def initialize(response, error = false)
|
78
|
+
@error = error
|
79
|
+
@response = response
|
80
|
+
end
|
81
|
+
|
82
|
+
def then(callback = nil, errback = nil, &blk)
|
83
|
+
result = Defer.new
|
84
|
+
|
85
|
+
callback ||= blk
|
86
|
+
|
87
|
+
EM.next_tick {
|
88
|
+
if @error
|
89
|
+
result.resolve(errback.nil? ? Defer.reject(@response) : errback.call(@response))
|
90
|
+
else
|
91
|
+
result.resolve(callback.nil? ? @response : callback.call(@response))
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
result.promise
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def initialize
|
101
|
+
@pending = []
|
102
|
+
@value = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def resolve(val = nil)
|
107
|
+
EM.schedule do
|
108
|
+
if !!@pending
|
109
|
+
callbacks = @pending
|
110
|
+
@pending = nil
|
111
|
+
@value = ref(val)
|
112
|
+
|
113
|
+
if callbacks.length > 0
|
114
|
+
EM.next_tick {
|
115
|
+
callbacks.each do |callback|
|
116
|
+
@value.then(callback[0], callback[1])
|
117
|
+
end
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def reject(reason = nil)
|
126
|
+
resolve(Defer.reject(reason))
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def promise
|
131
|
+
DeferredPromise.new( self )
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
#
|
136
|
+
# Creates a promise that is resolved as rejected with the specified reason. This api should be
|
137
|
+
# used to forward rejection in a chain of promises. If you are dealing with the last promise in
|
138
|
+
# a promise chain, you don't need to worry about it.
|
139
|
+
#
|
140
|
+
# When comparing deferreds/promises to the familiar behaviour of try/catch/throw, think of
|
141
|
+
# reject as the raise keyword in Ruby. This also means that if you "catch" an error via
|
142
|
+
# a promise error callback and you want to forward the error to the promise derived from the
|
143
|
+
# current promise, you have to "rethrow" the error by returning a rejection constructed via
|
144
|
+
# reject.
|
145
|
+
#
|
146
|
+
# @example handling rejections
|
147
|
+
#
|
148
|
+
# #!/usr/bin/env ruby
|
149
|
+
#
|
150
|
+
# require 'rubygems' # or use Bundler.setup
|
151
|
+
# require 'eventmachine'
|
152
|
+
#
|
153
|
+
# promiseB = promiseA.then(lambda {|result|
|
154
|
+
# # success: do something and resolve promiseB with the old or a new result
|
155
|
+
# return result
|
156
|
+
# }, lambda {|reason|
|
157
|
+
# # error: handle the error if possible and resolve promiseB with newPromiseOrValue,
|
158
|
+
# # otherwise forward the rejection to promiseB
|
159
|
+
# if canHandle(reason)
|
160
|
+
# # handle the error and recover
|
161
|
+
# return newPromiseOrValue
|
162
|
+
# end
|
163
|
+
# return Defer.reject(reason)
|
164
|
+
# })
|
165
|
+
#
|
166
|
+
# @param [Object] reason constant, message, exception or an object representing the rejection reason.
|
167
|
+
def self.reject(reason = nil)
|
168
|
+
return ResolvedPromise.new( reason, true ) # A resolved failed promise
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
protected
|
173
|
+
|
174
|
+
|
175
|
+
def ref(value)
|
176
|
+
return value if value.is_a?(Promise)
|
177
|
+
return ResolvedPromise.new( value ) # A resolved success promise
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
data/spec/defer.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'em-promise'
|
5
|
+
|
6
|
+
|
7
|
+
describe EventMachine::Defer do
|
8
|
+
|
9
|
+
|
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
|
18
|
+
})
|
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
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
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.
|
4
|
+
version: 1.0.1
|
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-
|
12
|
+
date: 2012-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -49,7 +49,14 @@ email:
|
|
49
49
|
executables: []
|
50
50
|
extensions: []
|
51
51
|
extra_rdoc_files: []
|
52
|
-
files:
|
52
|
+
files:
|
53
|
+
- lib/em-promise/defer.rb
|
54
|
+
- lib/em-promise/version.rb
|
55
|
+
- lib/em-promise.rb
|
56
|
+
- MIT-LICENSE
|
57
|
+
- Rakefile
|
58
|
+
- README.textile
|
59
|
+
- spec/defer.rb
|
53
60
|
homepage: https://github.com/cotag/em-promise
|
54
61
|
licenses: []
|
55
62
|
post_install_message:
|
@@ -74,4 +81,5 @@ rubygems_version: 1.8.24
|
|
74
81
|
signing_key:
|
75
82
|
specification_version: 3
|
76
83
|
summary: EventMachine based, promise/deferred implementation
|
77
|
-
test_files:
|
84
|
+
test_files:
|
85
|
+
- spec/defer.rb
|