Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

document methods in the matcher DSL

  • Loading branch information...
commit 17c04aa1d631761795f430d3a8cbb4e5d5da67a4 1 parent d3e5310
@dchelimsky dchelimsky authored
Showing with 135 additions and 30 deletions.
  1. +135 −30 lib/rspec/matchers/matcher.rb
View
165 lib/rspec/matchers/matcher.rb
@@ -1,5 +1,7 @@
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
@@ -7,6 +9,7 @@ class Matcher
attr_reader :expected, :actual, :rescued_exception
+ # @api private
def initialize(name, &declarations)
@name = name
@declarations = declarations
@@ -22,6 +25,7 @@ def initialize(name, &declarations)
}
end
+ # @api private
def for_expected(*expected)
@expected = expected
making_declared_methods_public do
@@ -30,6 +34,7 @@ def for_expected(*expected)
self
end
+ # @api private
# Used internally by +should+ and +should_not+.
def matches?(actual)
@actual = actual
@@ -49,65 +54,142 @@ 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)
@@ -115,8 +197,31 @@ def chain(method, &block)
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
Please sign in to comment.
Something went wrong with that request. Please try again.