rspec-expectations 3.0.0.beta2 → 3.0.0.rc1

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 (135) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -2
  4. data/.yardopts +0 -1
  5. data/Changelog.md +115 -35
  6. data/README.md +2 -2
  7. data/lib/rspec/expectations.rb +13 -8
  8. data/lib/rspec/{matchers → expectations}/configuration.rb +38 -13
  9. data/lib/rspec/expectations/expectation_target.rb +72 -8
  10. data/lib/rspec/expectations/fail_with.rb +10 -52
  11. data/lib/rspec/expectations/handler.rb +9 -11
  12. data/lib/rspec/expectations/syntax.rb +37 -35
  13. data/lib/rspec/expectations/version.rb +1 -1
  14. data/lib/rspec/matchers.rb +60 -9
  15. data/lib/rspec/matchers/aliased_matcher.rb +6 -0
  16. data/lib/rspec/matchers/built_in.rb +9 -1
  17. data/lib/rspec/matchers/built_in/all.rb +78 -0
  18. data/lib/rspec/matchers/built_in/base_matcher.rb +39 -1
  19. data/lib/rspec/matchers/built_in/be.rb +117 -42
  20. data/lib/rspec/matchers/built_in/be_between.rb +22 -0
  21. data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
  22. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
  23. data/lib/rspec/matchers/built_in/be_within.rb +26 -6
  24. data/lib/rspec/matchers/built_in/change.rb +89 -13
  25. data/lib/rspec/matchers/built_in/compound.rb +19 -3
  26. data/lib/rspec/matchers/built_in/contain_exactly.rb +17 -6
  27. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  28. data/lib/rspec/matchers/built_in/eq.rb +20 -5
  29. data/lib/rspec/matchers/built_in/eql.rb +15 -3
  30. data/lib/rspec/matchers/built_in/equal.rb +23 -6
  31. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  32. data/lib/rspec/matchers/built_in/has.rb +58 -3
  33. data/lib/rspec/matchers/built_in/include.rb +16 -1
  34. data/lib/rspec/matchers/built_in/match.rb +14 -4
  35. data/lib/rspec/matchers/built_in/operators.rb +16 -0
  36. data/lib/rspec/matchers/built_in/output.rb +47 -5
  37. data/lib/rspec/matchers/built_in/raise_error.rb +40 -23
  38. data/lib/rspec/matchers/built_in/respond_to.rb +37 -16
  39. data/lib/rspec/matchers/built_in/satisfy.rb +15 -0
  40. data/lib/rspec/matchers/built_in/start_and_end_with.rb +29 -14
  41. data/lib/rspec/matchers/built_in/throw_symbol.rb +32 -3
  42. data/lib/rspec/matchers/built_in/yield.rb +148 -44
  43. data/lib/rspec/matchers/composable.rb +48 -7
  44. data/lib/rspec/matchers/dsl.rb +45 -17
  45. data/lib/rspec/matchers/generated_descriptions.rb +7 -0
  46. data/lib/rspec/matchers/matcher_delegator.rb +6 -2
  47. data/lib/rspec/matchers/pretty.rb +15 -19
  48. metadata +33 -236
  49. metadata.gz.sig +0 -0
  50. data/features/README.md +0 -48
  51. data/features/Upgrade.md +0 -53
  52. data/features/built_in_matchers/README.md +0 -96
  53. data/features/built_in_matchers/be.feature +0 -175
  54. data/features/built_in_matchers/be_within.feature +0 -48
  55. data/features/built_in_matchers/comparisons.feature +0 -97
  56. data/features/built_in_matchers/contain_exactly.feature +0 -46
  57. data/features/built_in_matchers/cover.feature +0 -47
  58. data/features/built_in_matchers/end_with.feature +0 -48
  59. data/features/built_in_matchers/equality.feature +0 -136
  60. data/features/built_in_matchers/exist.feature +0 -45
  61. data/features/built_in_matchers/expect_change.feature +0 -59
  62. data/features/built_in_matchers/expect_error.feature +0 -144
  63. data/features/built_in_matchers/include.feature +0 -126
  64. data/features/built_in_matchers/match.feature +0 -51
  65. data/features/built_in_matchers/output.feature +0 -70
  66. data/features/built_in_matchers/predicates.feature +0 -161
  67. data/features/built_in_matchers/respond_to.feature +0 -84
  68. data/features/built_in_matchers/satisfy.feature +0 -33
  69. data/features/built_in_matchers/start_with.feature +0 -48
  70. data/features/built_in_matchers/throw_symbol.feature +0 -91
  71. data/features/built_in_matchers/types.feature +0 -116
  72. data/features/built_in_matchers/yield.feature +0 -161
  73. data/features/composing_matchers.feature +0 -250
  74. data/features/compound_expectations.feature +0 -45
  75. data/features/custom_matchers/access_running_example.feature +0 -53
  76. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  77. data/features/custom_matchers/define_matcher.feature +0 -340
  78. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -34
  79. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  80. data/features/customized_message.feature +0 -39
  81. data/features/diffing.feature +0 -85
  82. data/features/implicit_docstrings.feature +0 -52
  83. data/features/step_definitions/additional_cli_steps.rb +0 -22
  84. data/features/support/env.rb +0 -21
  85. data/features/support/rubinius.rb +0 -6
  86. data/features/syntax_configuration.feature +0 -71
  87. data/features/test_frameworks/minitest.feature +0 -44
  88. data/lib/rspec-expectations.rb +0 -1
  89. data/lib/rspec/expectations/diff_presenter.rb +0 -141
  90. data/lib/rspec/expectations/differ.rb +0 -44
  91. data/lib/rspec/expectations/encoded_string.rb +0 -56
  92. data/spec/rspec/expectations/diff_presenter_spec.rb +0 -249
  93. data/spec/rspec/expectations/encoded_string_spec.rb +0 -74
  94. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  95. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  96. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  97. data/spec/rspec/expectations/handler_spec.rb +0 -205
  98. data/spec/rspec/expectations/minitest_integration_spec.rb +0 -27
  99. data/spec/rspec/expectations/syntax_spec.rb +0 -89
  100. data/spec/rspec/expectations_spec.rb +0 -12
  101. data/spec/rspec/matchers/aliased_matcher_spec.rb +0 -48
  102. data/spec/rspec/matchers/aliases_spec.rb +0 -449
  103. data/spec/rspec/matchers/built_in/base_matcher_spec.rb +0 -83
  104. data/spec/rspec/matchers/built_in/be_between_spec.rb +0 -159
  105. data/spec/rspec/matchers/built_in/be_instance_of_spec.rb +0 -63
  106. data/spec/rspec/matchers/built_in/be_kind_of_spec.rb +0 -41
  107. data/spec/rspec/matchers/built_in/be_spec.rb +0 -592
  108. data/spec/rspec/matchers/built_in/be_within_spec.rb +0 -141
  109. data/spec/rspec/matchers/built_in/change_spec.rb +0 -808
  110. data/spec/rspec/matchers/built_in/compound_spec.rb +0 -292
  111. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +0 -441
  112. data/spec/rspec/matchers/built_in/cover_spec.rb +0 -69
  113. data/spec/rspec/matchers/built_in/eq_spec.rb +0 -156
  114. data/spec/rspec/matchers/built_in/eql_spec.rb +0 -41
  115. data/spec/rspec/matchers/built_in/equal_spec.rb +0 -106
  116. data/spec/rspec/matchers/built_in/exist_spec.rb +0 -124
  117. data/spec/rspec/matchers/built_in/has_spec.rb +0 -161
  118. data/spec/rspec/matchers/built_in/include_spec.rb +0 -540
  119. data/spec/rspec/matchers/built_in/match_spec.rb +0 -102
  120. data/spec/rspec/matchers/built_in/operators_spec.rb +0 -252
  121. data/spec/rspec/matchers/built_in/output_spec.rb +0 -165
  122. data/spec/rspec/matchers/built_in/raise_error_spec.rb +0 -461
  123. data/spec/rspec/matchers/built_in/respond_to_spec.rb +0 -292
  124. data/spec/rspec/matchers/built_in/satisfy_spec.rb +0 -44
  125. data/spec/rspec/matchers/built_in/start_and_end_with_spec.rb +0 -253
  126. data/spec/rspec/matchers/built_in/throw_symbol_spec.rb +0 -135
  127. data/spec/rspec/matchers/built_in/yield_spec.rb +0 -627
  128. data/spec/rspec/matchers/configuration_spec.rb +0 -213
  129. data/spec/rspec/matchers/description_generation_spec.rb +0 -191
  130. data/spec/rspec/matchers/dsl_spec.rb +0 -895
  131. data/spec/rspec/matchers/legacy_spec.rb +0 -101
  132. data/spec/rspec/matchers_spec.rb +0 -74
  133. data/spec/spec_helper.rb +0 -85
  134. data/spec/support/matchers.rb +0 -22
  135. data/spec/support/shared_examples.rb +0 -35
