rspec-expectations 2.13.0 → 2.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +15 -0
  2. data/Changelog.md +204 -23
  3. data/README.md +43 -87
  4. data/features/README.md +10 -10
  5. data/features/built_in_matchers/README.md +41 -41
  6. data/features/built_in_matchers/be.feature +40 -40
  7. data/features/built_in_matchers/be_within.feature +3 -3
  8. data/features/built_in_matchers/expect_change.feature +6 -6
  9. data/features/built_in_matchers/expect_error.feature +2 -2
  10. data/features/built_in_matchers/start_with.feature +1 -1
  11. data/features/built_in_matchers/throw_symbol.feature +11 -11
  12. data/features/built_in_matchers/yield.feature +18 -3
  13. data/features/custom_matchers/define_diffable_matcher.feature +1 -1
  14. data/features/custom_matchers/define_matcher_outside_rspec.feature +6 -6
  15. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +1 -1
  16. data/features/customized_message.feature +1 -1
  17. data/features/diffing.feature +1 -1
  18. data/features/step_definitions/additional_cli_steps.rb +10 -0
  19. data/features/support/env.rb +10 -1
  20. data/features/support/rubinius.rb +6 -0
  21. data/features/syntax_configuration.feature +3 -0
  22. data/features/test_frameworks/test_unit.feature +55 -17
  23. data/lib/rspec/expectations/caller_filter.rb +60 -0
  24. data/lib/rspec/{matchers → expectations}/configuration.rb +5 -3
  25. data/lib/rspec/expectations/deprecation.rb +20 -31
  26. data/lib/rspec/expectations/differ.rb +48 -9
  27. data/lib/rspec/expectations/expectation_target.rb +81 -15
  28. data/lib/rspec/expectations/extensions/object.rb +2 -12
  29. data/lib/rspec/expectations/fail_with.rb +11 -1
  30. data/lib/rspec/expectations/handler.rb +16 -7
  31. data/lib/rspec/expectations/syntax.rb +5 -7
  32. data/lib/rspec/expectations/version.rb +1 -1
  33. data/lib/rspec/expectations.rb +28 -2
  34. data/lib/rspec/matchers/be_close.rb +4 -1
  35. data/lib/rspec/matchers/built_in/base_matcher.rb +17 -10
  36. data/lib/rspec/matchers/built_in/be.rb +69 -32
  37. data/lib/rspec/matchers/built_in/be_within.rb +9 -3
  38. data/lib/rspec/matchers/built_in/change.rb +49 -3
  39. data/lib/rspec/matchers/built_in/has.rb +40 -7
  40. data/lib/rspec/matchers/built_in/have.rb +170 -5
  41. data/lib/rspec/matchers/built_in/include.rb +3 -11
  42. data/lib/rspec/matchers/built_in/match.rb +5 -0
  43. data/lib/rspec/matchers/built_in/match_array.rb +1 -1
  44. data/lib/rspec/matchers/built_in/raise_error.rb +34 -8
  45. data/lib/rspec/matchers/built_in/respond_to.rb +7 -1
  46. data/lib/rspec/matchers/built_in/satisfy.rb +7 -1
  47. data/lib/rspec/matchers/built_in/throw_symbol.rb +10 -2
  48. data/lib/rspec/matchers/built_in/yield.rb +102 -5
  49. data/lib/rspec/matchers/built_in.rb +2 -2
  50. data/lib/rspec/matchers/differentiate_block_method_types.rb +55 -0
  51. data/lib/rspec/matchers/dsl.rb +2 -1
  52. data/lib/rspec/matchers/match_aliases.rb +22 -0
  53. data/lib/rspec/matchers/matcher.rb +131 -10
  54. data/lib/rspec/matchers/operator_matcher.rb +70 -66
  55. data/lib/rspec/matchers/pretty.rb +11 -1
  56. data/lib/rspec/matchers/test_unit_integration.rb +28 -0
  57. data/lib/rspec/matchers.rb +175 -146
  58. data/lib/rspec-expectations.rb +5 -0
  59. data/spec/rspec/{matchers → expectations}/configuration_spec.rb +78 -91
  60. data/spec/rspec/expectations/differ_spec.rb +65 -6
  61. data/spec/rspec/expectations/expectation_target_spec.rb +72 -3
  62. data/spec/rspec/expectations/extensions/kernel_spec.rb +9 -5
  63. data/spec/rspec/expectations/fail_with_spec.rb +19 -0
  64. data/spec/rspec/expectations/handler_spec.rb +43 -22
  65. data/spec/rspec/expectations/syntax_spec.rb +51 -9
  66. data/spec/rspec/expectations_spec.rb +71 -0
  67. data/spec/rspec/matchers/base_matcher_spec.rb +33 -18
  68. data/spec/rspec/matchers/be_close_spec.rb +9 -6
  69. data/spec/rspec/matchers/be_spec.rb +146 -45
  70. data/spec/rspec/matchers/be_within_spec.rb +8 -0
  71. data/spec/rspec/matchers/change_spec.rb +107 -7
  72. data/spec/rspec/matchers/description_generation_spec.rb +38 -20
  73. data/spec/rspec/matchers/differentiate_block_method_types_spec.rb +39 -0
  74. data/spec/rspec/matchers/eq_spec.rb +1 -1
  75. data/spec/rspec/matchers/equal_spec.rb +26 -0
  76. data/spec/rspec/matchers/exist_spec.rb +9 -9
  77. data/spec/rspec/matchers/has_spec.rb +25 -1
  78. data/spec/rspec/matchers/have_spec.rb +411 -3
  79. data/spec/rspec/matchers/include_matcher_integration_spec.rb +2 -2
  80. data/spec/rspec/matchers/include_spec.rb +4 -4
  81. data/spec/rspec/matchers/match_array_spec.rb +1 -1
  82. data/spec/rspec/matchers/match_spec.rb +14 -1
  83. data/spec/rspec/matchers/matcher_spec.rb +213 -24
  84. data/spec/rspec/matchers/operator_matcher_spec.rb +56 -8
  85. data/spec/rspec/matchers/pretty_spec.rb +23 -0
  86. data/spec/rspec/matchers/raise_error_spec.rb +242 -102
  87. data/spec/rspec/matchers/respond_to_spec.rb +4 -4
  88. data/spec/rspec/matchers/satisfy_spec.rb +1 -1
  89. data/spec/rspec/matchers/start_with_end_with_spec.rb +2 -2
  90. data/spec/rspec/matchers/throw_symbol_spec.rb +14 -14
  91. data/spec/rspec/matchers/yield_spec.rb +81 -4
  92. data/spec/spec_helper.rb +5 -3
  93. data/spec/support/helper_methods.rb +42 -0
  94. data/spec/support/shared_examples.rb +42 -0
  95. metadata +40 -44
  96. data/spec/rspec/matchers/matchers_spec.rb +0 -37
