Skip to content

Commit

Permalink
document methods in the matcher DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
dchelimsky committed Nov 28, 2011
1 parent d3e5310 commit 17c04aa
Showing 1 changed file with 135 additions and 30 deletions.
165 changes: 135 additions & 30 deletions lib/rspec/matchers/matcher.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
module RSpec
module Matchers
# Provides the context in which the block passed to RSpec::Matchers.define
# will be evaluated.
class Matcher
include RSpec::Matchers::Extensions::InstanceEvalWithArgs
include RSpec::Matchers::Pretty
include RSpec::Matchers

attr_reader :expected, :actual, :rescued_exception

# @api private
def initialize(name, &declarations)
@name = name
@declarations = declarations
Expand All @@ -22,6 +25,7 @@ def initialize(name, &declarations)
}
end

# @api private
def for_expected(*expected)
@expected = expected
making_declared_methods_public do
Expand All @@ -30,6 +34,7 @@ def for_expected(*expected)
self
end

# @api private
# Used internally by +should+ and +should_not+.
def matches?(actual)
@actual = actual
Expand All @@ -49,74 +54,174 @@ def matches?(actual)
end
end

# Used internally by +should_not+
def does_not_match?(actual)
@actual = actual
@match_for_should_not_block ?
instance_eval_with_args(actual, &@match_for_should_not_block) :
!matches?(actual)
end

def include(*args)
singleton_class.__send__(:include, *args)
end

def define_method(name, &block)
singleton_class.__send__(:define_method, name, &block)
end

# See RSpec::Matchers
# Stores the block that is used to determine whether this matcher passes
# or fails. The block should return a boolean value. When the matcher is
# passed to `should` and the block returns `true`, then the expectation
# passes. Similarly, when the matcher is passed to `should_not` and the
# block returns `false`, then the expectation passes.
#
# Use `match_for_should` when used in conjuntion with
# `match_for_should_not`.
#
# @example
#
# RSpec::Matchers.define :be_even do
# match do |actual|
# actual.even?
# end
# end
#
# 4.should be_even # passes
# 3.should_not be_even # passes
# 3.should be_even # fails
# 4.should_not be_even # fails
#
# @yield [Object] actual the actual value (or receiver of should)
def match(&block)
@match_block = block
end
alias match_for_should match

# See RSpec::Matchers
alias_method :match_for_should, :match

# Use this to define the block for a negative expectation (`should_not`)
# when the positive and negative forms require different handling. This
# is rarely necessary, but can be helpful, for example, when specifying
# asynchronous processes that require different timeouts.
#
# @yield [Object] actual the actual value (or receiver of should)
def match_for_should_not(&block)
@match_for_should_not_block = block
end

# See RSpec::Matchers
# Use this instead of `match` when the block will raise an exception
# rather than returning false to indicate a failure.
#
# @example
#
# RSpec::Matchers.define :accept_as_valid do |candidate_address|
# match_unless_raises ValidationException do |validator|
# validator.validate(candidate_address)
# end
# end
#
# email_validator.should accept_as_valid("person@company.com")
def match_unless_raises(exception=Exception, &block)
@expected_exception = exception
match(&block)
end

# See RSpec::Matchers
# Customize the failure messsage to use when this matcher is invoked with
# `should`. Only use this when the message generated by default doesn't
# suit your needs.
#
# @example
#
# RSpec::Matchers.define :have_strength do |expected|
# match { ... }
#
# failure_message_for_should do |actual|
# "Expected strength of #{expected}, but had #{actual.strength}"
# end
# end
#
# @yield [Object] actual the actual object
def failure_message_for_should(&block)
cache_or_call_cached(:failure_message_for_should, &block)
end

# See RSpec::Matchers
# Customize the failure messsage to use when this matcher is invoked with
# `should_not`. Only use this when the message generated by default
# doesn't suit your needs.
#
# @example
#
# RSpec::Matchers.define :have_strength do |expected|
# match { ... }
#
# failure_message_for_should_not do |actual|
# "Expected not to have strength of #{expected}, but did"
# end
# end
#
# @yield [Object] actual the actual object
# @yield [Object] actual the actual object
def failure_message_for_should_not(&block)
cache_or_call_cached(:failure_message_for_should_not, &block)
end

# See RSpec::Matchers

# Customize the description to use for one-liners. Only use this when
# the description generated by default doesn't suit your needs.
#
# @example
#
# RSpec::Matchers.define :qualify_for do |expected|
# match { ... }
#
# description do
# "qualify for #{expected}"
# end
# end
def description(&block)
cache_or_call_cached(:description, &block)
end

#Used internally by objects returns by +should+ and +should_not+.
def diffable?
@diffable
end

# See RSpec::Matchers
# Tells the matcher to diff the actual and expected values in the failure
# message.
def diffable
@diffable = true
end

# See RSpec::Matchers
# Convenience for defining methods on this matcher to create a fluent
# interface. The trick about fluent interfaces is that each method must
# return self in order to chain methods together. `chain` handles that
# for you.
#
# @example
#
# RSpec::Matchers.define :have_errors_on do |key|
# chain :with do |message|
# @message = message
# end
#
# match do |actual|
# actual.errors[key] == @message
# end
# end
#
# minor.should have_errors_on(:age).with("Not old enough to participate")
def chain(method, &block)
define_method method do |*args|
block.call(*args)
self
end
end

# @api private
# Used internally by objects returns by +should+ and +should_not+.
def diffable?
@diffable
end

# @api private
# Used internally by +should_not+
def does_not_match?(actual)
@actual = actual
@match_for_should_not_block ?
instance_eval_with_args(actual, &@match_for_should_not_block) :
!matches?(actual)
end

private

def include(*args)
singleton_class.__send__(:include, *args)
end

def define_method(name, &block)
singleton_class.__send__(:define_method, name, &block)
end

def method_missing(method, *args, &block)
if $matcher_execution_context.respond_to?(method)
$matcher_execution_context.send method, *args, &block
Expand Down

0 comments on commit 17c04aa

Please sign in to comment.