rspec-expectations 2.13.0 → 3.0.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 (142) hide show
  1. checksums.yaml +15 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/Changelog.md +413 -23
  4. data/License.txt +1 -0
  5. data/README.md +109 -80
  6. data/lib/rspec/{matchers → expectations}/configuration.rb +44 -14
  7. data/lib/rspec/expectations/expectation_target.rb +71 -38
  8. data/lib/rspec/expectations/fail_with.rb +12 -44
  9. data/lib/rspec/expectations/handler.rb +135 -35
  10. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  11. data/lib/rspec/expectations/syntax.rb +60 -94
  12. data/lib/rspec/expectations/version.rb +1 -1
  13. data/lib/rspec/expectations.rb +46 -24
  14. data/lib/rspec/matchers/aliased_matcher.rb +41 -0
  15. data/lib/rspec/matchers/built_in/all.rb +78 -0
  16. data/lib/rspec/matchers/built_in/base_matcher.rb +63 -15
  17. data/lib/rspec/matchers/built_in/be.rb +170 -79
  18. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  19. data/lib/rspec/matchers/built_in/be_instance_of.rb +11 -3
  20. data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -0
  21. data/lib/rspec/matchers/built_in/be_within.rb +40 -16
  22. data/lib/rspec/matchers/built_in/change.rb +275 -74
  23. data/lib/rspec/matchers/built_in/compound.rb +122 -0
  24. data/lib/rspec/matchers/built_in/contain_exactly.rb +256 -0
  25. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  26. data/lib/rspec/matchers/built_in/eq.rb +60 -6
  27. data/lib/rspec/matchers/built_in/eql.rb +17 -5
  28. data/lib/rspec/matchers/built_in/equal.rb +55 -21
  29. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  30. data/lib/rspec/matchers/built_in/has.rb +71 -15
  31. data/lib/rspec/matchers/built_in/include.rb +58 -29
  32. data/lib/rspec/matchers/built_in/match.rb +19 -1
  33. data/lib/rspec/matchers/built_in/operators.rb +119 -0
  34. data/lib/rspec/matchers/built_in/output.rb +150 -0
  35. data/lib/rspec/matchers/built_in/raise_error.rb +91 -33
  36. data/lib/rspec/matchers/built_in/respond_to.rb +42 -20
  37. data/lib/rspec/matchers/built_in/satisfy.rb +19 -3
  38. data/lib/rspec/matchers/built_in/start_and_end_with.rb +54 -18
  39. data/lib/rspec/matchers/built_in/throw_symbol.rb +38 -8
  40. data/lib/rspec/matchers/built_in/yield.rb +237 -55
  41. data/lib/rspec/matchers/built_in.rb +45 -33
  42. data/lib/rspec/matchers/composable.rb +179 -0
  43. data/lib/rspec/matchers/dsl.rb +362 -5
  44. data/lib/rspec/matchers/generated_descriptions.rb +13 -6
  45. data/lib/rspec/matchers/matcher_delegator.rb +37 -0
  46. data/lib/rspec/matchers/pretty.rb +33 -20
  47. data/lib/rspec/matchers.rb +450 -227
  48. data.tar.gz.sig +1 -0
  49. metadata +100 -215
  50. metadata.gz.sig +0 -0
  51. data/features/README.md +0 -49
  52. data/features/Upgrade.md +0 -53
  53. data/features/built_in_matchers/README.md +0 -90
  54. data/features/built_in_matchers/be.feature +0 -175
  55. data/features/built_in_matchers/be_within.feature +0 -48
  56. data/features/built_in_matchers/cover.feature +0 -47
  57. data/features/built_in_matchers/end_with.feature +0 -48
  58. data/features/built_in_matchers/equality.feature +0 -139
  59. data/features/built_in_matchers/exist.feature +0 -45
  60. data/features/built_in_matchers/expect_change.feature +0 -59
  61. data/features/built_in_matchers/expect_error.feature +0 -144
  62. data/features/built_in_matchers/have.feature +0 -109
  63. data/features/built_in_matchers/include.feature +0 -174
  64. data/features/built_in_matchers/match.feature +0 -52
  65. data/features/built_in_matchers/operators.feature +0 -227
  66. data/features/built_in_matchers/predicates.feature +0 -137
  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 -146
  73. data/features/custom_matchers/access_running_example.feature +0 -53
  74. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  75. data/features/custom_matchers/define_matcher.feature +0 -368
  76. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  77. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  78. data/features/customized_message.feature +0 -22
  79. data/features/diffing.feature +0 -85
  80. data/features/implicit_docstrings.feature +0 -52
  81. data/features/step_definitions/additional_cli_steps.rb +0 -22
  82. data/features/support/env.rb +0 -5
  83. data/features/syntax_configuration.feature +0 -68
  84. data/features/test_frameworks/test_unit.feature +0 -46
  85. data/lib/rspec/expectations/deprecation.rb +0 -38
  86. data/lib/rspec/expectations/differ.rb +0 -115
  87. data/lib/rspec/expectations/errors.rb +0 -9
  88. data/lib/rspec/expectations/extensions/array.rb +0 -9
  89. data/lib/rspec/expectations/extensions/object.rb +0 -39
  90. data/lib/rspec/expectations/extensions.rb +0 -2
  91. data/lib/rspec/matchers/be_close.rb +0 -9
  92. data/lib/rspec/matchers/built_in/have.rb +0 -108
  93. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  94. data/lib/rspec/matchers/compatibility.rb +0 -14
  95. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  96. data/lib/rspec/matchers/matcher.rb +0 -300
  97. data/lib/rspec/matchers/method_missing.rb +0 -12
  98. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  99. data/lib/rspec-expectations.rb +0 -1
  100. data/spec/rspec/expectations/differ_spec.rb +0 -170
  101. data/spec/rspec/expectations/expectation_target_spec.rb +0 -75
  102. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  103. data/spec/rspec/expectations/fail_with_spec.rb +0 -95
  104. data/spec/rspec/expectations/handler_spec.rb +0 -206
  105. data/spec/rspec/expectations/syntax_spec.rb +0 -97
  106. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  107. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  108. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  109. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  110. data/spec/rspec/matchers/be_spec.rb +0 -516
  111. data/spec/rspec/matchers/be_within_spec.rb +0 -129
  112. data/spec/rspec/matchers/change_spec.rb +0 -542
  113. data/spec/rspec/matchers/configuration_spec.rb +0 -238
  114. data/spec/rspec/matchers/cover_spec.rb +0 -69
  115. data/spec/rspec/matchers/description_generation_spec.rb +0 -176
  116. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  117. data/spec/rspec/matchers/eq_spec.rb +0 -60
  118. data/spec/rspec/matchers/eql_spec.rb +0 -41
  119. data/spec/rspec/matchers/equal_spec.rb +0 -78
  120. data/spec/rspec/matchers/exist_spec.rb +0 -124
  121. data/spec/rspec/matchers/has_spec.rb +0 -122
  122. data/spec/rspec/matchers/have_spec.rb +0 -445
  123. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  124. data/spec/rspec/matchers/include_spec.rb +0 -531
  125. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  126. data/spec/rspec/matchers/match_spec.rb +0 -61
  127. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  128. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  129. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  130. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  131. data/spec/rspec/matchers/raise_error_spec.rb +0 -395
  132. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  133. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  134. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  135. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  136. data/spec/rspec/matchers/yield_spec.rb +0 -437
  137. data/spec/spec_helper.rb +0 -54
  138. data/spec/support/classes.rb +0 -56
  139. data/spec/support/in_sub_process.rb +0 -38
  140. data/spec/support/matchers.rb +0 -22
  141. data/spec/support/ruby_version.rb +0 -10
  142. data/spec/support/shared_examples.rb +0 -13
