deferrable_gratification 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,6 +19,19 @@ module DeferrableGratification
19
19
  # Allow DG.const, DG.failure etc
20
20
  extend Primitives
21
21
 
22
+ # Exception passed to errbacks by {Combinators#guard} if the arguments to
23
+ # +Deferrable#succeed+ fail the supplied predicate.
24
+ class GuardFailed < RuntimeError
25
+ attr_reader :reason
26
+ attr_reader :args
27
+
28
+ def initialize(reason, args)
29
+ @reason = reason || 'guard failed'
30
+ @args = args
31
+ super("#{@args.inspect}: #{@reason}")
32
+ end
33
+ end
34
+
22
35
  # Bestow DG goodness upon an existing module or class.
23
36
  #
24
37
  # N.B. calling this on a module won't enhance any classes that have already
@@ -165,6 +165,58 @@ module DeferrableGratification
165
165
  end
166
166
 
167
167
 
168
+ # If this Deferrable succeeds, ensure that the arguments passed to
169
+ # +Deferrable#succeed+ meet certain criteria (specified by passing a
170
+ # predicate as a block). If they do, subsequently defined callbacks will
171
+ # fire as normal, receiving the same arguments; if they do not, this
172
+ # Deferrable will fail instead, calling its errbacks with a {GuardFailed}
173
+ # exception.
174
+ #
175
+ # This follows the usual Deferrable semantics of calling +Deferrable#fail+
176
+ # inside a callback: any callbacks defined *before* the call to {#guard}
177
+ # will still execute as normal, but those defined *after* the call to
178
+ # {#guard} will only execute if the predicate returns truthy.
179
+ #
180
+ # Multiple successive calls to {#guard} will work as expected: the
181
+ # predicates will be evaluated in order, stopping as soon as any of them
182
+ # returns falsy, and subsequent callbacks will fire only if all the
183
+ # predicates pass.
184
+ #
185
+ # If instead of returning a boolean, the predicate raises an exception,
186
+ # the Deferrable will fail, but errbacks will receive the exception raised
187
+ # instead of {GuardFailed}. You could use this to indicate the reason for
188
+ # failure in a complex guard expression; however the same intent might be
189
+ # more clearly expressed by multiple guard expressions with appropriate
190
+ # reason messages.
191
+ #
192
+ # @param [String] reason optional description of the reason for the guard:
193
+ # specifying this will both serve as code
194
+ # documentation, and be included in the
195
+ # {GuardFailed} exception for error handling
196
+ # purposes.
197
+ #
198
+ # @yieldparam *args the arguments passed to callbacks if this Deferrable
199
+ # succeeds.
200
+ # @yieldreturn [Boolean] +true+ if subsequent callbacks should fire (with
201
+ # the same arguments); +false+ if instead errbacks
202
+ # should fire.
203
+ #
204
+ # @raise [ArgumentError] if called without a predicate
205
+ def guard(reason = nil, &block)
206
+ raise ArgumentError, 'must be called with a block' unless block_given?
207
+ callback do |*callback_args|
208
+ begin
209
+ unless block.call(*callback_args)
210
+ raise ::DeferrableGratification::GuardFailed.new(reason, callback_args)
211
+ end
212
+ rescue => exception
213
+ fail(exception)
214
+ end
215
+ end
216
+ self
217
+ end
218
+
219
+
168
220
  # Boilerplate hook to extend {ClassMethods}.
169
221
  def self.included(base)
170
222
  base.send :extend, ClassMethods
@@ -30,5 +30,16 @@ module DeferrableGratification
30
30
  super(&block)
31
31
  self
32
32
  end
33
+
34
+ # Ensure that if this Deferrable doesn't either succeed or fail within the
35
+ # timeout, it will call its errback with no parameters.
36
+ #
37
+ # @return [Deferrable, Fluent] +self+
38
+ #
39
+ # @see EventMachine::Deferrable#timeout
40
+ def timeout(seconds)
41
+ super(seconds)
42
+ self
43
+ end
33
44
  end
34
45
  end
@@ -12,20 +12,39 @@ module DeferrableGratification
12
12
  # {DeferrableGratification} extends this module, and thus the methods here
13
13
  # are accessible via the {DG} alias.
14
14
  module Primitives
15
- # Return a Deferrable which immediately succeeds with a constant value.
15
+ # Return a Deferrable which immediately succeeds, passing 0 or more values
16
+ # to callbacks.
17
+ def success(*values)
18
+ blank.tap {|d| d.succeed(*values) }
19
+ end
20
+
21
+ # Return a Deferrable which immediately succeeds, passing a constant value
22
+ # to callbacks.
16
23
  def const(value)
17
- blank.tap {|d| d.succeed(value) }
24
+ success(value)
18
25
  end
19
26
 
20
27
  # Return a Deferrable which immediately fails with an exception.
21
- def failure(class_or_message, message_or_nil = nil)
28
+ #
29
+ # @overload failure(message)
30
+ # Passes +RuntimeError.new(message)+ to errbacks.
31
+ # @overload failure(exception_class)
32
+ # Passes +exception_class.new+ to errbacks.
33
+ # @overload failure(exception_class, message)
34
+ # Passes +exception_class.new(message)+ to errbacks.
35
+ # @overload failure(exception)
36
+ # Passes +exception+ to errbacks.
37
+ def failure(exception_class_or_message, message_or_nil = nil)
22
38
  blank.tap do |d|
23
39
  d.fail(
24
- case class_or_message
40
+ case exception_class_or_message
41
+ when Exception
42
+ raise ArgumentError, "can't specify both exception and message" if message_or_nil
43
+ exception_class_or_message
25
44
  when Class
26
- class_or_message.new(message_or_nil)
45
+ exception_class_or_message.new(message_or_nil)
27
46
  else
28
- RuntimeError.new(class_or_message.to_s)
47
+ RuntimeError.new(exception_class_or_message.to_s)
29
48
  end)
30
49
  end
31
50
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deferrable_gratification
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease: false
4
+ hash: 19
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sam Stokes
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-21 00:00:00 -08:00
18
+ date: 2011-03-25 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -152,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
152
  requirements: []
153
153
 
154
154
  rubyforge_project:
155
- rubygems_version: 1.3.7
155
+ rubygems_version: 1.4.2
156
156
  signing_key:
157
157
  specification_version: 3
158
158
  summary: Makes evented programming easier with composition and abstraction.