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 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
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc "Run all RSpec tests"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
10
+ task :test => [:spec]
data/lib/em-promise.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'eventmachine'
2
+
3
+ require 'em-promise/defer.rb'
@@ -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
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ class Defer
3
+ VERSION = "1.0.1"
4
+ end
5
+ 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.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-20 00:00:00.000000000 Z
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