@@ -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
  #
@@ -13,18 +13,35 @@ module RSpec
13
13
  # class. If/when this changes, we will announce it and remove this warning.
14
14
  class BaseMatcher
15
15
  include RSpec::Matchers::Pretty
16
+ include RSpec::Matchers::Composable
16
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.
21
+ UNDEFINED = Object.new.freeze
22
+
23
+ # @private
17
24
  attr_reader :actual, :expected, :rescued_exception
18
25
 
19
- def initialize(expected = nil)
20
- @expected = expected
26
+ def initialize(expected = UNDEFINED)
27
+ @expected = expected unless UNDEFINED.equal?(expected)
21
28
  end
22
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.
23
34
  def matches?(actual)
24
35
  @actual = actual
25
36
  match(expected, actual)
26
37
  end
27
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.
28
45
  def match_unless_raises(*exceptions)
29
46
  exceptions.unshift Exception if exceptions.empty?
30
47
  begin
@@ -35,32 +52,63 @@ module RSpec
35
52
  end
36
53
  end
37
54
 
38
- def failure_message_for_should
39
- assert_ivars :@actual, :@expected
40
- "expected #{@actual.inspect} to #{name_to_sentence}#{expected_to_sentence}"
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]
60
+ def failure_message
61
+ assert_ivars :@actual
62
+ "expected #{@actual.inspect} to #{description}"
41
63
  end
