mr_darcy 0.2.0 → 0.3.0

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
2
  SHA1:
3
- metadata.gz: 76e4be46dc9cdd1c3940172e1fd429cb9ca06d1e
4
- data.tar.gz: c8f1944c3cf626b51599edf3337a66bc146969c6
3
+ metadata.gz: 5d61c338943c3f4d6e28c6cffdbe3c219d0e0786
4
+ data.tar.gz: 5dc024d54bee45d0c3f2b2c2dad483a8bb95b980
5
5
  SHA512:
6
- metadata.gz: 37ea3db8c56a22d1f78ab6f004c06e4b4f96e98f5fb95a8824fd2adc7491c40fa957546008c8d98bfdd7899739b9d2df29e2cd48e4d51da966305dc756dc95aa
7
- data.tar.gz: 3fdde3baecf7aa7fcb3a1b925084b09be2b742a405326344c83b644b9b4c21812b557ae17a3a3a0dfbdf4f8db1656dfb4ecd6016cf10c939667ad7da71dadf7e
6
+ metadata.gz: 158121e9b077abf2a36fe879548470fcc8338ed50aeff01ccea68b607f470d03cb24d06fdbf7a2167a904531f28f0349e761be2e6dfcef6490180acf4e57f348
7
+ data.tar.gz: 8daa06f36f9f7a867098adb3a89cb20cd1833157ce457aac38ff61eb81f6d5f264a9cdc862b667ce7c8e5a61284628c06401a63eb9d91ec8ca9dbb907ea818ca
data/README.md CHANGED
@@ -156,6 +156,24 @@ doing async ruby:
156
156
  end