@@ -1,11 +1,12 @@
1
1
  Feature: Test::Unit integration
2
2
 
3
- RSpec-expectations is a stand-alone gem that can be used without
4
- the rest of RSpec. It can easily be used with another test
5
- framework such as Test::Unit if you like RSpec's should/should_not
6
- syntax but prefer the test organization of another framework.
3
+ RSpec-expectations is a stand-alone gem that can be used without the rest of
4
+ RSpec. If you like the way Test::Unit (or MiniTest) organizes tests, but
5
+ prefer RSpec's approach to expressing expectations, you can have both.
7
6
 
8
- Scenario: Basic Test::Unit usage
7
+ The one downside is that failures are reported as errors with MiniTest.
8
+
9
+ Scenario: use rspec/expectations with Test::Unit
9
10
  Given a file named "rspec_expectations_test.rb" with:
10
11
  """ruby
11
12
  require 'test/unit'
@@ -17,30 +18,67 @@ Feature: Test::Unit integration
17
18
  end
18
19
 
19
20
  def be_an_int
20
- RSpec.deprecate(:be_an_int, :be_an_integer)
21
+ # This is actually an internal rspec-expectations API, but is used
22
+ # here to demonstrate that deprecation warnings from within
23
+ # rspec-expectations work correcty without depending on rspec-core
24
+ RSpec.deprecate(:be_an_int, :replacement => :be_an_integer)
21
25
  be_an_integer
22
26
  end
23
27
 
24
28
  def test_passing_expectation
25
- x = 1 + 3
26
- x.should == 4
29
+ expect(1 + 3).to eq 4
27
30
  end
28
31
 
29
32
  def test_failing_expectation
30
- array = [1, 2]
31
- array.should be_empty
32
- end
33
-
34
- def test_expect_matcher
35
- expect { @a = 5 }.to change { @a }.from(nil).to(5)
33
+ expect([1,2]).to be_empty
36
34
  end
37
35
 
38
- def test_custom_matcher_and_deprecation_warning
39
- 1.should be_an_int
36
+ def test_custom_matcher_with_deprecation_warning
37
+ expect(1).to be_an_int
40
38
  end
41
39
  end
42
40
  """