@@ -27,6 +27,12 @@ module RSpec
27
27
  AliasedMatcher.new(return_val, @description_block)
28
28
  end
29
29
 
30
+ # Provides the description of the aliased matcher. Aliased matchers
31
+ # are designed to behave identically to the original matcher except
32
+ # for this method. The description is different to reflect the aliased
33
+ # name.
34
+ #
35
+ # @api private
30
36
  def description
31
37
  @description_block.call(super)
32
38
  end
@@ -1,7 +1,14 @@
1
- require 'rspec/matchers/built_in/base_matcher'
1
+ RSpec::Support.require_rspec_matchers "built_in/base_matcher"
2
2
 
3
3
  module RSpec
4
4
  module Matchers
5
+ # Container module for all built-in matchers. The matcher classes are here
6
+ # (rather than directly under `RSpec::Matchers`) in order to prevent name
7
+ # collisions, since `RSpec::Matchers` gets included into the user's namespace.
8
+ #
9
+ # Autoloading is used to delay when the matcher classes get loaded, allowing
10
+ # rspec-matchers to boot faster, and avoiding loading matchers the user is
11
+ # not using.
5
12
  module BuiltIn
6
13
  autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
7
14
  autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
@@ -24,6 +31,7 @@ module RSpec
24
31
  autoload :Exist, 'rspec/matchers/built_in/exist'
