Permalink
Browse files

Merge branch 'in_parallel'

  • Loading branch information...
2 parents 4a4a355 + 35e83ed commit cef85877000de003cf9f99c4cc56a417ccc6f2d7 @ConradIrwin ConradIrwin committed Aug 16, 2012
@@ -304,7 +304,9 @@ def chain(*actions)
# passed an +Enumerable+ containing the results of those operations
# that succeeded.
def join_successes(*operations)
- Join::Successes.setup!(*operations)
+ in_parallel(*operations).transform do |successes, failures|
+ successes
+ end
end
# Combinator that waits for any of the supplied asynchronous operations
@@ -323,6 +325,20 @@ def join_first_success(*operations)
Join::FirstSuccess.setup!(*operations)
end
+
+ # Combinator that waits for all of the supplied asynchronous operations
+ # to succeed or fail, and then succeeds with an Array of the successes and
+ # an Array of the failures.
+ #
+ # @param [*Deferrable] *operations
+ #
+ # @return [Deferrable] a deferred status that will succeed with the Array
+ # of all +operations+ that succeeded, and the Array of all +operations+
+ # that failed.
+ def in_parallel(*operations)
+ Join::InParallel.setup!(*operations)
+ end
+
# Combinator that repeatedly executes the supplied block until it
# succeeds, then succeeds itself with the eventual result.
#
@@ -38,48 +38,44 @@ def self.setup!(*operations)
end
- # Combinator that waits for all of the supplied asynchronous operations
- # to succeed or fail, then succeeds with the results of all those
- # operations that were successful.
- #
- # This Deferrable will never fail. It may also never succeed, if _any_
- # of the supplied operations does not either succeed or fail.
+ # Combinator that waits for any of the supplied asynchronous operations
+ # to succeed, and succeeds with the result of the first (chronologically)
+ # to do so.
#
- # The successful results are guaranteed to be in the same order as the
- # operations were passed in (which may _not_ be the same as the
- # chronological order in which they succeeded).
+ # This Deferrable will fail if all the operations fail. It may never
+ # succeed or fail, if one of the operations also does not.
#
- # You probably want to call {ClassMethods#join_successes} rather than
+ # You probably want to call {ClassMethods#join_first_success} rather than
# using this class directly.
- class Successes < Join
+ class FirstSuccess < Join
private
def done?
- all_completed?
+ successes.length > 0
end
def finish
- succeed(successes)
+ succeed(successes.first)
end
end
- # Combinator that waits for any of the supplied asynchronous operations
- # to succeed, and succeeds with the result of the first (chronologically)
- # to do so.
+ # Combinator that waits for all of the supplied asynchronous operations
+ # to succeed or fail, and the succeeds with a list of the successes and
+ # a list of the failures.
#
- # This Deferrable will fail if all the operations fail. It may never
- # succeed or fail, if one of the operations also does not.
+ # This deferrable will never fail. It may never succeed if one of
+ # the supplied operations never completes.
#
- # You probably want to call {ClassMethods#join_first_success} rather than
+ # You probably want to call {ClassMethods#in_parallel} rather than
# using this class directly.
- class FirstSuccess < Join
+ class InParallel < Join
private
def done?
- successes.length > 0
+ all_completed?
end
def finish
- succeed(successes.first)
+ succeed(successes, failures)
end
end
@@ -0,0 +1,42 @@
+require 'deferrable_gratification'
+
+describe DeferrableGratification::Combinators do
+ describe '.in_parallel' do
+
+ describe 'with successful operations' do
+ subject do
+ DG.in_parallel(
+ DummyDB.query(:id, :name => 'Sam'),
+ DummyDB.query(:age, :id => 42)
+ )
+ end
+
+ before do
+ DummyDB.stub_successful_query(:id, :name => 'Sam') { 42 }
+ DummyDB.stub_successful_query(:age, :id => 42) { 26 }
+ end
+
+ it "should succeed" do
+ subject.should succeed_with [42, 26], []
+ end
+ end
+
+ describe 'with a mix of operations' do
+ subject do
+ DG.in_parallel(
+ DummyDB.query(:id, :name => 'Sam'),
+ DummyDB.query(:age, :id => 42)
+ )
+ end
+
+ before do
+ DummyDB.stub_successful_query(:id, :name => 'Sam') { 42 }
+ DummyDB.stub_failing_query(:age, :id => 42) { 'oops' }
+ end
+
+ it "should succeed" do
+ subject.should succeed_with [42], ['oops']
+ end
+ end
+ end
+end
View
@@ -110,15 +110,13 @@ def result=(result)
DummyDB = SpecTools::DummyDB
-RSpec::Matchers.define :succeed_with do |*value_or_empty|
- case value_or_empty.size
+RSpec::Matchers.define :succeed_with do |*values_or_empty|
+ case values_or_empty.size
when 0
@cares_about_value = false
- when 1
- @cares_about_value = true
- @value = value_or_empty[0]
else
- raise ArgumentError, 'too many arguments to succeed_with'
+ @cares_about_value = true
+ @values = values_or_empty
end
match do |deferrable|
@@ -130,11 +128,13 @@ def result=(result)
!@errback.called? && @callback.called? && (
if @cares_about_value
- @callback.result_satisfies? do |result|
- if @value.respond_to? :match
- @value.match(result)
- else
- @value == result
+ @callback.result_satisfies? do |*results|
+ @values.zip(results).each do |(value, result)|
+ if value.respond_to? :match
+ value.match(result)
+ else
+ value == result
+ end
end
end
else
@@ -148,7 +148,7 @@ def description
def expectation_description
if @cares_about_value
- "succeed with #{@value.inspect}"
+ "succeed with #{@values.map(&:inspect).join(", ")}"
else
'succeed'
end

0 comments on commit cef8587

Please sign in to comment.