em-promise 1.0.0 → 1.0.1

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.
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