Module: DeferrableGratification::Combinators
- Defined in:
- lib/deferrable_gratification/combinators.rb,
lib/deferrable_gratification/combinators/bind.rb,
lib/deferrable_gratification/combinators/join.rb
Overview
Combinators for building up higher-level asynchronous abstractions by composing simpler asynchronous operations, without having to manually wire callbacks together and remember to propagate errors correctly.
Defined Under Namespace
Modules: ClassMethods Classes: Bind, Join
Class Method Summary collapse
-
.included(base) ⇒ Object
Boilerplate hook to extend ClassMethods.
Instance Method Summary collapse
-
#>>(prok) ⇒ Deferrable
Alias for #bind!.
-
#bind!(&block) ⇒ Deferrable
Register callbacks so that when this Deferrable succeeds, its result will be passed to the block, which is assumed to return another Deferrable representing the status of a second operation.
-
#guard(reason = nil) {|*args| ... } ⇒ Object
If this Deferrable succeeds, ensure that the arguments passed to Deferrable#succeed meet certain criteria (specified by passing a predicate as a block).
-
#transform(&block) ⇒ Deferrable
Transform the result of this Deferrable by invoking
block
, returning a Deferrable which succeeds with the transformed result. -
#transform_error(&block) ⇒ Deferrable
Transform the value passed to the errback of this Deferrable by invoking
block
.
Class Method Details
.included(base) ⇒ Object
Boilerplate hook to extend ClassMethods.
221 222 223 |
# File 'lib/deferrable_gratification/combinators.rb', line 221 def self.included(base) base.send :extend, ClassMethods end |
Instance Method Details
#>>(prok) ⇒ Deferrable
71 72 73 |
# File 'lib/deferrable_gratification/combinators.rb', line 71 def >>(prok) Bind.setup!(self, &prok) end |
#bind!(&block) ⇒ Deferrable
Register callbacks so that when this Deferrable succeeds, its result will be passed to the block, which is assumed to return another Deferrable representing the status of a second operation.
If this operation fails, the block will not be run. If either operation fails, the compound Deferrable returned will fire its errbacks, meaning callers don’t have to know about the inner operations and can just subscribe to the result of #bind!.
If you find yourself writing lots of nested #bind! calls, you can equivalently rewrite them as a chain and remove the nesting: e.g.
a.bind! do |x|
b(x).bind! do |y|
c(y).bind! do |z|
d(z)
end
end
end
has the same behaviour as
a.bind! do |x|
b(x)
end.bind! do |y|
c(y)
end.bind! do |z|
d(y)
end
As well as being more readable due to avoiding left margin inflation, this prevents introducing bugs due to inadvertent local variable capture by the nested blocks.
123 124 125 |
# File 'lib/deferrable_gratification/combinators.rb', line 123 def bind!(&block) Bind.setup!(self, &block) end |
#guard(reason = nil) {|*args| ... } ⇒ Object
If this Deferrable succeeds, ensure that the arguments passed to Deferrable#succeed meet certain criteria (specified by passing a predicate as a block). If they do, subsequently defined callbacks will fire as normal, receiving the same arguments; if they do not, this Deferrable will fail instead, calling its errbacks with a GuardFailed exception.
This follows the usual Deferrable semantics of calling Deferrable#fail inside a callback: any callbacks defined before the call to #guard will still execute as normal, but those defined after the call to #guard will only execute if the predicate returns truthy.
Multiple successive calls to #guard will work as expected: the predicates will be evaluated in order, stopping as soon as any of them returns falsy, and subsequent callbacks will fire only if all the predicates pass.
If instead of returning a boolean, the predicate raises an exception, the Deferrable will fail, but errbacks will receive the exception raised instead of GuardFailed. You could use this to indicate the reason for failure in a complex guard expression; however the same intent might be more clearly expressed by multiple guard expressions with appropriate reason messages.
205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/deferrable_gratification/combinators.rb', line 205 def guard(reason = nil, &block) raise ArgumentError, 'must be called with a block' unless block_given? callback do |*callback_args| begin unless block.call(*callback_args) raise ::DeferrableGratification::GuardFailed.new(reason, callback_args) end rescue => exception fail(exception) end end self end |
#transform(&block) ⇒ Deferrable
Transform the result of this Deferrable by invoking block
, returning a Deferrable which succeeds with the transformed result.
If this operation fails, the operation will not be run, and the returned Deferrable will also fail.
141 142 143 |
# File 'lib/deferrable_gratification/combinators.rb', line 141 def transform(&block) Bind.setup!(self, :without_chaining => true, &block) end |
#transform_error(&block) ⇒ Deferrable
Transform the value passed to the errback of this Deferrable by invoking block
. If this operation succeeds, the returned Deferrable will succeed with the same value. If this operation fails, the returned Deferrable will fail with the transformed error value.
156 157 158 159 160 161 162 163 164 165 |
# File 'lib/deferrable_gratification/combinators.rb', line 156 def transform_error(&block) errback do |*err| self.fail( begin yield(*err) rescue => e e end) end end |