43
41
  When I run `ruby rspec_expectations_test.rb`
44
- Then the output should contain "4 tests, 0 assertions, 1 failures, 0 errors" or "4 tests, 0 assertions, 0 failures, 1 errors"
42
+ Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors"
45
43
  And the output should contain "expected empty? to return true, got false"
46
44
  And the output should contain "be_an_int is deprecated"
45
+ And the output should contain a deprecation warning about rspec-expecations' built-in integration
46
+
47
+ Scenario: use rspec/expectations with Test::Unit
48
+ Given a file named "rspec_expectations_with_manual_integration_test.rb" with:
49
+ """
50
+ require 'test/unit'
51
+ require "rspec/expectations"
52
+
53
+ class RSpecExpectationsTest < Test::Unit::TestCase
54
+ include ::RSpec::Matchers
55
+
56
+ RSpec::Matchers.define :be_an_integer do
57
+ match { |actual| Integer === actual }
58
+ end
59
+
60
+ def be_an_int
61
+ # This is actually an internal rspec-expectations API, but is used
62
+ # here to demonstrate that deprecation warnings from within
63
+ # rspec-expectations work correcty without depending on rspec-core
64
+ RSpec.deprecate(:be_an_int, :replacement => :be_an_integer)
65
+ be_an_integer
66
+ end
67
+
68
+ def test_passing_expectation
69
+ expect(1 + 3).to eq 4
70
+ end
71
+
72
+ def test_failing_expectation
73
+ expect([1,2]).to be_empty
74
+ end
75
+
76
+ def test_custom_matcher_with_deprecation_warning
77
+ expect(1).to be_an_int
78
+ end
79
+ end
80
+ """
81
+ When I run `ruby rspec_expectations_with_manual_integration_test.rb`
82
+ Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors"
83
+ And the output should not contain a deprecation warning about rspec-expecations' built-in integration
84
+
@@ -0,0 +1,60 @@
1
+ module RSpec
2
+ # Consistent implementation for "cleaning" the caller method to strip out
3
+ # non-rspec lines. This enables errors to be reported at the call site in
4
+ # the code using the library, which is far more useful than the particular
5
+ # internal method that raised an error.
6
+
7
+ unless defined?(CallerFilter)
8
+
9
+ class CallerFilter
10
+
11
+ RSPEC_LIBS = %w[
12
+ core
13
+ mocks
14
+ expectations
15
+ matchers
16
+ rails
17
+ ]
18
+
19
+ ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun ]
20
+
21
+ LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)}
22
+
23
+ if RUBY_VERSION >= '2.0.0'
24
+ def self.first_non_rspec_line
25
+ # `caller` is an expensive method that scales linearly with the size of
26
+ # the stack. The performance hit for fetching it in chunks is small,
27
+ # and since the target line is probably near the top of the stack, the
28
+ # overall improvement of a chunked search like this is significant.
29
+ #
30
+ # See benchmarks/caller.rb for measurements.
31
+
32
+ # Initial value here is mostly arbitrary, but is chosen to give good
33
+ # performance on the common case of creating a double.
34
+ increment = 5
35
+ i = 1
36
+ line = nil
37
+
38
+ while !line
39
+ stack = caller(i, increment)
40
+ return nil unless stack
41
+
42
+ line = stack.find { |l| l !~ LIB_REGEX }
43
+
44
+ i += increment
45
+ increment *= 2 # The choice of two here is arbitrary.
46
+ end
47
+
48
+ line
49
+ end
50
+ else
51
+ # Earlier rubies do not support the two argument form of `caller`. This
52
+ # fallback is logically the same, but slower.
53
+ def self.first_non_rspec_line
54
+ caller.find { |line| line !~ LIB_REGEX }
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -1,7 +1,7 @@
1
1
  require 'rspec/expectations/syntax'
