mr_darcy 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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