25
32
  autoload :Has, 'rspec/matchers/built_in/has'
26
33
  autoload :Include, 'rspec/matchers/built_in/include'
34
+ autoload :All, 'rspec/matchers/built_in/all'
27
35
  autoload :Match, 'rspec/matchers/built_in/match'
28
36
  autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
29
37
  autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
@@ -0,0 +1,78 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `all`.
6
+ # Not intended to be instantiated directly.
7
+ class All < BaseMatcher
8
+
9
+ # @private
10
+ attr_reader :matcher, :failed_objects
11
+
12
+ def initialize(matcher)
13
+ @matcher = matcher
14
+ @failed_objects = {}
15
+ end
16
+
17
+ # @private
18
+ def does_not_match?(actual)
19
+ raise NotImplementedError, '`expect().not_to all( matcher )` is not supported.'
20
+ end
21
+
22
+ # @api private
23
+ # @return [String]
24
+ def failure_message
25
+ all_messages = [improve_hash_formatting(super)]
26
+ failed_objects.each do |index, matcher_failure_message|
27
+ all_messages << failure_message_for_item(index, matcher_failure_message)
28
+ end
29
+ all_messages.join("\n\n")
30
+ end
31
+
32
+ # @api private
33
+ # @return [String]
34
+ def description
35
+ described_items = surface_descriptions_in(matcher)
36
+ improve_hash_formatting "all#{to_sentence(described_items)}"
37
+ end
38
+
39
+ private
40
+
41
+ def match(_, actual)
42
+ index_failed_objects
43
+ failed_objects.empty?
44
+ end
45
+
46
+ def index_failed_objects
47
+ actual.each_with_index do |actual_item, index|
48
+ cloned_matcher = matcher.clone
49
+ matches = cloned_matcher.matches?(actual_item)
50
+ failed_objects[index] = cloned_matcher.failure_message unless matches
51
+ end
52
+ end
53
+
54
+ def failure_message_for_item(index, failure_message)
55
+ failure_message = indent_multiline_message( add_new_line_if_needed(failure_message) )
56
+ indent_multiline_message("object at index #{index} failed to match:#{failure_message}")
57
+ end
58
+
59
+ def add_new_line_if_needed(message)
60
+ message.start_with?("\n") ? message : "\n#{message}"
61
+ end
62
+
63
+ def indent_multiline_message(message)
64
+ message = message.sub(/\n+\z/, '')
65
+ message.lines.map do |line|
66
+ line =~ /\S/ ? ' ' + line : line
67
+ end.join
68
+ end
69
+
70
+ def initialize_copy(other)
71
+ @matcher = @matcher.clone
72
+ super
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -4,7 +4,7 @@ module RSpec
4
4
  # @api private
