rosarium 0.1.6 → 0.1.7

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rosarium/promise.rb +86 -105
  3. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7953c9f3adbdebbd4299ddef80c064fe76f32fe74e8bd8abafd160d0f9b6d816
4
- data.tar.gz: 80b937b9e1e8750f1bbac9a9913a0e129b5dc80048d97b3ea360774b02eb1e2e
3
+ metadata.gz: 3789051cf47569f1729bb161433ecb033274b49833e774eec788e5884c490897
4
+ data.tar.gz: f48ef4f93bc8cadb68d9069d3dd3f8434b45ce43e6f3057918912d97d72f5432
5
5
  SHA512:
6
- metadata.gz: 22821d81eaccb0357841982eb2095c57e58f786d535834600b5903719c180d91af364b9e8778ac1256236d19db95835262fd77b8c313bedafdf60ae0e04a7579
7
- data.tar.gz: 34ac659ece6ea48a111db7dab0ea89f8c0adcf90cfcf0802aaecba89eec49b1e8831fb9d55147691c4b686e51c1e68e38164fcd6dd27b4464fd5363ca802bcc9
6
+ metadata.gz: 3e108887ddb2cdee8bc0e5abe53915e249028d5f367a7c617874460a4b7b5ec19ca04c364631e3b6235c0da80c2ae2951501e08c6b280dd2609455bdbe9f7858
7
+ data.tar.gz: 7622eab1b92fcee040b91394422f8b7f1f2c78be004c62988d5e588bf528ab6718da0bc97d3adb8b24022b22498d80843e31c051b5b83ed2f4fed308ff288370
@@ -3,20 +3,20 @@
3
3
  module Rosarium
4
4
  class Promise
5
5
 
6
- DEFAULT_ON_FULFILL = proc { |value| value }
7
- DEFAULT_ON_REJECT = proc { |reason| raise reason }
8
-
9
6
  private_class_method :new
10
7
 
11
8
  def self.defer
12
9
  promise = new
13
10
 
14
- resolver = ->(value) { promise.send(:try_settle, value, nil) }
11
+ resolver = lambda do |value|
12
+ promise.send(:try_settle, value, nil)
13
+ nil # do not leak
14
+ end
15
15
 
16
16
  rejecter = lambda do |reason|
17
17
  raise "reason must be an Exception" unless reason.is_a?(Exception)
18
-
19
18
  promise.send(:try_settle, nil, reason)
19
+ nil # do not leak
20
20
  end
21
21
 
22
22
  Deferred.new(promise, resolver, rejecter)
@@ -57,7 +57,7 @@ module Rosarium
57
57
  end
58
58
 
59
59
  promises.each do |promise|
60
- promise.then(check, &check)
60
+ promise.send(:when_settled, &check)
61
61
  end
62
62
 
63
63
  deferred.promise
@@ -72,60 +72,27 @@ module Rosarium
72
72
  waiting_for = promises.count
73
73
  mutex = Mutex.new
74
74
 
75
- do_reject = ->(reason) { deferred.reject(reason) }
76
- do_fulfill = proc do
77
- # Only fulfilled (not rejected), so hits zero iff all promises were fulfilled
78
- if mutex.synchronize { (waiting_for -= 1) == 0 }
79
- deferred.resolve(promises.map(&:value))
75
+ check = lambda do |promise|
76
+ if promise.fulfilled?
77
+ # Hits zero iff all promises were fulfilled
78
+ if mutex.synchronize { (waiting_for -= 1) == 0 }
79
+ deferred.resolve(promises.map(&:value))
80
+ end
81
+ else
82
+ deferred.reject(promise.reason)
80
83
  end
81
84
  end
82
85
 
83
86
  promises.each do |promise|
84
- promise.then(do_reject, &do_fulfill)
85
- end
86
-
87
- deferred.promise
88
- end
89
-
90
- def then(on_rejected = nil, &on_fulfilled)
91
- deferred = self.class.defer
92
-
93
- on_fulfilled ||= DEFAULT_ON_FULFILL
94
- on_rejected ||= DEFAULT_ON_REJECT
95
-
96
- when_settled do
97
- EXECUTOR.submit do
98
- begin
99
- deferred.resolve(
100
- if fulfilled?
101
- # User-supplied code
102
- on_fulfilled.call value
103
- else
104
- # User-supplied code
105
- on_rejected.call reason
106
- end
107
- )
108
- rescue Exception => e
109
- deferred.reject e
110
- end
111
- end
87
+ promise.send(:when_settled) { check.call(promise) }
112
88
  end
113
89
 
114
90
  deferred.promise
115
91
  end
116
92
 
117
- def rescue(&block)
118
- self.then(block)
119
- end
120
-
121
- alias catch rescue
122
- alias on_error rescue
123
-
124
- public
125
-
126
93
  def initialize
127
94
  @state = :pending
128
- @resolving = false
95
+ @settling = false
129
96
  @mutex = Mutex.new
130
97
  @condition = ConditionVariable.new
131
98
  @when_settled = []
@@ -136,15 +103,23 @@ module Rosarium
136
103
  end
137
104
 
138
105
  def value
139
- wait
106
+ wait_until_settled
140
107
  synchronize { @value }
141
108
  end
142
109
 
143
110
  def reason
144
- wait
111
+ wait_until_settled
145
112
  synchronize { @reason }