42
64
 
43
- def failure_message_for_should_not
44
- assert_ivars :@actual, :@expected
45
- "expected #{@actual.inspect} not to #{name_to_sentence}#{expected_to_sentence}"
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]
70
+ def failure_message_when_negated
71
+ assert_ivars :@actual
72
+ "expected #{@actual.inspect} not to #{description}"
46
73
  end
47
74
 
75
+ # @api private
76
+ # Generates a "pretty" description using the logic in {Pretty}.
77
+ # @return [String]
48
78
  def description
49
- expected ? "#{name_to_sentence} #{@expected.inspect}" : name_to_sentence
79
+ return name_to_sentence unless defined?(@expected)
80
+ "#{name_to_sentence}#{to_sentence @expected}"
50
81
  end
51
82
 
83
+ # @api private
84
+ # Matchers are not diffable by default. Override this to make your
85
+ # subclass diffable.
52
86
  def diffable?
53
87
  false
54
88
  end
55
89
 
56
- def ==(other)
57
- matches?(other)
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
58
96
  end
59
97
 
60
- private
98
+ private
99
+
100
+ def assert_ivars(*expected_ivars)
101
+ if (expected_ivars - present_ivars).any?
102
+ raise "#{self.class.name} needs to supply#{to_sentence expected_ivars}"
103
+ end
104
+ end
61
105
 
62
- def assert_ivars *ivars
63
- raise "#{self.class.name} needs to supply #{to_sentence ivars}" unless ivars.all? { |v| instance_variables.map(&:intern).include? v }
106
+ if RUBY_VERSION.to_f < 1.9
107
+ def present_ivars
108
+ instance_variables.map { |v| v.to_sym }
109
+ end
110
+ else
111
+ alias present_ivars instance_variables
64
112
  end
65
113
  end
66
114
  end
@@ -1,73 +1,80 @@
1
- require 'rspec/matchers/dsl'
2
-
3
1
  module RSpec
4
2
  module Matchers
5
3
  module BuiltIn
6
- class BeTrue < BaseMatcher
7
- def match(_, actual)
8
- !!actual
4
+ # @api private
5
+ # Provides the implementation for `be_truthy`.
6
+ # Not intended to be instantiated directly.
7
+ class BeTruthy < BaseMatcher
8
+
9
+ # @api private
10
+ # @return [String]
11
+ def failure_message
12
+ "expected: truthy value\n got: #{actual.inspect}"
9
13
  end
10
14
 
11
- def failure_message_for_should
12
- "expected: true value\n got: #{actual.inspect}"
15
+ # @api private
16
+ # @return [String]
17
+ def failure_message_when_negated
18
+ "expected: falsey value\n got: #{actual.inspect}"
13
19
  end
14
20
 
15
- def failure_message_for_should_not
16
- "expected: non-true value\n got: #{actual.inspect}"
21
+ private
22
+
23
+ def match(_, actual)
24
+ !!actual
17
25
  end
18
26
  end
19
27
 
20
- class BeFalse < BaseMatcher
21
- def match(_, actual)
22
- !actual
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]
35
+ def failure_message
36
+ "expected: falsey value\n got: #{actual.inspect}"
23
37
  end
24
38
 
25
- def failure_message_for_should
26
- "expected: false value\n got: #{actual.inspect}"
39
+ # @api private
40
+ # @return [String]
41
+ def failure_message_when_negated
42
+ "expected: truthy value\n got: #{actual.inspect}"
27
43
  end
28
44
 
29
- def failure_message_for_should_not
30
- "expected: non-false value\n got: #{actual.inspect}"
45
+ private
46
+
47
+ def match(_, actual)
48
+ !actual
31
49
  end
32
50
  end
33
51
 
52
+ # @api private
53
+ # Provides the implementation for `be_nil`.
54
+ # Not intended to be instantiated directly.
34
55
  class BeNil < BaseMatcher