2
2
 
3
3
  module RSpec
4
- module Matchers
4
+ module Expectations
5
5
  # Provides configuration options for rspec-expectations.
6
6
  class Configuration
7
7
  # Configures the supported syntax.
@@ -94,11 +94,13 @@ module RSpec
94
94
  end
95
95
  end
96
96
  end
97
+ end
97
98
 
99
+ module Matchers
98
100
  # The configuration object
99
- # @return [RSpec::Matchers::Configuration] the configuration object
101
+ # @return [RSpec::Expectations::Configuration] the configuration object
100
102
  def self.configuration
101
- @configuration ||= Configuration.new
103
+ @configuration ||= Expectations::Configuration.new
102
104
  end
103
105
 
104
106
  # set default syntax
@@ -1,38 +1,27 @@
1
- module RSpec
2
- unless respond_to?(:deprecate)
3
- class << self
4
- # Used internally by RSpec to display standard deprecation warnings.
5
- # This is also defined in rspec-core, but we can't assume it's loaded
6
- # since rspec-expectations should be usable w/o rspec-core.
7
- def deprecate(method, alternate_method=nil, version=nil)
8
- version_string = version ? "rspec-#{version}" : "a future version of RSpec"
9
-
10
- message = <<-NOTICE
11
-
12
- *****************************************************************
13
- DEPRECATION WARNING: you are using deprecated behaviour that will
14
- be removed from #{version_string}.
15
-
16
- #{caller(0)[2]}
1
+ require 'rspec/expectations/caller_filter' unless defined?(::RSpec::CallerFilter)
17
2
 
18
- * #{method} is deprecated.
19
- NOTICE
20
- if alternate_method
21
- message << <<-ADDITIONAL
22
- * please use #{alternate_method} instead.
23
- ADDITIONAL
24
- end
25
-
26
- message << "*****************************************************************"
27
- warn_deprecation(message)
3
+ module RSpec
4
+ module Expectations
5
+ module Deprecation
6
+ # @private
7
+ #
8
+ # Used internally to print deprecation warnings
9
+ def deprecate(deprecated, options={})
10
+ message = "DEPRECATION: #{deprecated} is deprecated."
11
+ message << " Use #{options[:replacement]} instead." if options[:replacement]
12
+ message << " Called from #{CallerFilter.first_non_rspec_line}."
13
+ warn message
28
14
  end
29
15
 
30
- # Used internally by RSpec to display custom deprecation warnings. This
31
- # is also defined in rspec-core, but we can't assume it's loaded since
32
- # rspec-expectations should be usable w/o rspec-core.
33
- def warn_deprecation(message)
34
- warn(message)
16
+ # @private
17
+ #
18
+ # Used internally to print deprecation warnings
19
+ def warn_deprecation(warning)
20
+ message = "\nDEPRECATION: #{warning}\n"
21
+ warn message
35
22
  end
36
23
  end
37
24
  end
25
+
26
+ extend(Expectations::Deprecation) unless respond_to?(:deprecate)
38
27
  end
@@ -5,12 +5,14 @@ require 'pp'
5
5
  module RSpec
6
6
  module Expectations
7
7
  class Differ
8
+
8
9
  # This is snagged from diff/lcs/ldiff.rb (which is a commandline tool)
9
- def diff_as_string(data_new, data_old)
10
- data_old = data_old.split(/\n/).map! { |e| e.chomp }
11
- data_new = data_new.split(/\n/).map! { |e| e.chomp }
10
+ def diff_as_string(input_data_new, input_data_old)
11
+ encoding = pick_encoding input_data_new, input_data_old
12
+ output = matching_encoding("", encoding)
13
+ data_old = input_data_old.split(matching_encoding("\n", encoding)).map! { |e| e.chomp }
14
+ data_new = input_data_new.split(matching_encoding("\n", encoding)).map! { |e| e.chomp }
12
15
  diffs = Diff::LCS.diff(data_old, data_new)
