rosarium 0.1.5 → 0.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b929d183760e88db677cb8b244179733aadc3f5e
4
- data.tar.gz: 8fd7a19f473d4555108d6317b3b3a75368411d81
2
+ SHA256:
3
+ metadata.gz: 7953c9f3adbdebbd4299ddef80c064fe76f32fe74e8bd8abafd160d0f9b6d816
4
+ data.tar.gz: 80b937b9e1e8750f1bbac9a9913a0e129b5dc80048d97b3ea360774b02eb1e2e
5
5
  SHA512:
6
- metadata.gz: 566a7cfd9743d343b6c25bb058c079617ad25a7ae82c16f7905cb3679b667b9c21d3881085beb6593863398d84fc7dfd93f409ca1dc3f43ae8e25ee291400e5a
7
- data.tar.gz: 395d6da1824396ee9c9ba915054b5c01a2343645d95035edee0d3498407bc907df2a71350379a39f2500911e903fa907492c99ba057b7f45806d04786f6309d5
6
+ metadata.gz: 22821d81eaccb0357841982eb2095c57e58f786d535834600b5903719c180d91af364b9e8778ac1256236d19db95835262fd77b8c313bedafdf60ae0e04a7579
7
+ data.tar.gz: 34ac659ece6ea48a111db7dab0ea89f8c0adcf90cfcf0802aaecba89eec49b1e8831fb9d55147691c4b686e51c1e68e38164fcd6dd27b4464fd5363ca802bcc9
data/Gemfile CHANGED
@@ -1 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem 'rspec', '~> 3.4'
4
+ gem 'rubocop', '~> 1.2'
data/Gemfile.lock CHANGED
@@ -1,25 +1,46 @@
1
1
  GEM
2
2
  specs:
3
- diff-lcs (1.2.5)
4
- rspec (3.4.0)
5
- rspec-core (~> 3.4.0)
6
- rspec-expectations (~> 3.4.0)
7
- rspec-mocks (~> 3.4.0)
8
- rspec-core (3.4.1)
9
- rspec-support (~> 3.4.0)
10
- rspec-expectations (3.4.0)
3
+ ast (2.4.1)
4
+ diff-lcs (1.4.4)
5
+ parallel (1.20.1)
6
+ parser (3.0.0.0)
7
+ ast (~> 2.4.1)
8
+ rainbow (3.0.0)
9
+ regexp_parser (1.8.2)
10
+ rexml (3.2.4)
11
+ rspec (3.9.0)
12
+ rspec-core (~> 3.9.0)
13
+ rspec-expectations (~> 3.9.0)
14
+ rspec-mocks (~> 3.9.0)
15
+ rspec-core (3.9.2)
16
+ rspec-support (~> 3.9.3)
17
+ rspec-expectations (3.9.2)
11
18
  diff-lcs (>= 1.2.0, < 2.0)
12
- rspec-support (~> 3.4.0)
13
- rspec-mocks (3.4.0)
19
+ rspec-support (~> 3.9.0)
20
+ rspec-mocks (3.9.1)
14
21
  diff-lcs (>= 1.2.0, < 2.0)
15
- rspec-support (~> 3.4.0)
16
- rspec-support (3.4.1)
22
+ rspec-support (~> 3.9.0)
23
+ rspec-support (3.9.3)
24
+ rubocop (1.2.0)
25
+ parallel (~> 1.10)
26
+ parser (>= 2.7.1.5)
27
+ rainbow (>= 2.2.2, < 4.0)
28
+ regexp_parser (>= 1.8)
29
+ rexml
30
+ rubocop-ast (>= 1.0.1)
31
+ ruby-progressbar (~> 1.7)
32
+ unicode-display_width (>= 1.4.0, < 2.0)
33
+ rubocop-ast (1.4.0)
34
+ parser (>= 2.7.1.5)
35
+ ruby-progressbar (1.11.0)
36
+ unicode-display_width (1.7.0)
17
37
 
18
38
  PLATFORMS
19
39
  ruby
20
40
 
21
41
  DEPENDENCIES
22
42
  rspec (~> 3.4)
43
+ rubocop (~> 1.2)
23
44
 
24
45
  BUNDLED WITH
25
- 1.11.2
46
+ 1.17.3
data/lib/rosarium.rb CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'rosarium/fixed_thread_executor'
2
- require_relative 'rosarium/simple_promise'
3
4
  require_relative 'rosarium/deferred'
4
5
  require_relative 'rosarium/promise'
5
6
 
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class Deferred
4
5
 
5
6
  def initialize(promise, resolver, rejecter)
@@ -8,9 +9,7 @@ module Rosarium
8
9
  @rejecter = rejecter
9
10
  end
10
11
 