146
113
  end
147
114
 
115
+ def value!
116
+ wait_until_settled
117
+ synchronize do
118
+ raise @reason if @state == :rejected
119
+ @value
120
+ end
121
+ end
122
+
148
123
  def inspect
149
124
  synchronize do
150
125
  r = { state: @state }
@@ -162,24 +137,43 @@ module Rosarium
162
137
  state == :rejected
163
138
  end
164
139
 
165
- def value!
166
- wait
167
- synchronize do
168
- raise @reason if @state == :rejected
169
- @value
140
+ def then(on_rejected = nil, &on_fulfilled)
141
+ deferred = self.class.defer
142
+
143
+ when_settled do
144
+ EXECUTOR.submit do
145
+ begin
146
+ deferred.resolve(
147
+ if fulfilled?
148
+ # User-supplied code
149
+ on_fulfilled ? on_fulfilled.call(value) : value
150
+ else
151
+ # User-supplied code
152
+ on_rejected ? on_rejected.call(reason) : raise(reason)
153
+ end
154
+ )
155
+ rescue Exception => e
156
+ deferred.reject e
157
+ end
158
+ end
170
159
  end
160
+
161
+ deferred.promise
171
162
  end
172
163
 
173
- private
164
+ def rescue(&block)
165
+ self.then(block)
166
+ end
174
167
 
175
- def wait
176
- when_settled do
177
- synchronize { @condition.broadcast }
178
- end
168
+ alias catch rescue
169
+ alias on_error rescue
179
170
 
171
+ private
172
+
173
+ def wait_until_settled
180
174
  synchronize do
181
175
  loop do
182
- return if @state == :fulfilled || @state == :rejected
176
+ return if @state != :pending
183
177
  @condition.wait @mutex
184
178
  end
185
179
  end
@@ -189,68 +183,55 @@ module Rosarium
189
183
  @mutex.synchronize(&block)
190
184
  end
191
185
 
186
+ # Can be called more than once
192
187
  def try_settle(value, reason)
193
- callbacks = []
194
- add_when_settled = false
188
+ settle_with = nil
195
189
 
196
190
  synchronize do
197
- if @state == :pending && !@resolving
198
- if value.is_a? Promise
199
- @resolving = true
200
- add_when_settled = true
201
- elsif reason.nil?
202
- @value = value
203
- @state = :fulfilled
204
- callbacks.concat @when_settled
205
- @when_settled.clear
206
- else
207
- @reason = reason
208
- @state = :rejected
209
- callbacks.concat @when_settled
210
- @when_settled.clear
211
- end
191
+ return if @state != :pending || @settling
192
+
193
+ if value.is_a? Promise
194
+ @settling = true
195
+ elsif reason.nil?
196
+ settle_with = [value, nil]
197
+ else
198
+ settle_with = [nil, reason]
212
199
  end
213
200
  end
214
201
 
215
- # rubocop:disable Style/IfUnlessModifier
216
- if add_when_settled
217
- value.when_settled { copy_settlement_from value }
202
+ if settle_with
203
+ settle(*settle_with)
204
+ else
205
+ value.when_settled do
206
+ if value.fulfilled?
207
+ settle(value.value, nil)
208
+ else
209
+ settle(nil, value.reason)
210
+ end
211
+ end
218
212
  end
219
- # rubocop:enable Style/IfUnlessModifier
220
-
221
- callbacks.each { |c| EXECUTOR.submit(&c) }
222
213
  end
223
214
 
224
- def copy_settlement_from(other)
225
- callbacks = []
226
-
215
+ # Only called once
216
+ def settle(value, reason)
227
217
  synchronize do
228
- @value = other.value
229
- @reason = other.reason
230
- @state = other.state
231
- @resolving = false
232
- callbacks.concat @when_settled
233
- @when_settled.clear
234
- end
235
-
236
- callbacks.each { |c| EXECUTOR.submit(&c) }
218
+ @state = (reason ? :rejected : :fulfilled)
219
+ @value = value
220
+ @reason = reason
221
+ @condition.broadcast
222
+ @when_settled.slice!(0, @when_settled.length)
223
+ end.each(&:call)
237
224
  end
238
225
 
239
226
  protected
240
227
 
241
228
  def when_settled(&block)
242
229
  immediate = synchronize do
243
- if @state == :fulfilled || @state == :rejected
244
- true
245
- else
246
- @when_settled << block
247
- false
248
- end
230
+ @when_settled << block if @state == :pending
231
+ @state != :pending
249
232
  end
250
233
 
251
234
  block.call if immediate
252
-
253
- nil
254
235
  end
255
236
 
256
237
  @resolved = resolve(nil)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rosarium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rachel Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-13 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -64,7 +64,7 @@ homepage: http://rve.org.uk/gems/rosarium
64
64
  licenses:
65
65
  - Apache-2.0
66
66
  metadata: {}
67
- post_install_message:
67
+ post_install_message:
68
68
  rdoc_options: []
69
69
  require_paths:
70
70
  - lib
@@ -79,8 +79,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
81
  requirements: []
82
- rubygems_version: 3.0.8
83
- signing_key:
82
+ rubygems_version: 3.0.3
83
+ signing_key:
84
84
  specification_version: 4
85
85
  summary: Promises, or something like them
86
86
  test_files: []