Skip to content

Commit

Permalink
Merge pull request #119 from rspec/expect_syntax
Browse files Browse the repository at this point in the history
Add support for `expect(value)` syntax.
  • Loading branch information
myronmarston committed May 22, 2012
2 parents b396938 + f00de57 commit 71a2c8d
Show file tree
Hide file tree
Showing 12 changed files with 459 additions and 53 deletions.
2 changes: 2 additions & 0 deletions lib/rspec/expectations.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'rspec/expectations/extensions'
require 'rspec/matchers'
require 'rspec/matchers/configuration'
require 'rspec/expectations/expectation_target'
require 'rspec/expectations/fail_with'
require 'rspec/expectations/errors'
require 'rspec/expectations/deprecation'
Expand Down
52 changes: 52 additions & 0 deletions lib/rspec/expectations/expectation_target.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module RSpec
module Expectations
# Wraps the target of an expectation.
# @example
# expect(something) # => ExpectationTarget wrapping something
class ExpectationTarget
# @api private
def initialize(target)
@target = target
end

# Runs the given expectation, passing if `matcher` returns true.
# @example
# expect(value).to eq(5)
# expect { perform }.to raise_error
# @param [Matcher]
# matcher
# @param [String] message optional message to display when the expectation fails
# @return [Boolean] true if the expectation succeeds (else raises)
# @see RSpec::Matchers
def to(matcher=nil, message=nil, &block)
prevent_operator_matchers(:to, matcher)
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(@target, matcher, message, &block)
end

# Runs the given expectation, passing if `matcher` returns false.
# @example
# expect(value).to_not eq(5)
# expect(value).not_to eq(5)
# @param [Matcher]
# matcher
# @param [String] message optional message to display when the expectation fails
# @return [Boolean] false if the negative expectation succeeds (else raises)
# @see RSpec::Matchers
def to_not(matcher=nil, message=nil, &block)
prevent_operator_matchers(:to_not, matcher)
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
end
alias not_to to_not

private

def prevent_operator_matchers(verb, matcher)
return if matcher

raise ArgumentError, "The expect syntax does not support operator matchers, " +
"so you must pass a matcher to `##{verb}`."
end
end
end
end

1 change: 0 additions & 1 deletion lib/rspec/expectations/extensions.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
require 'rspec/expectations/extensions/kernel'
require 'rspec/expectations/extensions/array'
require 'rspec/expectations/extensions/object'
26 changes: 0 additions & 26 deletions lib/rspec/expectations/extensions/kernel.rb

This file was deleted.

91 changes: 91 additions & 0 deletions lib/rspec/expectations/syntax.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
module RSpec
module Expectations
# @api private
# Provides methods for enabling and disabling the available
# syntaxes provided by rspec-expectations.
module Syntax
extend self

# @api private
# Enables the `should` syntax.
def enable_should(syntax_host = ::Kernel)
return if should_enabled?(syntax_host)

syntax_host.module_eval do
# Passes if +matcher+ returns true. Available on every +Object+.
# @example
# actual.should eq(expected)
# actual.should be > 4
# @param [Matcher]
# matcher
# @param [String] message optional message to display when the expectation fails
# @return [Boolean] true if the expectation succeeds (else raises)
# @see RSpec::Matchers
def should(matcher=nil, message=nil, &block)
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
end

# Passes if +matcher+ returns false. Available on every +Object+.
# @example
# actual.should_not eq(expected)
# @param [Matcher]
# matcher
# @param [String] message optional message to display when the expectation fails
# @return [Boolean] false if the negative expectation succeeds (else raises)
# @see RSpec::Matchers
def should_not(matcher=nil, message=nil, &block)
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
end
end
end

# @api private
# Disables the `should` syntax.
def disable_should(syntax_host = ::Kernel)
return unless should_enabled?(syntax_host)

syntax_host.module_eval do
undef should
undef should_not
end
end

# @api private
# Enables the `expect` syntax.
def enable_expect(syntax_host = ::RSpec::Matchers)
return if expect_enabled?(syntax_host)

syntax_host.module_eval do
def expect(*target, &target_block)
target << target_block if block_given?
raise ArgumentError.new("You must pass an argument or a block to #expect but not both.") unless target.size == 1
::RSpec::Expectations::ExpectationTarget.new(target.first)
end
end
end

# @api private
# Disables the `expect` syntax.
def disable_expect(syntax_host = ::RSpec::Matchers)
return unless expect_enabled?(syntax_host)

syntax_host.module_eval do
undef expect
end
end

# @api private
# Indicates whether or not the `should` syntax is enabled.
def should_enabled?(syntax_host = ::Kernel)
syntax_host.method_defined?(:should)
end

# @api private
# Indicates whether or not the `expect` syntax is enabled.
def expect_enabled?(syntax_host = ::RSpec::Matchers)
syntax_host.method_defined?(:expect)
end
end
end
end

