class RSpec::Matchers::BuiltIn::YieldProbe
def self.assert_valid_expect_block!(block)
def self.assert_valid_expect_block!(block) return if block.arity == 1 raise "Your expect block must accept an argument to be used with this " + "matcher. Pass the argument as a block on to the method you are testing." end
def self.probe(block)
def self.probe(block) probe = new assert_valid_expect_block!(block) block.call(probe) probe.assert_used! probe end
def assert_used!
def assert_used! return if @used raise "You must pass the argument yielded to your expect block on " + "to the method-under-test as a block. It acts as a probe that " + "allows the matcher to detect whether or not the method-under-test " + "yields, and, if so, how many times, and what the yielded arguments " + "are." end
def initialize
def initialize @used = false self.num_yields, self.yielded_args = 0, [] end
def single_yield_args
def single_yield_args yielded_args.first end
def successive_yield_args
def successive_yield_args yielded_args.map do |arg_array| arg_array.size == 1 ? arg_array.first : arg_array end end
def to_proc
def to_proc @used = true probe = self Proc.new do |*args| probe.num_yields += 1 probe.yielded_args << args end end
def yielded_once?(matcher_name)
def yielded_once?(matcher_name) case num_yields when 1 then true when 0 then false else raise "The #{matcher_name} matcher is not designed to be used with a " + "method that yields multiple times. Use the yield_successive_args " + "matcher for that case." end end