5
5
  #
6
6
  # Used _internally_ as a base class for matchers that ship with
7
- # rspec-expectations.
7
+ # rspec-expectations and rspec-rails.
8
8
  #
9
9
  # ### Warning:
10
10
  #
@@ -15,19 +15,33 @@ module RSpec
15
15
  include RSpec::Matchers::Pretty
16
16
  include RSpec::Matchers::Composable
17
17
 
18
+ # @api private
19
+ # Used to detect when no arg is passed to `initialize`.
20
+ # `nil` cannot be used because it's a valid value to pass.
18
21
  UNDEFINED = Object.new.freeze
19
22
 
23
+ # @private
20
24
  attr_reader :actual, :expected, :rescued_exception
21
25
 
22
26
  def initialize(expected = UNDEFINED)
23
27
  @expected = expected unless UNDEFINED.equal?(expected)
24
28
  end
25
29
 
30
+ # @api private
31
+ # Indicates if the match is successful. Delegates to `match`, which
32
+ # should be defined on a subclass. Takes care of consistently
33
+ # initializing the `actual` attribute.
26
34
  def matches?(actual)
27
35
  @actual = actual
28
36
  match(expected, actual)
29
37
  end
30
38
 
39
+ # @api private
40
+ # Used to wrap a block of code that will indicate failure by
41
+ # raising one of the named exceptions.
42
+ #
43
+ # This is used by rspec-rails for some of its matchers that
44
+ # wrap rails' assertions.
31
45
  def match_unless_raises(*exceptions)
32
46
  exceptions.unshift Exception if exceptions.empty?
33
47
  begin
@@ -38,25 +52,49 @@ module RSpec
38
52
  end
39
53
  end
40
54
 
55
+ # @api private
56
+ # Provides a good generic failure message. Based on `description`.
57
+ # When subclassing, if you are not satisfied with this failure message
58
+ # you often only need to override `description`.
59
+ # @return [String]
41
60
  def failure_message
42
61
  assert_ivars :@actual
43
62
  "expected #{@actual.inspect} to #{description}"
44
63
  end
45
64
 
65
+ # @api private
66
+ # Provides a good generic negative failure message. Based on `description`.
67
+ # When subclassing, if you are not satisfied with this failure message
68
+ # you often only need to override `description`.
69
+ # @return [String]
46
70
  def failure_message_when_negated
47
71
  assert_ivars :@actual
48
72
  "expected #{@actual.inspect} not to #{description}"
49
73
  end
50
74
 
75
+ # @api private
76
+ # Generates a "pretty" description using the logic in {Pretty}.
77
+ # @return [String]
51
78
  def description
52
79
  return name_to_sentence unless defined?(@expected)
53
80
  "#{name_to_sentence}#{to_sentence @expected}"
54
81
  end
55
82
 
83
+ # @api private
84
+ # Matchers are not diffable by default. Override this to make your
85
+ # subclass diffable.
56
86
  def diffable?
57
87
  false
58
88
  end
59
89
 
90
+ # @api private
91
+ # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
92
+ # rather than block matchers (i.e. meant to work with `expect { }`), so
93
+ # this defaults to false. Block matchers must override this to return true.
94
+ def supports_block_expectations?
95
+ false
96
+ end
97
+
60
98
  private
61
99
 
62
100
  def assert_ivars(*expected_ivars)
@@ -1,50 +1,79 @@
1
- require 'rspec/matchers/dsl'
2
-
3
1
  module RSpec