11
- def promise
12
- @promise
13
- end
12
+ attr_reader :promise
14
13
 
15
14
  def resolve(value)
16
15
  @resolver.call(value)
@@ -21,5 +20,4 @@ module Rosarium
21
20
  end
22
21
 
23
22
  end
24
-
25
23
  end
@@ -1,5 +1,6 @@
1
- module Rosarium
1
+ # frozen_string_literal: true
2
2
 
3
+ module Rosarium
3
4
  class FixedThreadExecutor
4
5
 
5
6
  def initialize(max = 1)
@@ -14,7 +15,7 @@ module Rosarium
14
15
  @mutex.synchronize do
15
16
  @waiting << block
16
17
  if @executing < @max
17
- @executing = @executing + 1
18
+ @executing += 1
18
19
  t = Thread.new { execute_and_count_down }
19
20
  @threads.push t
20
21
  end
@@ -36,26 +37,23 @@ module Rosarium
36
37
  private
37
38
 
38
39
  def execute_and_count_down
39
- begin
40
- execute
41
- ensure
42
- @mutex.synchronize do
43
- @executing = @executing - 1
44
- end
40
+ execute
41
+ ensure
42
+ @mutex.synchronize do
43
+ @executing -= 1
45
44
  end
46
45
  end
47
46
 
48
47
  def execute
49
- while true
48
+ loop do
50
49
  block = @mutex.synchronize { @waiting.shift }
51
50
  block or break
52
51
  begin
53
52
  block.call
54
- rescue Exception => e
53
+ rescue Exception
55
54
  end
56
55
  end
57
56
  end
58
57
 
59
58
  end
60
-
61
59
  end
@@ -1,15 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rosarium
4
+ class Promise
2
5
 
3
- class Promise < SimplePromise
6
+ DEFAULT_ON_FULFILL = proc { |value| value }
7
+ DEFAULT_ON_REJECT = proc { |reason| raise reason }
4
8
 
5
- DEFAULT_ON_FULFILL = Proc.new {|value| value}
6
- DEFAULT_ON_REJECT = Proc.new {|reason| raise reason}
9
+ private_class_method :new
7
10
 
8
- def self.resolve(value)
9
- if value.kind_of? Promise
10
- return value
11
+ def self.defer
12
+ promise = new
13
+
14
+ resolver = ->(value) { promise.send(:try_settle, value, nil) }
15
+
16
+ rejecter = lambda do |reason|
17
+ raise "reason must be an Exception" unless reason.is_a?(Exception)
18
+
19
+ promise.send(:try_settle, nil, reason)
11
20
  end
12
21
 
22
+ Deferred.new(promise, resolver, rejecter)
23
+ end
24
+
25
+ def self.resolve(value)
26
+ return value if value.is_a? Promise
27
+
13
28
  deferred = defer
14
29
  deferred.resolve(value)
15
30
  deferred.promise
@@ -21,9 +36,8 @@ module Rosarium
21
36
  deferred.promise
22
37
  end
23
38
 
24
- @@resolved = resolve(nil)
25
39
  def self.execute(&block)
26
- @@resolved.then { block.call }
40
+ @resolved.then(&block)
27
41
  end
28
42
 
29
43
  def self.all_settled(promises)
@@ -35,7 +49,7 @@ module Rosarium
35
49
  waiting_for = promises.count
36
50
  mutex = Mutex.new
37
51
 
38
- check = Proc.new do
52
+ check = proc do
39
53
  # Includes both fulfilled and rejected, so always hits zero eventually
40
54
  if mutex.synchronize { (waiting_for -= 1) == 0 }
41
55
  deferred.resolve promises
@@ -43,7 +57,7 @@ module Rosarium
43
57
  end
44
58
 
45
59
  promises.each do |promise|
46
- promise.then(check) { check.call }
60
+ promise.then(check, &check)
47
61
  end
48
62
 
49
63
  deferred.promise
@@ -58,16 +72,16 @@ module Rosarium
58
72
  waiting_for = promises.count
59
73
  mutex = Mutex.new
60
74
 
61
- do_reject = Proc.new {|reason| deferred.reject reason}
62
- do_fulfill = Proc.new do
75
+ do_reject = ->(reason) { deferred.reject(reason) }
76
+ do_fulfill = proc do
63
77
  # Only fulfilled (not rejected), so hits zero iff all promises were fulfilled
64
78
  if mutex.synchronize { (waiting_for -= 1) == 0 }
65
- deferred.resolve(promises.map &:value)
79
+ deferred.resolve(promises.map(&:value))
66
80
  end
67
81
  end
68
82
 
69
83
  promises.each do |promise|
70
- promise.then(do_reject) { do_fulfill.call }
84
+ promise.then(do_reject, &do_fulfill)
71
85
  end