157
157
  ```
158
158
 
159
+ #### Promise collections
160
+
161
+ If you have a bunch of promises, and you'd like to know when they're all
162
+ complete then you can use the `MrDarcy.all_promises` method:
163
+
164
+ ```ruby
165
+ MrDarcy.all_promises do
166
+ 10.times.map { |i| MrDarcy.promise { sleep 1; resolve i } }
167
+ end.then do |values|
168
+ puts "All done."
169
+ end
170
+ ```
171
+
172
+ In the above example, all 10 promises will execute simultaneously, the message
173
+ `All done.` will be printed to standard out, after approximately one second.
174
+
175
+ It will also reject as soon as the first promise rejects.
176
+
159
177
  ### Sprinkle on some DCI goodness.
160
178
 
161
179
  [DCI](http://fulloo.info) is a method of specifying interactions between
data/lib/mr_darcy.rb CHANGED
@@ -17,11 +17,10 @@ module MrDarcy
17
17
  end
18
18
 
19
19
  def all_drivers
20
- drivers = %w| synchronous thread celluloid em |.map(&:to_sym)
21
-
20
+ return @drivers if @drivers && !@drivers.empty?
21
+ drivers ||= %w| synchronous thread celluloid em |.map(&:to_sym)
22
22
  drivers.delete :em if RUBY_ENGINE=='jruby'
23
-
24
- drivers
23
+ @drivers = drivers
25
24
  end
26
25
 
27
26
  def promise opts={}, &block
@@ -29,4 +28,8 @@ module MrDarcy
29
28
  MrDarcy::Promise.new driver: driver, &block
30
29
  end
31
30
 
31
+ def all_promises opts={}
32
+ MrDarcy::Promise::Collection.new yield, opts
33
+ end
34
+
32
35
  end
@@ -3,7 +3,7 @@ module MrDarcy
3
3
 
4
4
  attr_accessor :promise, :last_promise
5
5
 
6
- %w| resolved? rejected? unresolved? resolve reject final result |.map(&:to_sym).each do |method|
6
+ %w| resolved? rejected? unresolved? resolve reject final result raise |.map(&:to_sym).each do |method|
7
7
  define_method method do |*args|
8
8
  last_promise.public_send method, *args
9
9
  end
@@ -17,7 +17,7 @@ module MrDarcy
17
17
  self.last_promise = last_promise.fail(&block)
18
18
  end
19
19
 
20
- def initialize opts={}
20
+ def initialize opts={}, &block
21
21
  driver = opts[:driver] || MrDarcy.driver
22
22
  self.promise = MrDarcy::Promise.new(driver: driver) {}
23
23
  self.last_promise = promise
@@ -9,6 +9,7 @@ module MrDarcy
9
9
  autoload :Thread, File.expand_path('../promise/thread', __FILE__)
10
10
  autoload :Celluloid, File.expand_path('../promise/celluloid', __FILE__)
11
11
  autoload :EM, File.expand_path('../promise/em', __FILE__)
12
+ autoload :Collection, File.expand_path('../promise/collection', __FILE__)
12
13
 
13
14
  module_function
14
15
 
@@ -72,11 +72,11 @@ module MrDarcy
72
72
  end
73
73
 
74
74
  def do_reject exception
75
- will_reject value
75
+ will_reject exception
76
76
  set_value_to exception
77
77
  state_machine_reject
78
78
  reject_child_promise
79
- did_reject value
79
+ did_reject exception
80
80
  end
81
81
 
82
82
  def will_resolve value; end
@@ -0,0 +1,106 @@
1
+ require 'thread'
2
+
3
+ module MrDarcy
4
+ module Promise
5
+ class Collection
6
+ attr_accessor :promises, :size
7
+
8
+ include Enumerable
9
+
10
+ def initialize promises, opts={}
11
+ @lock = Mutex.new
12
+ this = self
13
+
14
+ @promises = []
15
+ @size = promises.size
16
+ @promise = MrDarcy.promise opts do
17
+ promises.each do |p|
18
+ this.send :add_promise, p
19
+ end
20
+ end
21
+ end
22
+
23
+ %w| resolved? rejected? unresolved? result raise then fail |.each do |method|
24
+ define_method method do |*args,&block|
25
+ my_promise.public_send method, *args, &block
26
+ end
27
+ end
28
+
29
+ def final
30
+ my_promise.final
31
+ self
32
+ end
33
+
34
+ def each &block
35
+ promises.each &block
36
+ end
37
+
38
+ def push promise
39
+ @lock.synchronize { @size = @size + 1 }
40
+ add_promise promise
41
+ end
42
+ alias << push
43
+
44
+ def size
45
+ @lock.synchronize { @size }
46
+ end
47
+
48
+ def promises
49
+ @lock.synchronize { @promises }
50
+ end
51
+
52
+ private
53
+
54
+ def add_promise promise
55
+ ::Kernel.raise RuntimeError, "Collection already resolved" if my_promise && resolved?
56
+ ::Kernel.raise RuntimeError, "Collection already rejected" if my_promise && rejected?
57
+ ::Kernel.raise ArgumentError, "must be thenable" unless promise.respond_to?(:then) && promise.respond_to?(:fail)
58
+ @lock.synchronize { @promises.push promise }
59
+ set_up_promise promise
60
+ end
61
+
62
+ def my_promise
63
+ @lock.synchronize { @promise }
64
+ end
65
+
66
+ def set_up_promise promise
67
+ promise.then(&method(:record_resolve))
68
+ promise.fail(&method(:record_reject))
69
+ # promise.then { |v| record_resolve v }.fail { |v| record_reject v }
70
+ end
71
+
72
+ def record_resolve value
73
+ @lock.synchronize do
74
+ resolve_promise if can_resolve?
75
+ value
76
+ end
77
+ end
78
+
79
+ # NOT THREADSAFE:
80
+ # because they're called from within #record_resolve
81
+ # which locks.
82
+ def resolve_promise
83
+ @promise.resolve @promises.map(&:result)
84
+ end
85
+
86
+ def can_resolve?
87
+ all_promises_present? && all_promises_resolved? && @promise.unresolved?
88
+ end
89
+
90
+ def all_promises_present?
91
+ @promises.size == @size
92
+ end
93
+
94
+ def all_promises_resolved?
95
+ @promises.all?(&:resolved?)
96
+ end
97
+ # /NOT THREADSAFE
98
+
99
+ def record_reject value
100
+ my_promise.reject value
101
+ value
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -27,6 +27,14 @@ module MrDarcy
27
27
  ::Thread.new &block
28
28
  end
29
29
 
30
+ def resolve_child_promise
31
+ ::Thread.new { super }
32
+ end
33
+
34
+ def reject_child_promise
35
+ ::Thread.new { super }
36
+ end
37
+
30
38
  def did_resolve value
31
39
  complete!
32
40
  end
@@ -48,7 +56,8 @@ module MrDarcy
48
56
  end
49
57
 
50
58
  def complete?
51
- semaphore.synchronize { @complete }
59
+ # semaphore.synchronize { @complete }
60
+ @complete
52
61
  end
53
62
 
54
63
  def complete!
@@ -1,3 +1,3 @@
1
1
  module MrDarcy
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/mr_darcy.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["james@resistor.io"]
11
11
  spec.summary = %q{A mashup of async Promises and DCI in Ruby.}
12
12
  spec.description = <<-EOF
13
- MrDarcy takes async promises from the javascript word, DCI from Jim
13
+ MrDarcy takes async promises from the javascript world, DCI from Jim
14
14
  Gay's brain and sprinkles some ruby on top for great justice!
15
15
  EOF
16
16
  spec.homepage = "https://github.com/jamesotron/MrDarcy"
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe MrDarcy::Deferred do
4
+ %w| promise last_promise resolved? rejected? unresolved? resolve reject
5
+ final result raise then fail |.each do |method|
6
+ it { should respond_to method }
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe MrDarcy::Promise::Collection do
4
+ MrDarcy.all_drivers.each do |driver|
5
+ describe "with driver #{driver}" do
6
+ next if driver == :synchronous
7
+
8
+ let(:first_promise) { MrDarcy.promise(driver: driver) { resolve 1 } }
9
+ let(:second_promise) { MrDarcy.promise(driver: driver) { resolve 2 } }
10
+ let(:third_promise) { MrDarcy.promise(driver: driver) { resolve 3 } }
11
+ let(:three_promises) { [ first_promise, second_promise, third_promise ] }
12
+ let(:collection) { described_class.new three_promises, driver: driver }
13
+ subject { collection }
14
+
15
+ its(:final) { should be_an Enumerable }
16
+ its(:final) { should respond_to :each }
17
+ its(:final) { should respond_to :push }
18
+ its(:final) { should respond_to :<< }
19
+ its(:final) { should respond_to :size }
20
+ its(:final) { should respond_to :promises }
21
+ its(:final) { should respond_to :raise }
22
+
23
+ When 'all the promises resolve' do
24
+ its(:final) { should be_resolved }
25
+ its(:result) { should eq [1,2,3] }
26
+ end
27
+
28
+ When 'any of the promises reject' do
29
+ let(:second_promise) { MrDarcy.promise(driver: driver) { reject 2 } }
30
+ its(:final) { should be_rejected }
31
+ its(:result) { should eq 2 }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -10,13 +10,19 @@ describe MrDarcy do
10
10
  subject { MrDarcy.all_drivers }
11
11
 
12
12
  When 'running on jruby' do
13
- before { stub_const 'RUBY_ENGINE', 'jruby' }
13
+ before do
14
+ MrDarcy.all_drivers.clear
15
+ stub_const 'RUBY_ENGINE', 'jruby'
16
+ end
14
17
 
15
18
  it { should_not include :em }
16
19
  end
17
20
 
18
21
  Otherwise do
19
- before { stub_const 'RUBY_ENGINE', 'mri'}
22
+ before do
23
+ MrDarcy.all_drivers.clear
24
+ stub_const 'RUBY_ENGINE', 'mri'
25
+ end
20
26
 
21
27
  it { should include :synchronous }
22
28
  it { should include :thread }
metadata CHANGED
@@ -1,171 +1,171 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mr_darcy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Harton
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-25 00:00:00.000000000 Z
11
+ date: 2014-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
- version_requirements: !ruby/object:Gem::Requirement
15
+ requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.5'
20
- requirement: !ruby/object:Gem::Requirement
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
21
23
  requirements:
22
24
  - - ~>
23
25
  - !ruby/object:Gem::Version
24
26
  version: '1.5'
25
- prerelease: false
26
- type: :development
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
29
  requirement: !ruby/object:Gem::Requirement
35
30
  requirements:
36
31
  - - '>='
37
32
  - !ruby/object:Gem::Version
38
33
  version: '0'
39
- prerelease: false
40
34
  type: :development
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
35
+ prerelease: false
43
36
  version_requirements: !ruby/object:Gem::Requirement
44
37
  requirements:
45
38
  - - '>='
46
39
  - !ruby/object:Gem::Version
47
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
44
  requirements:
50
45
  - - '>='
51
46
  - !ruby/object:Gem::Version
52
47
  version: '0'
53
- prerelease: false
54
48
  type: :development
55
- - !ruby/object:Gem::Dependency
56
- name: guard
49
+ prerelease: false
57
50
  version_requirements: !ruby/object:Gem::Requirement
58
51
  requirements:
59
52
  - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
62
57
  requirement: !ruby/object:Gem::Requirement
63
58
  requirements:
64
59
  - - '>='
65
60
  - !ruby/object:Gem::Version
66
61
  version: '0'
67
- prerelease: false
68
62
  type: :development
69
- - !ruby/object:Gem::Dependency
70
- name: guard-rspec
63
+ prerelease: false
71
64
  version_requirements: !ruby/object:Gem::Requirement
72
65
  requirements:
73
66
  - - '>='
74
67
  - !ruby/object:Gem::Version
75
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
76
71
  requirement: !ruby/object:Gem::Requirement
77
72
  requirements:
78
73
  - - '>='
79
74
  - !ruby/object:Gem::Version
80
75
  version: '0'
81
- prerelease: false
82
76
  type: :development
83
- - !ruby/object:Gem::Dependency
84
- name: guard-bundler
77
+ prerelease: false
85
78
  version_requirements: !ruby/object:Gem::Requirement
86
79
  requirements:
87
80
  - - '>='
88
81
  - !ruby/object:Gem::Version
89
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-bundler
90
85
  requirement: !ruby/object:Gem::Requirement
91
86
  requirements:
92
87
  - - '>='
93
88
  - !ruby/object:Gem::Version
94
89
  version: '0'
95
- prerelease: false
96
90
  type: :development
97
- - !ruby/object:Gem::Dependency
98
- name: terminal-notifier-guard
91
+ prerelease: false
99
92
  version_requirements: !ruby/object:Gem::Requirement
100
93
  requirements:
101
94
  - - '>='
102
95
  - !ruby/object:Gem::Version
103
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: terminal-notifier-guard
104
99
  requirement: !ruby/object:Gem::Requirement
105
100
  requirements:
106
101
  - - '>='
107
102
  - !ruby/object:Gem::Version
108
103
  version: '0'
109
- prerelease: false
110
104
  type: :development
111
- - !ruby/object:Gem::Dependency
112
- name: pry
105
+ prerelease: false
113
106
  version_requirements: !ruby/object:Gem::Requirement
114
107
  requirements:
115
108
  - - '>='
116
109
  - !ruby/object:Gem::Version
117
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
118
113
  requirement: !ruby/object:Gem::Requirement
119
114
  requirements:
120
115
  - - '>='
121
116
  - !ruby/object:Gem::Version
122
117
  version: '0'
123
- prerelease: false
124
118
  type: :development
125
- - !ruby/object:Gem::Dependency
126
- name: eventmachine
119
+ prerelease: false
127
120
  version_requirements: !ruby/object:Gem::Requirement
128
121
  requirements:
129
122
  - - '>='
130
123
  - !ruby/object:Gem::Version
131
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: eventmachine
132
127
  requirement: !ruby/object:Gem::Requirement
133
128
  requirements:
134
129
  - - '>='
135
130
  - !ruby/object:Gem::Version
136
131
  version: '0'
137
- prerelease: false
138
132
  type: :development
139
- - !ruby/object:Gem::Dependency
140
- name: em-http-request
133
+ prerelease: false
141
134
  version_requirements: !ruby/object:Gem::Requirement
142
135
  requirements:
143
136
  - - '>='
144
137
  - !ruby/object:Gem::Version
145
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: em-http-request
146
141
  requirement: !ruby/object:Gem::Requirement
147
142
  requirements:
148
143
  - - '>='
149
144
  - !ruby/object:Gem::Version
150
145
  version: '0'
151
- prerelease: false
152
146
  type: :development
153
- - !ruby/object:Gem::Dependency
154
- name: celluloid
147
+ prerelease: false
155
148
  version_requirements: !ruby/object:Gem::Requirement
156
149
  requirements:
157
150
  - - '>='
158
151
  - !ruby/object:Gem::Version
159
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: celluloid
160
155
  requirement: !ruby/object:Gem::Requirement
161
156
  requirements:
162
157
  - - '>='
163
158
  - !ruby/object:Gem::Version
164
159
  version: '0'
165
- prerelease: false
166
160
  type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
167
  description: |2
168
- MrDarcy takes async promises from the javascript word, DCI from Jim
168
+ MrDarcy takes async promises from the javascript world, DCI from Jim
169
169
  Gay's brain and sprinkles some ruby on top for great justice!
170
170
  email:
171
171
  - james@resistor.io
@@ -192,6 +192,7 @@ files:
192
192
  - lib/mr_darcy/promise/base.rb
193
193
  - lib/mr_darcy/promise/celluloid.rb
194
194
  - lib/mr_darcy/promise/child_promise.rb
195
+ - lib/mr_darcy/promise/collection.rb
195
196
  - lib/mr_darcy/promise/dsl.rb
196
197
  - lib/mr_darcy/promise/em.rb
197
198
  - lib/mr_darcy/promise/state.rb
@@ -212,8 +213,10 @@ files:
212
213
  - spec/acceptance/simple_promise_with_fail_spec.rb
213
214
  - spec/acceptance/simple_promise_with_then_spec.rb
214
215
  - spec/lib/mr_darcy/context_spec.rb
216
+ - spec/lib/mr_darcy/deferred_spec.rb
215
217
  - spec/lib/mr_darcy/promise/base_spec.rb
216
218
  - spec/lib/mr_darcy/promise/child_promise_spec.rb
219
+ - spec/lib/mr_darcy/promise/collection_spec.rb
217
220
  - spec/lib/mr_darcy/promise/dsl_spec.rb
218
221
  - spec/lib/mr_darcy/promise/em_spec.rb
219
222
  - spec/lib/mr_darcy/promise/state/base_spec.rb
@@ -233,7 +236,7 @@ homepage: https://github.com/jamesotron/MrDarcy
233
236
  licenses:
234
237
  - MIT
235
238
  metadata: {}
236
- post_install_message:
239
+ post_install_message:
237
240
  rdoc_options: []
238
241
  require_paths:
239
242
  - lib
@@ -248,9 +251,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
248
251
  - !ruby/object:Gem::Version
249
252
  version: '0'
250
253
  requirements: []
251
- rubyforge_project:
254
+ rubyforge_project:
252
255
  rubygems_version: 2.2.2
253
- signing_key:
256
+ signing_key:
254
257
  specification_version: 4
255
258
  summary: A mashup of async Promises and DCI in Ruby.
256
259
  test_files:
@@ -262,8 +265,10 @@ test_files:
262
265
  - spec/acceptance/simple_promise_with_fail_spec.rb
263
266
  - spec/acceptance/simple_promise_with_then_spec.rb
264
267
  - spec/lib/mr_darcy/context_spec.rb
268
+ - spec/lib/mr_darcy/deferred_spec.rb
265
269
  - spec/lib/mr_darcy/promise/base_spec.rb
266
270
  - spec/lib/mr_darcy/promise/child_promise_spec.rb
271
+ - spec/lib/mr_darcy/promise/collection_spec.rb
267
272
  - spec/lib/mr_darcy/promise/dsl_spec.rb
268
273
  - spec/lib/mr_darcy/promise/em_spec.rb
269
274
  - spec/lib/mr_darcy/promise/state/base_spec.rb