4
2
  module Matchers
5
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_truthy`.
6
+ # Not intended to be instantiated directly.
6
7
  class BeTruthy < BaseMatcher
7
- def match(_, actual)
8
- !!actual
9
- end
10
8
 
9
+ # @api private
10
+ # @return [String]
11
11
  def failure_message
12
12
  "expected: truthy value\n got: #{actual.inspect}"
13
13
  end
14
14
 
15
+ # @api private
16
+ # @return [String]
15
17
  def failure_message_when_negated
16
18
  "expected: falsey value\n got: #{actual.inspect}"
17
19
  end
18
- end
19
20
 
20
- class BeFalsey < BaseMatcher
21
+ private
22
+
21
23
  def match(_, actual)
22
- !actual
24
+ !!actual
23
25
  end
26
+ end
24
27
 
28
+ # @api private
29
+ # Provides the implementation for `be_falsey`.
30
+ # Not intended to be instantiated directly.
31
+ class BeFalsey < BaseMatcher
32
+
33
+ # @api private
34
+ # @return [String]
25
35
  def failure_message
26
36
  "expected: falsey value\n got: #{actual.inspect}"
27
37
  end
28
38
 
39
+ # @api private
40
+ # @return [String]
29
41
  def failure_message_when_negated
30
42
  "expected: truthy value\n got: #{actual.inspect}"
31
43
  end
32
- end
33
44
 
34
- class BeNil < BaseMatcher
45
+ private
46
+
35
47
  def match(_, actual)
36
- actual.nil?
48
+ !actual
37
49
  end
50
+ end
38
51
 
52
+ # @api private
53
+ # Provides the implementation for `be_nil`.
54
+ # Not intended to be instantiated directly.
55
+ class BeNil < BaseMatcher
56
+
57
+ # @api private
58
+ # @return [String]
39
59
  def failure_message
40
60
  "expected: nil\n got: #{actual.inspect}"
41
61
  end
42
62
 
63
+ # @api private
64
+ # @return [String]
43
65
  def failure_message_when_negated
44
66
  "expected: not nil\n got: nil"
45
67
  end
68
+
69
+ private
70
+
71
+ def match(_, actual)
72
+ actual.nil?
73
+ end
46
74
  end
47
75
 
76
+ # @private
48
77
  module BeHelpers
49
78
  private
50
79
 
@@ -69,6 +98,9 @@ module RSpec
69
98
  end
70
99
  end
71
100
 
101
+ # @api private
102
+ # Provides the implementation for `be`.
103
+ # Not intended to be instantiated directly.
72
104
  class Be < BaseMatcher
73
105
  include BeHelpers
74
106
 
@@ -76,14 +108,14 @@ module RSpec
76
108
  @args = args
77
109
  end
78
110
 
79
- def match(_, actual)
80
- !!actual
81
- end
82
-
111
+ # @api private
112
+ # @return [String]
83
113
  def failure_message
84
114
  "expected #{@actual.inspect} to evaluate to true"
85
115
  end
86
116
 
117
+ # @api private
118
+ # @return [String]
87
119
  def failure_message_when_negated
88
120
  "expected #{@actual.inspect} to evaluate to false"
89
121
  end
@@ -93,8 +125,17 @@ module RSpec
93
125
  BeComparedTo.new(operand, operator)
94
126
  end
95
127
  end
128
+
129
+ private
130
+
131
+ def match(_, actual)
132
+ !!actual
133
+ end
96
134
  end
97
135
 
136
+ # @api private
137
+ # Provides the implementation of `be <operator> value`.
138
+ # Not intended to be instantiated directly.
98
139
  class BeComparedTo < BaseMatcher
99
140
  include BeHelpers
100
141
 
@@ -108,10 +149,14 @@ module RSpec
108
149
  @actual.__send__ @operator, @expected
109
150
  end
110
151
 
152
+ # @api private
153
+ # @return [String]
111
154
  def failure_message
112
155
  "expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
113
156
  end
114
157
 
158
+ # @api private
159
+ # @return [String]
115
160
  def failure_message_when_negated
116
161
  message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
117
162
  if [:<, :>, :<=, :>=].include?(@operator)
@@ -121,11 +166,16 @@ module RSpec
121
166
  end
122
167
  end
123
168
 
169
+ # @api private
170
+ # @return [String]
124
171
  def description
125
172
  "be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
126
173
  end
127
174
  end
128
175
 
176
+ # @api private
177
+ # Provides the implementation of `be_<predicate>`.
178
+ # Not intended to be instantiated directly.
129
179
  class BePredicate < BaseMatcher
130
180
  include BeHelpers
131
181
 
@@ -135,56 +185,68 @@ module RSpec
135
185
  @block = block
136
186
  end
137
187
 
138
- def matches?(actual)
139
- @actual = actual
140
-
141
- if is_private_on?( @actual )
142
- raise Expectations::ExpectationNotMetError.new("expectation set on private method `#{predicate}`")
143
- end
144
-
145
- begin
146
- return @result = actual.__send__(predicate, *@args, &@block)
147
- rescue NameError => predicate_missing_error
148
- end
188
+ def matches?(actual, &block)
189
+ @actual = actual
190
+ @block ||= block
191
+ predicate_accessible? && predicate_matches?
192
+ end
149
193
 