72
86
 
73
87
  deferred.promise
@@ -79,7 +93,7 @@ module Rosarium
79
93
  on_fulfilled ||= DEFAULT_ON_FULFILL
80
94
  on_rejected ||= DEFAULT_ON_REJECT
81
95
 
82
- on_resolution do
96
+ when_settled do
83
97
  EXECUTOR.submit do
84
98
  begin
85
99
  deferred.resolve(
@@ -104,9 +118,142 @@ module Rosarium
104
118
  self.then(block)
105
119
  end
106
120
 
107
- alias_method :catch, :rescue
108
- alias_method :on_error, :rescue
121
+ alias catch rescue
122
+ alias on_error rescue
109
123
 
110
- end
124
+ public
125
+
126
+ def initialize
127
+ @state = :pending
128
+ @resolving = false
129
+ @mutex = Mutex.new
130
+ @condition = ConditionVariable.new
131
+ @when_settled = []
132
+ end
133
+
134
+ def state
135
+ synchronize { @state }
136
+ end
137
+
138
+ def value
139
+ wait
140
+ synchronize { @value }
141
+ end
142
+
143
+ def reason
144
+ wait
145
+ synchronize { @reason }
146
+ end
147
+
148
+ def inspect
149
+ synchronize do
150
+ r = { state: @state }
151
+ r[:value] = @value if @state == :fulfilled
152
+ r[:reason] = @reason if @state == :rejected
153
+ r
154
+ end
155
+ end
156
+
157
+ def fulfilled?
158
+ state == :fulfilled
159
+ end
160
+
161
+ def rejected?
162
+ state == :rejected
163
+ end
164
+
165
+ def value!
166
+ wait
167
+ synchronize do
168
+ raise @reason if @state == :rejected
169
+ @value
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def wait
176
+ when_settled do
177
+ synchronize { @condition.broadcast }
178
+ end
179
+
180
+ synchronize do
181
+ loop do
182
+ return if @state == :fulfilled || @state == :rejected
183
+ @condition.wait @mutex
184
+ end
185
+ end
186
+ end
187
+
188
+ def synchronize(&block)
189
+ @mutex.synchronize(&block)
190
+ end
191
+
192
+ def try_settle(value, reason)
193
+ callbacks = []
194
+ add_when_settled = false
195
+
196
+ 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
212
+ end
213
+ end
214
+
215
+ # rubocop:disable Style/IfUnlessModifier
216
+ if add_when_settled
217
+ value.when_settled { copy_settlement_from value }
218
+ end
219
+ # rubocop:enable Style/IfUnlessModifier
220
+
221
+ callbacks.each { |c| EXECUTOR.submit(&c) }
222
+ end
223
+
224
+ def copy_settlement_from(other)
225
+ callbacks = []
226
+
227
+ 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
111
235
 
236
+ callbacks.each { |c| EXECUTOR.submit(&c) }
237
+ end
238
+
239
+ protected
240
+
241
+ def when_settled(&block)
242
+ immediate = synchronize do
243
+ if @state == :fulfilled || @state == :rejected
244
+ true
245
+ else
246
+ @when_settled << block
247
+ false
248
+ end
249
+ end
250
+
251
+ block.call if immediate
252
+
253
+ nil
254
+ end
255
+
256
+ @resolved = resolve(nil)
257
+
258
+ end
112
259
  end
@@ -14,16 +14,11 @@ describe "instantly-resolved promises" do
14
14
  it "creates a fulfilled promise" do
15
15
  t = Rosarium::Promise.resolve 7
16
16
  check_fulfilled t, 7
17
- expect(t).not_to respond_to(:fulfill)
18
- expect(t).not_to respond_to(:reject)
19
17
  end
20
18
 
21
19
  it "creates a rejected promise" do
22
- e = an_error
23
20
  t = Rosarium::Promise.reject an_error
24
21
  check_rejected t, an_error
25
- expect(t).not_to respond_to(:fulfill)
26
- expect(t).not_to respond_to(:reject)
27
22
  end
28
23
 
29
24
  it "creates an immediately-executable promise" do
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.5
4
+ version: 0.1.6
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: 2017-07-04 00:00:00.000000000 Z
11
+ date: 2021-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
27
41
  description: |2
28
42
  Rosarium implements something that's a bit like Promises,
29
43
  inspired by the stability and ease of use of Q
@@ -41,7 +55,6 @@ files:
41
55
  - lib/rosarium/deferred.rb
42
56
  - lib/rosarium/fixed_thread_executor.rb
43
57
  - lib/rosarium/promise.rb
44
- - lib/rosarium/simple_promise.rb
45
58
  - spec/deferred_spec.rb
46
59
  - spec/fixed_thread_executor_spec.rb
47
60
  - spec/promise_methods_spec.rb
@@ -51,7 +64,7 @@ homepage: http://rve.org.uk/gems/rosarium
51
64
  licenses:
52
65
  - Apache-2.0
53
66
  metadata: {}
54
- post_install_message:
67
+ post_install_message:
55
68
  rdoc_options: []
56
69
  require_paths:
57
70
  - lib
@@ -59,16 +72,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
72
  requirements:
60
73
  - - ">="
61
74
  - !ruby/object:Gem::Version
62
- version: '0'
75
+ version: '2.4'
63
76
  required_rubygems_version: !ruby/object:Gem::Requirement
64
77
  requirements:
65
78
  - - ">="
66
79
  - !ruby/object:Gem::Version
67
80
  version: '0'
68
81
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.5.1
71
- signing_key:
82
+ rubygems_version: 3.0.8
83
+ signing_key:
72
84
  specification_version: 4
73
85
  summary: Promises, or something like them
74
86
  test_files: []
@@ -1,164 +0,0 @@
1
- module Rosarium
2
-
3
- class SimplePromise
4
-
5
- def self.defer
6
- promise = new
7
- resolver = promise.method :resolve
8
- rejecter = promise.method :reject
9
-
10
- class <<promise
11
- undef :resolve
12
- undef :reject
13
- end
14
-
15
- Deferred.new(promise, resolver, rejecter)
16
- end
17
-
18
- def initialize
19
- @state = :pending
20
- @resolving = false
21
- @mutex = Mutex.new
22
- @condition = ConditionVariable.new
23
- @on_resolution = []
24
- end
25
-
26
- def state
27
- synchronize { @state }
28
- end
29
-
30
- def value
31
- wait
32
- synchronize { @value }
33
- end
34
-
35
- def reason
36
- wait
37
- synchronize { @reason }
38
- end
39
-
40
- def inspect
41
- synchronize do
42
- r = { state: @state }
43
- r[:value] = @value if @state == :fulfilled
44
- r[:reason] = @reason if @state == :rejected
45
- r
46
- end
47
- end
48
-
49
- def fulfilled?
50
- state == :fulfilled
51
- end
52
-
53
- def rejected?
54
- state == :rejected
55
- end
56
-
57
- def value!
58
- wait
59
- synchronize do
60
- if @state == :rejected
61
- raise @reason
62
- else
63
- @value
64
- end
65
- end
66
- end
67
-
68
- private
69
-
70
- def wait
71
- on_resolution do
72
- synchronize { @condition.broadcast }
73
- end
74
-
75
- synchronize do
76
- loop do
77
- return if @state == :fulfilled or @state == :rejected
78
- @condition.wait @mutex
79
- end
80
- end
81
- end
82
-
83
- def synchronize
84
- @mutex.synchronize { yield }
85
- end
86
-
87
- public
88
-
89
- def resolve(value)
90
- _resolve(value, nil)
91
- end
92
-
93
- def reject(reason)
94
- raise "reason must be an Exception" unless reason.kind_of? Exception
95
- _resolve(nil, reason)
96
- end
97
-
98
- private
99
-
100
- def _resolve(value, reason)
101
- callbacks = []
102
- add_on_resolution = false
103
-
104
- synchronize do
105
- if @state == :pending and not @resolving
106
- if value.kind_of? SimplePromise
107
- @resolving = true
108
- add_on_resolution = true
109
- elsif reason.nil?
110
- @value = value
111
- @state = :fulfilled
112
- callbacks.concat @on_resolution
113
- @on_resolution.clear
114
- else
115
- @reason = reason
116
- @state = :rejected
117
- callbacks.concat @on_resolution
118
- @on_resolution.clear
119
- end
120
- end
121
- end
122
-
123
- if add_on_resolution
124
- value.on_resolution { copy_resolution_from value }
125
- end
126
-
127
- callbacks.each {|c| EXECUTOR.submit { c.call } }
128
- end
129
-
130
- def copy_resolution_from(other)
131
- callbacks = []
132
-
133
- synchronize do
134
- @value = other.value
135
- @reason = other.reason
136
- @state = other.state
137
- @resolving = false
138
- callbacks.concat @on_resolution
139
- @on_resolution.clear
140
- end
141
-
142
- callbacks.each {|c| EXECUTOR.submit { c.call } }
143
- end
144
-
145
- protected
146
-
147
- def on_resolution(&block)
148
- immediate = synchronize do
149
- if @state == :fulfilled or @state == :rejected
150
- true
151
- else
152
- @on_resolution << block
153
- false
154
- end
155
- end
156
-
157
- block.call if immediate
158
-
159
- nil
160
- end
161
-
162
- end
163
-
164
- end