deferrable_gratification 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.