35
- def match(_, actual)
36
- actual.nil?
37
- end
38
56
 
39
- def failure_message_for_should
57
+ # @api private
58
+ # @return [String]
59
+ def failure_message
40
60
  "expected: nil\n got: #{actual.inspect}"
41
61
  end
42
62
 
43
- def failure_message_for_should_not
63
+ # @api private
64
+ # @return [String]
65
+ def failure_message_when_negated
44
66
  "expected: not nil\n got: nil"
45
67
  end
46
- end
47
68
 
48
- class Be < BaseMatcher
49
- def initialize(*args, &block)
50
- @args = args
51
- end
69
+ private
52
70
 
53
71
  def match(_, actual)
54
- !!actual
55
- end
56
-
57
- def failure_message_for_should
58
- "expected #{@actual.inspect} to evaluate to true"
59
- end
60
-
61
- def failure_message_for_should_not
62
- "expected #{@actual.inspect} to evaluate to false"
63
- end
64
-
65
- [:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
66
- define_method operator do |operand|
67
- BeComparedTo.new(operand, operator)
68
- end
72
+ actual.nil?
69
73
  end
74
+ end
70
75
 
76
+ # @private
77
+ module BeHelpers
71
78
  private
72
79
 
73
80
  def args_to_s
@@ -91,10 +98,50 @@ module RSpec
91
98
  end
92
99
  end
93
100
 
94
- class BeComparedTo < Be
101
+ # @api private
102
+ # Provides the implementation for `be`.
103
+ # Not intended to be instantiated directly.
104
+ class Be < BaseMatcher
105
+ include BeHelpers
106
+
107
+ def initialize(*args, &block)
108
+ @args = args
109
+ end
110
+
111
+ # @api private
112
+ # @return [String]
113
+ def failure_message
114
+ "expected #{@actual.inspect} to evaluate to true"
115
+ end
116
+
117
+ # @api private
118
+ # @return [String]
119
+ def failure_message_when_negated
120
+ "expected #{@actual.inspect} to evaluate to false"
121
+ end
122
+
123
+ [:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
124
+ define_method operator do |operand|
125
+ BeComparedTo.new(operand, operator)
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def match(_, actual)
132
+ !!actual
133
+ end
134
+ end
135
+
136
+ # @api private
137
+ # Provides the implementation of `be <operator> value`.
138
+ # Not intended to be instantiated directly.
139
+ class BeComparedTo < BaseMatcher
140
+ include BeHelpers
141
+
95
142
  def initialize(operand, operator)
96
143
  @expected, @operator = operand, operator
97
- @args = []
144
+ @args = []
98
145
  end
99
146
 
100
147
  def matches?(actual)
@@ -102,72 +149,104 @@ module RSpec
102
149
  @actual.__send__ @operator, @expected
103
150
  end
104
151
 
105
- def failure_message_for_should
152
+ # @api private
153
+ # @return [String]
154
+ def failure_message
106
155
  "expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
107
156
  end
108
157
 
109
- def failure_message_for_should_not
110
- message = <<-MESSAGE
111
- `#{negative_expectation_expression}` not only FAILED,
112
- it is a bit confusing.
113
- MESSAGE
114
-
115
- raise message << ([:===,:==].include?(@operator) ?
116
- "It might be more clearly expressed without the \"be\"?" :
117
- "It might be more clearly expressed in the positive?")
158
+ # @api private
159
+ # @return [String]
160
+ def failure_message_when_negated
161
+ message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
162
+ if [:<, :>, :<=, :>=].include?(@operator)
163
+ message + " not only FAILED, it is a bit confusing."
164
+ else
165
+ message
166
+ end
118
167
  end
119
168
 
169
+ # @api private
170
+ # @return [String]
120
171
  def description
121
172
  "be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
122
173
  end
123
-
124
- def negative_expectation_expression
125
- Expectations::Syntax.negative_expression("actual", "be #{@operator} #{@expected}")
126
- end
127
174
  end
128
175
 
129
- class BePredicate < Be
176
+ # @api private
177
+ # Provides the implementation of `be_<predicate>`.
178
+ # Not intended to be instantiated directly.
179
+ class BePredicate < BaseMatcher
180
+ include BeHelpers
181
+
130
182
  def initialize(*args, &block)
131
183
  @expected = parse_expected(args.shift)
132
184
  @args = args
133
185
  @block = block
134
186
  end
135
187
 
136
- def matches?(actual)
137
- @actual = actual
138
- begin
139
- return @result = actual.__send__(predicate, *@args, &@block)
140
- rescue NameError => predicate_missing_error
141
- "this needs to be here or rcov will not count this branch even though it's executed in a code example"
142
- end
188
+ def matches?(actual, &block)
189
+ @actual = actual
190
+ @block ||= block
191
+ predicate_accessible? && predicate_matches?
192
+ end
143
193
 
144
- begin
145
- return @result = actual.__send__(present_tense_predicate, *@args, &@block)
146
- rescue NameError
147
- raise predicate_missing_error
148
- end
194
+ def does_not_match?(actual, &block)
195
+ @actual = actual
196
+ @block ||= block
197
+ predicate_accessible? && !predicate_matches?
149
198
  end
150
199
 
151
- def failure_message_for_should
152
- "expected #{predicate}#{args_to_s} to return true, got #{@result.inspect}"
200
+ # @api private
201
+ # @return [String]
202
+ def failure_message
203
+ failure_message_expecting(true)
153
204
  end
154
205
 
155
- def failure_message_for_should_not
156
- "expected #{predicate}#{args_to_s} to return false, got #{@result.inspect}"
206
+ # @api private
207
+ # @return [String]
208
+ def failure_message_when_negated
209
+ failure_message_expecting(false)
157
210
  end
158
211
 
212
+ # @api private
213
+ # @return [String]
159
214
  def description
160
215
  "#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
161
216
  end
162
217
 
163
- private
218
+ private
219
+
220
+ def predicate_accessible?
221
+ !private_predicate? && predicate_exists?
222
+ end
223
+
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
228
+ end
229
+ else
230
+ def private_predicate?
231
+ @actual.private_methods.include? predicate
232
+ end
233
+ end
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
164
243
 
165
244
  def predicate
166
- "#{@expected}?".to_sym
245
+ :"#{@expected}?"
167
246
  end
168
247
 
169
248
  def present_tense_predicate
170
- "#{@expected}s?".to_sym
249
+ :"#{@expected}s?"
171
250
  end
172
251
 
173
252
  def parse_expected(expected)
@@ -176,13 +255,25 @@ it is a bit confusing.
176
255
  end
177
256
 
178
257
  def prefix_and_expected(symbol)
179
- symbol.to_s =~ /^(be_(an?_)?)(.*)/
180
- return $1, $3
258
+ Matchers::BE_PREDICATE_REGEX.match(symbol.to_s).captures.compact
181
259
  end
182
260
 
183
261
  def prefix_to_sentence
184
262
  split_words(@prefix)
185
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
186
277
  end
187
278
  end
188
279
  end
@@ -0,0 +1,77 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_between`.
6
+ # Not intended to be instantiated directly.
7
+ class BeBetween < BaseMatcher
8
+ def initialize(min, max)
9
+ @min, @max = min, max
10
+ inclusive
11
+ end
12
+
13
+ # @api public
14
+ # Makes the between comparison inclusive.
15
+ #
16
+ # @example
17
+ # expect(3).to be_between(2, 3).inclusive
18
+ #
19
+ # @note The matcher is inclusive by default; this simply provides
20
+ # a way to be more explicit about it.
21
+ def inclusive
22
+ @less_than_operator = :<=
23
+ @greater_than_operator = :>=
24
+ @mode = :inclusive
25
+ self
26
+ end
27
+
28
+ # @api public
29
+ # Makes the between comparison exclusive.
30
+ #
31
+ # @example
32
+ # expect(3).to be_between(2, 4).exclusive
33
+ def exclusive
34
+ @less_than_operator = :<
35
+ @greater_than_operator = :>
36
+ @mode = :exclusive
37
+ self
38
+ end
39
+
40
+ # @api private
41
+ # @return [Boolean]
42
+ def matches?(actual)
43
+ @actual = actual
44
+ comparable? && compare
45
+ rescue ArgumentError
46
+ false
47
+ end
48
+
49
+ # @api private
50
+ # @return [String]
51
+ def failure_message
52
+ "#{super}#{not_comparable_clause}"
53
+ end
54
+
55
+ # @api private
56
+ # @return [String]
57
+ def description
58
+ "be between #{@min.inspect} and #{@max.inspect} (#{@mode})"
59
+ end
60
+
61
+ private
62
+
63
+ def comparable?
64
+ @actual.respond_to?(@less_than_operator) && @actual.respond_to?(@greater_than_operator)
65
+ end
66
+
67
+ def not_comparable_clause
68
+ ", but it does not respond to `#{@less_than_operator}` and `#{@greater_than_operator}`" unless comparable?
69
+ end
70
+
71
+ def compare
72
+ @actual.__send__(@greater_than_operator, @min) && @actual.__send__(@less_than_operator, @max)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,14 +1,22 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_an_instance_of`.
6
+ # Not intended to be instantiated directly.
4
7
  class BeAnInstanceOf < BaseMatcher
5
- def match(expected, actual)
6
- actual.instance_of? expected
7
- end
8
8
 
9
+ # @api private
10
+ # @return [String]
9
11
  def description
10
12
  "be an instance of #{expected}"
11
13
  end
14
+
15
+ private
16
+
17
+ def match(expected, actual)
18
+ actual.instance_of? expected
19
+ end
12
20
  end
13
21
  end
14
22
  end
@@ -1,7 +1,12 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_a_kind_of`.
6
+ # Not intended to be instantiated directly.
4
7
  class BeAKindOf < BaseMatcher
8
+
9
+ private
5
10
  def match(expected, actual)
6
11
  actual.kind_of? expected
7
12
  end
@@ -1,19 +1,18 @@
1
1
  module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
+ # @api private
5
+ # Provides the implementation for `be_within`.
6
+ # Not intended to be instantiated directly.
4
7
  class BeWithin
8
+ include Composable
9
+
5
10
  def initialize(delta)
6
11
  @delta = delta
7
12
  end
8
13
 
9
- def matches?(actual)
10
- @actual = actual
11
- raise needs_expected unless defined? @expected
12
- raise needs_subtractable unless @actual.respond_to? :-
13
- (@actual - @expected).abs <= @tolerance
14
- end
15
- alias == matches?
16
-
14
+ # @api public
15
+ # Sets the expected value.
17
16
  def of(expected)
18
17
  @expected = expected
19
18
  @tolerance = @delta
@@ -21,34 +20,59 @@ module RSpec
21
20
  self
22
21
  end
23
22
 
23
+ # @api public
24
+ # Sets the expected value, and makes the matcher do
25
+ # a percent comparison.
24
26
  def percent_of(expected)
25
27
  @expected = expected
26
- @tolerance = @delta * @expected / 100
28
+ @tolerance = @delta * @expected.abs / 100.0
27
29
  @unit = '%'
28
30
  self
29
31
  end
30
32
 
31
- def failure_message_for_should
32
- "expected #{@actual} to #{description}"
33
+ # @private
34
+ def matches?(actual)
35
+ @actual = actual
36
+ raise needs_expected unless defined? @expected
37
+ numeric? && (@actual - @expected).abs <= @tolerance
33
38
  end
34
39
 
35
- def failure_message_for_should_not
36
- "expected #{@actual} not to #{description}"
40
+ # @api private
41
+ # @return [String]
42
+ def failure_message
43
+ "expected #{@actual.inspect} to #{description}#{not_numeric_clause}"
37
44
  end
38
45
 
46
+ # @api private
47
+ # @return [String]
48
+ def failure_message_when_negated
49
+ "expected #{@actual.inspect} not to #{description}"
50
+ end
51
+
52
+ # @api private
53
+ # @return [String]
39
54
  def description
40
55
  "be within #{@delta}#{@unit} of #{@expected}"
41
56
  end
42
57
 
43
- private
58
+ # @private
59
+ def supports_block_expectations?
60
+ false
61
+ end
62
+
63
+ private
44
64
 
45
- def needs_subtractable
46
- ArgumentError.new "The actual value (#{@actual.inspect}) must respond to `-`"
65
+ def numeric?
66
+ @actual.respond_to?(:-)
47
67
  end
48
68
 
49
69
  def needs_expected
50
70
  ArgumentError.new "You must set an expected value using #of: be_within(#{@delta}).of(expected_value)"
51
71
  end
72
+
73
+ def not_numeric_clause
74
+ ", but it could not be treated as a numeric value" unless numeric?
75
+ end
52
76
  end
53
77
  end
54
78
  end