class Slayer::ResultMatcher
ResultMatcher
is the object passed to the block of a {Command.call}. The ResultMatcher
allows the block-author to specify which piece of logic they would like to invoke based on the state of the {Result} object.
In the event that multiple blocks match the {Result}, only the most specific matching block will be invoked. Status matches take precedence over default matches. If there are two blocks with a matching status, the pass/fail block takes precedence over the all block.
Matching based on success or failure¶ ↑
The ResultMatcher
matches calls to {#pass} to a {Result} that returns true
for {Result#success?}, calls to {#fail} to a {Result} that returns true
for {Result#failure?}, and calls to {#all} to a {Result} in either state.
A matching call to {#pass} or {#fail} takes precedence over matching calls to {#all}
Matching based on status¶ ↑
Additionally, the ResultMatcher
can also match by the {Result#status}. If a status or statuses is passed to {#pass}, {#fail}, or {#all}, these will only be invoked if the status of the {Result} matches the passed in status.
If the default block is the same as the block for one of the statuses the status :default
can be used to indicate which block should be used as the default. Successful status matches take precedence over default matchers.
Both pass and fail must be handled¶ ↑
If the block form of a {Command.call} is invoked, both the block must handle the default status for both a {Result#success?} and a {Result#failure?}. If both are not handled, the matching block will not be invoked and a {CommandResultNotHandledError} will be raised.
@example Matcher invokes the matching pass block, with precedence given to {#pass} and {#fail}
# Call produces a successful Result SuccessCommand.call do |m| m.pass { puts "Pass!" } m.fail { puts "Fail!" } m.all { puts "All!" } # will never be invoked, due to both a pass and fail response existing end # => prints "Pass!"
@example Matcher invokes the matching status of the result object, or the default
# Call produces a successful Result with status :ok SuccessCommand.call do |m| m.pass(:ok) { puts "Pass, OK!" } m.pass { puts "Pass, default!" } m.fail { puts "Fail!" } end # => prints "Pass, OK!" # Call produces a successful Result with status :created SuccessCommand.call do |m| m.pass(:ok) { puts "Pass, OK!" } m.pass { puts "Pass, default!" } m.fail { puts "Fail!" } end # => prints "Pass, default!"
@example Matcher invokes the explicitly indicated default block
# Call produces a successful Result with status :created SuccessCommand.call do |m| m.pass(:ok, :default) { puts "Pass, OK!" } m.pass(:great) { puts "Pass, default!" } m.fail { puts "Fail!" } end # => prints "Pass, OK!"
@example Matcher must handle both pass and fail defaults.
# Call produces a successful Result with status :ok SuccessCommand.call do |m| m.pass(:ok) { puts "Pass, OK!"} m.fail { puts "Fail!" } end # => raises CommandResultNotHandledError (because no default pass was provided) # Call produces a successful Result with status :ok SuccessCommand.call do |m| m.pass(:ok, :default) { puts "Pass, OK!"} m.fail { puts "Fail!" } end # => prints "Pass, OK!" # Call produces a successful Result with status :ok SuccessCommand.call do |m| m.pass(:ok) { puts "Pass, OK!"} m.all { puts "All!" } end # => prints "Pass, OK!" # Call produces a successful Result with status :ok SuccessCommand.call do |m| m.pass(:ok, :default) { puts "Pass, OK!"} end # => raises CommandResultNotHandledError (because no default fail was provided)
Attributes
Public Class Methods
@api private
# File lib/slayer/result_matcher.rb, line 102 def initialize(result, command) @result = result @command = command @status = result.status || :default @handled_default_pass = false @handled_default_fail = false # These are set to false if they are never set. If they are set to `nil` that # means the block intentionally passed `nil` as the block to be executed. @matching_block = false @matching_all = false @default_block = false @default_all = false @ensure_block = false end
Public Instance Methods
Provide a block that should be invoked for any {Result}. This has a lower precedence that either {#pass} or {#fail}.
@param statuses [Array<status>] Statuses that should be compared to the {Result}. If
any of provided statuses match the {Result} this block will be considered a match. The symbol +:default+ can also be used to indicate that this should match any {Result} not matched by other matchers. If no value is provided for statuses it defaults to +:default+.
# File lib/slayer/result_matcher.rb, line 167 def all(*statuses, &block) statuses << :default if statuses.empty? @handled_default_pass ||= statuses.include?(:default) @handled_default_fail ||= statuses.include?(:default) block_is_match = statuses.include?(@status) block_is_default = statuses.include?(:default) @matching_all = block if block_is_match @default_all = block if block_is_default end
Provide a block that should be always be invoked after other blocks have executed. This block will be invoked even if the other block raises an error.
# File lib/slayer/result_matcher.rb, line 181 def ensure(&block) @ensure_block = block end
Executes the ensure block if one exists. @api private
# File lib/slayer/result_matcher.rb, line 210 def execute_ensure_block run_block(@ensure_block) if @ensure_block != false # nil should pass this test end
Executes the provided block that best matched the {Result}. If no block matched nothing is executed
@api private
# File lib/slayer/result_matcher.rb, line 196 def execute_matching_block if @matching_block != false # nil should pass this test run_block(@matching_block) elsif @matching_all != false run_block(@matching_all) elsif @default_block != false run_block(@default_block) elsif @default_all run_block(@default_all) end end
Provide a block that should be invoked if the {Result} is a failure.
@param statuses [Array<status>] Statuses that should be compared to the {Result}. If
any of provided statuses match the {Result} this block will be considered a match. The symbol +:default+ can also be used to indicate that this should match any {Result} not matched by other matchers. If no value is provided for statuses it defaults to +:default+.
# File lib/slayer/result_matcher.rb, line 147 def fail(*statuses, &block) statuses << :default if statuses.empty? @handled_default_fail ||= statuses.include?(:default) block_is_match = @result.failure? && statuses.include?(@status) block_is_default = @result.failure? && statuses.include?(:default) @matching_block = block if block_is_match @default_block = block if block_is_default end
@return Whether both the pass and the fail defaults have been handled.
@api private
# File lib/slayer/result_matcher.rb, line 188 def handled_defaults? return @handled_default_pass && @handled_default_fail end
Provide a block that should be invoked if the {Result} is a success.
@param statuses [Array<status>] Statuses that should be compared to the {Result}. If
any of provided statuses match the {Result} this block will be considered a match. The symbol +:default+ can also be used to indicate that this should match any {Result} not matched by other matchers. If no value is provided for statuses it defaults to +:default+.
# File lib/slayer/result_matcher.rb, line 128 def pass(*statuses, &block) statuses << :default if statuses.empty? @handled_default_pass ||= statuses.include?(:default) block_is_match = @result.success? && statuses.include?(@status) block_is_default = @result.success? && statuses.include?(:default) @matching_block = block if block_is_match @default_block = block if block_is_default end
Private Instance Methods
# File lib/slayer/result_matcher.rb, line 216 def run_block(block) block.call(@result.value, @result, @command) if block # explicit nil should fail this test end