13
- output = ""
14
16
  return output if diffs.empty?
15
17
  oldhunk = hunk = nil
16
18
  file_length_difference = 0
@@ -33,16 +35,24 @@ module RSpec
33
35
  hunk.unshift(oldhunk)
34
36
  end
35
37
  else
36
- output << oldhunk.diff(format)
38
+ output << matching_encoding(oldhunk.diff(format).to_s, encoding)
37
39
  end
38
40
  ensure
39
41
  oldhunk = hunk
40
- output << "\n"
42
+ output << matching_encoding("\n", encoding)
41
43
  end
42
44
  end
43
45
  #Handle the last remaining hunk
44
- output << oldhunk.diff(format) << "\n"
46
+ output << matching_encoding(oldhunk.diff(format).to_s, encoding)
47
+ output << matching_encoding("\n", encoding)
45
48
  color_diff output
49
+ rescue Encoding::CompatibilityError
50
+ if input_data_new.encoding != input_data_old.encoding
51
+ "Could not produce a diff because the encoding of the actual string (#{input_data_old.encoding}) "+
52
+ "differs from the encoding of the expected string (#{input_data_new.encoding})"
53
+ else
54
+ "Could not produce a diff because of the encoding of the string (#{input_data_old.encoding})"
55
+ end
46
56
  end
47
57
 
48
58
  def diff_as_object(actual, expected)
@@ -99,8 +109,16 @@ module RSpec
99
109
  def object_to_string(object)
100
110
  case object
101
111
  when Hash