150
- begin
151
- return @result = actual.__send__(present_tense_predicate, *@args, &@block)
152
- rescue NameError
153
- raise predicate_missing_error
154
- end
194
+ def does_not_match?(actual, &block)
195
+ @actual = actual
196
+ @block ||= block
197
+ predicate_accessible? && !predicate_matches?
155
198
  end
156
199
 
200
+ # @api private
201
+ # @return [String]
157
202
  def failure_message
158
- "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
203
+ failure_message_expecting(true)
159
204
  end
160
205
 
206
+ # @api private
207
+ # @return [String]
161
208
  def failure_message_when_negated
162
- "expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}"
209
+ failure_message_expecting(false)
163
210
  end
164
211
 
212
+ # @api private
213
+ # @return [String]
165
214
  def description
166
215
  "#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
167
216
  end
168
217
 
169
- private
218
+ private
219
+
220
+ def predicate_accessible?
221
+ !private_predicate? && predicate_exists?
222
+ end
170
223
 
171
- # support 1.8.7
172
- if methods.first.is_a? String
173
- def is_private_on? actual
174
- actual.private_methods.include? predicate.to_s
224
+ # support 1.8.7, evaluate once at load time for performance
225
+ if String === methods.first
226
+ def private_predicate?
227
+ @actual.private_methods.include? predicate.to_s
175
228
  end
176
229
  else
177
- def is_private_on? actual
178
- actual.private_methods.include? predicate
230
+ def private_predicate?
231
+ @actual.private_methods.include? predicate
179
232
  end
180
233
  end
181
234
 
235
+ def predicate_exists?
236
+ actual.respond_to?(predicate) || actual.respond_to?(present_tense_predicate)
237
+ end
238
+
239
+ def predicate_matches?
240
+ method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
241
+ @predicate_matches = actual.__send__(method_name, *@args, &@block)
242
+ end
243
+
182
244
  def predicate
183
- "#{@expected}?".to_sym
245
+ :"#{@expected}?"
184
246
  end
185
247
 
186
248
  def present_tense_predicate
187
- "#{@expected}s?".to_sym
249
+ :"#{@expected}s?"
188
250
  end
189
251
 
190
252
  def parse_expected(expected)
@@ -199,6 +261,19 @@ module RSpec
199
261
  def prefix_to_sentence
200
262
  split_words(@prefix)
201
263
  end
264
+
265
+ def failure_message_expecting(value)
266
+ validity_message ||
267
+ "expected `#{@actual.inspect}.#{predicate}#{args_to_s}` to return #{value}, got #{@predicate_matches.inspect}"
268
+ end
269
+
270
+ def validity_message
271
+ if private_predicate?
272
+ "expected #{@actual} to respond to `#{predicate}` but `#{predicate}` is a private method"
273
+ elsif !predicate_exists?
274
+ "expected #{@actual} to respond to `#{predicate}`"
275
+ end
276
+ end
202
277
  end
203
278
  end
204
279
  end