17 changes: 12 additions & 5 deletions lib/rspec/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ module Matchers
require 'rspec/matchers/operator_matcher'
require 'rspec/matchers/be_close'

require 'rspec/matchers/block_aliases'
require 'rspec/matchers/generated_descriptions'
require 'rspec/matchers/method_missing'
require 'rspec/matchers/compatibility'
Expand Down Expand Up @@ -664,19 +663,27 @@ def yield_successive_args(*args)
BuiltIn::YieldSuccessiveArgs.new(*args)
end

# Passes if actual contains all of the expected regardless of order.
# This works for collections. Pass in multiple args and it will only
# Passes if actual contains all of the expected regardless of order.
# This works for collections. Pass in multiple args and it will only
# pass if all args are found in collection.
#
# NOTE: there is no should_not version of array.should =~ other_array
#
# @note This is also available using the `=~` operator with `should`,
# but `=~` is not supported with `expect`.
# @note There is no should_not version of array.should =~ other_array
#
# @example
#
# expect([1,2,3]).to match_array([1,2,3])
# expect([1,2,3]).to match_array([1,3,2])
# [1,2,3].should =~ [1,2,3] # => would pass
# [1,2,3].should =~ [2,3,1] # => would pass
# [1,2,3,4].should =~ [1,2,3] # => would fail
# [1,2,2,3].should =~ [1,2,3] # => would fail
# [1,2,3].should =~ [1,2,3,4] # => would fail
def match_array(array)
BuiltIn::MatchArray.new(array)
end

OperatorMatcher.register(Array, '=~', BuiltIn::MatchArray)
end
end
21 changes: 0 additions & 21 deletions lib/rspec/matchers/block_aliases.rb

This file was deleted.

53 changes: 53 additions & 0 deletions lib/rspec/matchers/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'rspec/expectations/syntax'

module RSpec
module Matchers
# Provides configuration options for rspec-expectations.
class Configuration
# Configures the supported syntax.
# @param [Array<Symbol>, Symbol] values the syntaxes to enable
# @example
# RSpec.configure do |rspec|
# rspec.expect_with :rspec do |c|
# c.syntax = :should
# # or
# c.syntax = :expect
# # or
# c.syntax = [:should, :expect]
# end
# end
def syntax=(values)
if Array(values).include?(:expect)
Expectations::Syntax.enable_expect
else
Expectations::Syntax.disable_expect
end

if Array(values).include?(:should)
Expectations::Syntax.enable_should
else
Expectations::Syntax.disable_should
end
end

# The list of configured syntaxes.
# @return [Array<Symbol>] the list of configured syntaxes.
def syntax
syntaxes = []
syntaxes << :should if Expectations::Syntax.should_enabled?
syntaxes << :expect if Expectations::Syntax.expect_enabled?
syntaxes
end
end

# The configuration object
# @return [RSpec::Matchers::Configuration] the configuration object
def self.configuration
@configuration ||= Configuration.new
end

# set default syntax
configuration.syntax = [:expect, :should]
end
end

65 changes: 65 additions & 0 deletions spec/rspec/expectations/expectation_target_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require 'spec_helper'

module RSpec
module Expectations
# so our examples below can set expectations about the target
ExpectationTarget.send(:attr_reader, :target)

describe ExpectationTarget do
context 'when constructed via #expect' do
it 'constructs a new instance targetting the given argument' do
expect(7).target.should eq(7)
end

it 'constructs a new instance targetting the given block' do
block = lambda {}
expect(&block).target.should be(block)
end

it 'raises an ArgumentError when given an argument and a block' do
lambda { expect(7) { } }.should raise_error(ArgumentError)
end

it 'raises an ArgumentError when given neither an argument nor a block' do
lambda { expect }.should raise_error(ArgumentError)
end

it 'can be passed nil' do
expect(nil).target.should be_nil
end

it 'passes a valid positive expectation' do
expect(5).to eq(5)
end

it 'passes a valid negative expectation' do
expect(5).to_not eq(4)
expect(5).not_to eq(4)
end

it 'fails an invalid positive expectation' do
lambda { expect(5).to eq(4) }.should fail_with(/expected: 4.*got: 5/m)
end

it 'fails an invalid negative expectation' do
message = /expected 5 not to be a kind of Fixnum/
lambda { expect(5).to_not be_a(Fixnum) }.should fail_with(message)
lambda { expect(5).not_to be_a(Fixnum) }.should fail_with(message)
end

it 'does not support operator matchers from #to' do
expect {
expect(3).to == 3
}.to raise_error(ArgumentError)
end

it 'does not support operator matchers from #not_to' do
expect {
expect(3).not_to == 4
}.to raise_error(ArgumentError)
end
end
end
end
end

Loading

0 comments on commit 71a2c8d

Please sign in to comment.