102
- object.keys.sort_by { |k| k.to_s }.map do |k|
103
- %(#{PP.singleline_pp(k, "")} => #{PP.singleline_pp(object[k], "")})
112
+ object.keys.sort_by { |k| k.to_s }.map do |key|
113
+ pp_key = PP.singleline_pp(key, "")
114
+ pp_value = PP.singleline_pp(object[key], "")
115
+
116
+ # on 1.9.3 PP seems to minimise to US-ASCII, ensure we're matching source encoding
117
+ #
118
+ # note, PP is used to ensure the ordering of the internal values of key/value e.g.
119
+ # <# a: b: c:> not <# c: a: b:>
120
+ encoding = pick_encoding pp_key, pp_value
121
+ matching_encoding("#{pp_key} => #{pp_value}", encoding)
104
122
  end.join(",\n")
105
123
  when String
106
124
  object =~ /\n/ ? object : object.inspect
@@ -108,6 +126,27 @@ module RSpec
108
126
  PP.pp(object,"")
109
127
  end
110
128
  end
129
+
130
+ private
131
+
132
+ if String.method_defined?(:encoding)
133
+ def pick_encoding(source_a, source_b)
134
+ Encoding.compatible?(source_a, source_b) || Encoding.default_external
135
+ end
136
+
137
+ def matching_encoding(string, encoding)
138
+ string.encode encoding
139
+ rescue Encoding::UndefinedConversionError
140
+ string.encode(encoding, :undef => :replace)
141
+ end
142
+ else
143
+ def pick_encoding(source_a, source_b)
144
+ end
145
+
146
+ def matching_encoding(string, encoding)
147
+ string
148
+ end
149
+ end
111
150
  end
112
151
 
113
152
  end
@@ -1,23 +1,48 @@
1
1
  module RSpec
2
2
  module Expectations
3
3
  # Wraps the target of an expectation.
4
+ #
4
5
  # @example
5
- # expect(something) # => ExpectationTarget wrapping something
6
+ # expect(something) # => ExpectationTarget wrapping something
7
+ # expect { do_something } # => ExpectationTarget wrapping the block
6
8
  #
7
9
  # # used with `to`
8
10
  # expect(actual).to eq(3)
9
11
  #
10
- # # with `to_not`
11
- # expect(actual).to_not eq(3)
12
+ # # with `not_to`
13
+ # expect(actual).not_to eq(3)
14
+ #
15
+ # @note `ExpectationTarget` is not intended to be instantiated
16
+ # directly by users. Use `expect` instead.
12
17
  class ExpectationTarget
13
18
  class << self
14
19
  attr_accessor :deprecated_should_enabled
15
20
  alias deprecated_should_enabled? deprecated_should_enabled
16
21
  end
17
22
 
23
+ # @private
24
+ # Used as a sentinel value to be able to tell when the user
25
+ # did not pass an argument. We can't use `nil` for that because
26
+ # `nil` is a valid value to pass.
27
+ UndefinedValue = Module.new
28
+
18
29
  # @api private
19
- def initialize(target)
20
- @target = target
30
+ def initialize(value)
31
+ @target = value
32
+ end
33
+
34
+ # @private
35
+ def self.for(value, block)
36
+ if UndefinedValue.equal?(value)
37
+ unless block
38
+ raise ArgumentError, "You must pass either an argument or a block to `expect`."
39
+ end
40
+ BlockExpectationTarget.new(block)
41
+ elsif block
42
+ raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
43
+ else
44
+ new(value)
45
+ end
21
46
  end
22
47
 
23
48
  # Runs the given expectation, passing if `matcher` returns true.
@@ -30,35 +55,34 @@ module RSpec
30
55
  # @return [Boolean] true if the expectation succeeds (else raises)
31
56
  # @see RSpec::Matchers
32
57
  def to(matcher=nil, message=nil, &block)
33
- prevent_operator_matchers(:to, matcher)
58
+ prevent_operator_matchers(:to) unless matcher
34
59
  RSpec::Expectations::PositiveExpectationHandler.handle_matcher(@target, matcher, message, &block)
35
60
  end
36
61
 
37
62
  # Runs the given expectation, passing if `matcher` returns false.
38
63
  # @example
39
- # expect(value).to_not eq(5)
40
64
  # expect(value).not_to eq(5)
41
65
  # @param [Matcher]
42
66
  # matcher
43
67
  # @param [String] message optional message to display when the expectation fails
44
68
  # @return [Boolean] false if the negative expectation succeeds (else raises)
45
69
  # @see RSpec::Matchers
46
- def to_not(matcher=nil, message=nil, &block)
47
- prevent_operator_matchers(:to_not, matcher)
70
+ def not_to(matcher=nil, message=nil, &block)
71
+ prevent_operator_matchers(:not_to) unless matcher
48
72
  RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
49
73
  end
50
- alias not_to to_not
74
+ alias to_not not_to
51
75
 
52
76
  def self.enable_deprecated_should
53
77
  return if deprecated_should_enabled?
54
78
 
55
79
  def should(*args)
56
- RSpec.deprecate "`expect { }.should`", "`expect { }.to`", 3
80
+ RSpec.deprecate "`expect { }.should`", :replacement => "`expect { }.to`"
57
81
  @target.should(*args)
58
82
  end
59
83
 
60
84
  def should_not(*args)
61
- RSpec.deprecate "`expect { }.should_not`", "`expect { }.to_not`", 3
85
+ RSpec.deprecate "`expect { }.should_not`", :replacement => "`expect { }.not_to`"
62
86
  @target.should_not(*args)
63
87
  end
64
88
 
@@ -76,13 +100,55 @@ module RSpec
76
100
 
77
101
  private
78
102
 
79
- def prevent_operator_matchers(verb, matcher)
80
- return if matcher
81
-
103
+ def prevent_operator_matchers(verb)
82
104
  raise ArgumentError, "The expect syntax does not support operator matchers, " +
83
105
  "so you must pass a matcher to `##{verb}`."
84
106
  end
85
107
  end
108
+
109
+ # @private
110
+ # Validates the provided matcher to ensure it supports block
111
+ # expectations, in order to avoid user confusion when they
112
+ # use a block thinking the expectation will be on the return
113
+ # value of the block rather than the block itself.
114
+ class BlockExpectationTarget < ExpectationTarget
115
+ def to(matcher, message=nil, &block)
116
+ enforce_block_expectation(matcher)
117
+ super
118
+ end
119
+
120
+ def not_to(matcher, message=nil, &block)
121
+ enforce_block_expectation(matcher)
122
+ super
123
+ end
124
+ alias to_not not_to
125
+
126
+ private
127
+
128
+ def enforce_block_expectation(matcher)
129
+ return if supports_block_expectations?(matcher)
130
+
131
+ RSpec.deprecate("Using a matcher in a block expectation expression " +
132
+ "(e.g. `expect { }.to matcher`) that does not implement " +
133
+ "`supports_block_expectations?`",
134
+ :replacement => "a value expectation expression " +
135
+ "(e.g. `expect(value).to matcher`) or implement " +
136
+ "`supports_block_expectations?` on the provided matcher " +
137
+ "(#{description_of matcher})")
138
+ end
139
+
140
+ def supports_block_expectations?(matcher)
141
+ matcher.supports_block_expectations?
142
+ rescue NoMethodError
143
+ false
144
+ end
145
+
146
+ def description_of(matcher)
147
+ matcher.description
148
+ rescue NoMethodError
149
+ matcher.inspect
150
+ end
151
+ end
86
152
  end
87
153
  end
88
154
 
@@ -6,17 +6,7 @@ module RSpec
6
6
  def const_missing(name)
7
7
  case name
8
8
  when :Rspec, :Spec
9
- RSpec.warn_deprecation <<-WARNING
10
- *****************************************************************
11
- DEPRECATION WARNING: you are using a deprecated constant that will
12
- be removed from a future version of RSpec.
13
-
14
- #{caller(0)[2]}
15
-
16
- * #{name} is deprecated.
17
- * RSpec is the new top-level module in RSpec-2
18
- ***************************************************************
19
- WARNING
9
+ RSpec.deprecate(name.to_s, :replacement => "RSpec")
20
10
  RSpec
21
11
  else
22
12
  begin
@@ -31,7 +21,7 @@ WARNING
31
21
 
32
22
  # @deprecated (no replacement)
33
23
  def differ=(ignore)
34
- RSpec.deprecate("RSpec::Expectations.differ=(differ)", "nothing at all (diffing is now automatic and no longer configurable)")
24
+ RSpec.deprecate("RSpec::Expectations.differ=(differ)")
35
25
  end
36
26
  end
37
27
  end
@@ -52,7 +52,17 @@ module RSpec
52
52
 
53
53
  def coerce_to_string(string_or_array)
54
54
  return string_or_array unless Array === string_or_array
55
- string_or_array.join(',')
55
+ diffably_stringify(string_or_array).join("\n")
56
+ end
57
+
58
+ def diffably_stringify(array)
59
+ array.map do |entry|
60
+ if Array === entry
61
+ entry.inspect
62
+ else
63
+ entry.to_s.gsub("\n", "\\n")
64
+ end
65
+ end
56
66
  end
57
67
 
58
68
  if String.method_defined?(:encoding)
@@ -2,13 +2,14 @@ module RSpec
2
2
  module Expectations
3
3
 
4
4
  class ExpectationHandler
5
- def self.message_must_be_string(msg)
6
- "WARNING: ignoring the provided expectation message argument " +
7
- "(#{msg.inspect}) since it is not a string."
8
- end
9
-
10
5
  def self.check_message(msg)
11
- ::Kernel.warn message_must_be_string(msg) unless msg.nil? || msg.is_a?(String)
6
+ unless msg.nil? || msg.respond_to?(:to_str) || msg.respond_to?(:call)
7
+ ::Kernel.warn [
8
+ "WARNING: ignoring the provided expectation message argument (",
9
+ msg.inspect,
10
+ ") since it is not a string or a proc."
11
+ ].join
12
+ end
12
13
  end
13
14
  end
14
15
 
@@ -23,12 +24,18 @@ module RSpec
23
24
  match = matcher.matches?(actual, &block)
24
25
  return match if match
25
26
 
27
+ message = message.call if message.respond_to?(:call)
28
+
26
29
  message ||= matcher.respond_to?(:failure_message_for_should) ?
27
30
  matcher.failure_message_for_should :
28
31
  matcher.failure_message
29
32
 
30
33
  if matcher.respond_to?(:diffable?) && matcher.diffable?
31
- ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
34
+ if RSpec::Matchers::DSL::Matcher === matcher
35
+ ::RSpec::Expectations.fail_with message, matcher.expected_as_array, matcher.actual
36
+ else
37
+ ::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
38
+ end
32
39
  else
33
40
  ::RSpec::Expectations.fail_with message
34
41
  end
@@ -47,6 +54,8 @@ module RSpec
47
54
  matcher.matches?(actual, &block)
48
55
  return match unless match
49
56
 
57
+ message = message.call if message.respond_to?(:call)
58
+
50
59
  message ||= matcher.respond_to?(:failure_message_for_should_not) ?
51
60
  matcher.failure_message_for_should_not :
52
61
  matcher.negative_failure_message
@@ -32,10 +32,10 @@ module RSpec
32
32
  # `ExpectationTarget`.
33
33
  # @example
34
34
  # expect(actual).to eq(expected)
35
- # expect(actual).to_not eq(expected)
35
+ # expect(actual).not_to eq(expected)
36
36
  # @return [ExpectationTarget]
37
37
  # @see ExpectationTarget#to
38
- # @see ExpectationTarget#to_not
38
+ # @see ExpectationTarget#not_to
39
39
 
40
40
  # @api private
41
41
  # Determines where we add `should` and `should_not`.
@@ -79,11 +79,9 @@ module RSpec
79
79
  def enable_expect(syntax_host = ::RSpec::Matchers)
80
80
  return if expect_enabled?(syntax_host)
81
81
 
82
- syntax_host.module_eval do
83
- def expect(*target, &target_block)
84
- target << target_block if block_given?
85
- raise ArgumentError.new("You must pass an argument or a block to #expect but not both.") unless target.size == 1
86
- ::RSpec::Expectations::ExpectationTarget.new(target.first)
82
+ syntax_host.module_exec do
83
+ def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
84
+ ::RSpec::Expectations::ExpectationTarget.for(value, block)
87
85
  end
88
86
  end
89
87
 
@@ -2,7 +2,7 @@ module RSpec
2
2
  module Expectations
3
3
  # @private
4
4
  module Version
5
- STRING = '2.13.0'
5
+ STRING = '2.99.0'
6
6
  end
7
7
  end
8
8
  end
@@ -1,7 +1,7 @@
1
1
  require 'rspec/expectations/extensions'
2
2
  require 'rspec/matchers'
3
3
  require 'rspec/expectations/expectation_target'
4
- require 'rspec/matchers/configuration'
4
+ require 'rspec/expectations/configuration'
5
5
  require 'rspec/expectations/fail_with'
6
6
  require 'rspec/expectations/errors'
7
7
  require 'rspec/expectations/deprecation'
@@ -37,11 +37,37 @@ module RSpec
37
37
  # `eq.failure_message_for_should_not`.
38
38
  #
39
39
  # rspec-expectations ships with a standard set of useful matchers, and writing
40
- # your own matchers is quite simple.
40
+ # your own matchers is quite simple.
41
41
  #
42
42
  # See [RSpec::Matchers](../RSpec/Matchers) for more information about the
43
43
  # built-in matchers that ship with rspec-expectations, and how to write your
44
44
  # own custom matchers.
45
45
  module Expectations
46
+
47
+ # @api private
48
+ KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method)
49
+
50
+ # @api private
51
+ #
52
+ # Used internally to get a method handle for a particular object
53
+ # and method name.
54
+ #
55
+ # Includes handling for a few special cases:
56
+ #
57
+ # - Objects that redefine #method (e.g. an HTTPRequest struct)
58
+ # - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator)
59
+ if RUBY_VERSION.to_i >= 2
60
+ def self.method_handle_for(object, method_name)
61
+ KERNEL_METHOD_METHOD.bind(object).call(method_name)
62
+ end
63
+ else
64
+ def self.method_handle_for(object, method_name)
65
+ if ::Kernel === object
66
+ KERNEL_METHOD_METHOD.bind(object).call(method_name)
67
+ else
68
+ object.method(method_name)
69
+ end
70
+ end
71
+ end
46
72
  end
47
73
  end