Module: DeferrableGratification::Combinators::ClassMethods

Included in:
DeferrableGratification
Defined in:
lib/deferrable_gratification/combinators.rb

Overview

Combinators which don’t make sense as methods on Deferrable.

DeferrableGratification extends this module, and thus the methods here are accessible via the DG alias.

Instance Method Summary collapse

Instance Method Details

#chain(*actions) ⇒ Deferrable

Execute a sequence of asynchronous operations that may each depend on the result of the previous operation.

Parameters:

  • *actions (*Proc)

    procs that will perform an operation and return a Deferrable.

Returns:

  • (Deferrable)

    Deferrable that will succeed if all of the chained operations succeeded, and callback with the result of the last operation.

See Also:



241
242
243
# File 'lib/deferrable_gratification/combinators.rb', line 241

def chain(*actions)
  actions.inject(DG.const(nil), &:>>)
end

#join_first_success(*operations) ⇒ Deferrable

Combinator that waits for any of the supplied asynchronous operations to succeed, and succeeds with the result of the first (chronologically) to do so.

This Deferrable will fail if all the operations fail. It may never succeed or fail, if one of the operations also does not.

Parameters:

  • *operations (*Deferrable)

    deferred statuses of asynchronous operations to wait for.

Returns:

  • (Deferrable)

    a deferred status that will succeed as soon as any of the operations succeeds; its callbacks will be passed the result of that operation.



279
280
281
# File 'lib/deferrable_gratification/combinators.rb', line 279

def join_first_success(*operations)
  Join::FirstSuccess.setup!(*operations)
end

#join_successes(*operations) ⇒ Deferrable

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.

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).

Parameters:

  • *operations (*Deferrable)

    deferred statuses of asynchronous operations to wait for.

Returns:

  • (Deferrable)

    a deferred status that will succeed after all the operations have either succeeded or failed; its callbacks will be passed an Enumerable containing the results of those operations that succeeded.



263
264
265
# File 'lib/deferrable_gratification/combinators.rb', line 263

def join_successes(*operations)
  Join::Successes.setup!(*operations)
end

#loop_until_success(loop_deferrable = DefaultDeferrable.new, &block) ⇒ Deferrable

Note:

this combinator is intended for use inside EventMachine. It will still work outside of EventMachine, provided that the operation is synchronous (although a simple while loop might be preferable in this case!).

Combinator that repeatedly executes the supplied block until it succeeds, then succeeds itself with the eventual result.

This Deferrable may never succeed, if the operation never succeeds. It will fail if an iteration raises an exception.

Parameters:

  • loop_deferrable (defaults to: DefaultDeferrable.new)

    for internal use only, always omit this.

  • &block

    operation to execute until it succeeds.

Yield Returns:

  • (Deferrable)

    deferred status of the operation. If it fails, the operation will be retried. If it succeeds, the combinator will succeed with the result.

Returns:

  • (Deferrable)

    a deferred status that will succeed once the supplied operation eventually succeeds.



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/deferrable_gratification/combinators.rb', line 303

def loop_until_success(loop_deferrable = DefaultDeferrable.new, &block)
  if EM.reactor_running?
    EM.next_tick do
      begin
        attempt = yield
      rescue => e
        loop_deferrable.fail(e)
      else
        attempt.callback(&loop_deferrable.method(:succeed))
        attempt.errback { loop_until_success(loop_deferrable, &block) }
      end
    end
  else
    # In the synchronous case, we could simply use the same
    # implementation as in EM, but without the next_tick; unfortunately
    # that means direct recursion, so risks stack overflow.  Instead we
    # just reimplement as a loop.
    results = []
    begin
      yield.callback {|*values| results << values } while results.empty?
    rescue => e
      loop_deferrable.fail(e)
    else
      loop_deferrable.succeed(*results[0])
    end
  end
  loop_deferrable
end