rspec-expectations 2.14.0 → 3.13.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 (155) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +976 -25
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +162 -26
  8. data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
  9. data/lib/rspec/expectations/configuration.rb +230 -0
  10. data/lib/rspec/expectations/expectation_target.rb +127 -51
  11. data/lib/rspec/expectations/fail_with.rb +17 -57
  12. data/lib/rspec/expectations/failure_aggregator.rb +229 -0
  13. data/lib/rspec/expectations/handler.rb +146 -32
  14. data/lib/rspec/expectations/minitest_integration.rb +58 -0
  15. data/lib/rspec/expectations/syntax.rb +68 -100
  16. data/lib/rspec/expectations/version.rb +1 -1
  17. data/lib/rspec/expectations.rb +58 -23
  18. data/lib/rspec/matchers/aliased_matcher.rb +116 -0
  19. data/lib/rspec/matchers/built_in/all.rb +86 -0
  20. data/lib/rspec/matchers/built_in/base_matcher.rb +191 -20
  21. data/lib/rspec/matchers/built_in/be.rb +114 -114
  22. data/lib/rspec/matchers/built_in/be_between.rb +77 -0
  23. data/lib/rspec/matchers/built_in/be_instance_of.rb +15 -4
  24. data/lib/rspec/matchers/built_in/be_kind_of.rb +10 -1
  25. data/lib/rspec/matchers/built_in/be_within.rb +35 -18
  26. data/lib/rspec/matchers/built_in/change.rb +389 -80
  27. data/lib/rspec/matchers/built_in/compound.rb +290 -0
  28. data/lib/rspec/matchers/built_in/contain_exactly.rb +310 -0
  29. data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
  30. data/lib/rspec/matchers/built_in/cover.rb +3 -0
  31. data/lib/rspec/matchers/built_in/eq.rb +30 -8
  32. data/lib/rspec/matchers/built_in/eql.rb +23 -8
  33. data/lib/rspec/matchers/built_in/equal.rb +55 -22
  34. data/lib/rspec/matchers/built_in/exist.rb +74 -10
  35. data/lib/rspec/matchers/built_in/has.rb +141 -22
  36. data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
  37. data/lib/rspec/matchers/built_in/include.rb +184 -32
  38. data/lib/rspec/matchers/built_in/match.rb +95 -1
  39. data/lib/rspec/matchers/built_in/operators.rb +128 -0
  40. data/lib/rspec/matchers/built_in/output.rb +207 -0
  41. data/lib/rspec/matchers/built_in/raise_error.rb +192 -44
  42. data/lib/rspec/matchers/built_in/respond_to.rb +154 -28
  43. data/lib/rspec/matchers/built_in/satisfy.rb +39 -9
  44. data/lib/rspec/matchers/built_in/start_or_end_with.rb +94 -0
  45. data/lib/rspec/matchers/built_in/throw_symbol.rb +58 -14
  46. data/lib/rspec/matchers/built_in/yield.rb +240 -161
  47. data/lib/rspec/matchers/built_in.rb +47 -33
  48. data/lib/rspec/matchers/composable.rb +171 -0
  49. data/lib/rspec/matchers/dsl.rb +531 -10
  50. data/lib/rspec/matchers/english_phrasing.rb +58 -0
  51. data/lib/rspec/matchers/fail_matchers.rb +42 -0
  52. data/lib/rspec/matchers/generated_descriptions.rb +14 -8
  53. data/lib/rspec/matchers/matcher_delegator.rb +61 -0
  54. data/lib/rspec/matchers/matcher_protocol.rb +105 -0
  55. data/lib/rspec/matchers/multi_matcher_diff.rb +82 -0
  56. data/lib/rspec/matchers.rb +520 -173
  57. data.tar.gz.sig +0 -0
  58. metadata +141 -242
  59. metadata.gz.sig +2 -0
  60. data/features/README.md +0 -48
  61. data/features/Upgrade.md +0 -53
  62. data/features/built_in_matchers/README.md +0 -90
  63. data/features/built_in_matchers/be.feature +0 -175
  64. data/features/built_in_matchers/be_within.feature +0 -48
  65. data/features/built_in_matchers/cover.feature +0 -47
  66. data/features/built_in_matchers/end_with.feature +0 -48
  67. data/features/built_in_matchers/equality.feature +0 -139
  68. data/features/built_in_matchers/exist.feature +0 -45
  69. data/features/built_in_matchers/expect_change.feature +0 -59
  70. data/features/built_in_matchers/expect_error.feature +0 -144
  71. data/features/built_in_matchers/have.feature +0 -109
  72. data/features/built_in_matchers/include.feature +0 -174
  73. data/features/built_in_matchers/match.feature +0 -52
  74. data/features/built_in_matchers/operators.feature +0 -227
  75. data/features/built_in_matchers/predicates.feature +0 -137
  76. data/features/built_in_matchers/respond_to.feature +0 -84
  77. data/features/built_in_matchers/satisfy.feature +0 -33
  78. data/features/built_in_matchers/start_with.feature +0 -48
  79. data/features/built_in_matchers/throw_symbol.feature +0 -91
  80. data/features/built_in_matchers/types.feature +0 -116
  81. data/features/built_in_matchers/yield.feature +0 -161
  82. data/features/custom_matchers/access_running_example.feature +0 -53
  83. data/features/custom_matchers/define_diffable_matcher.feature +0 -27
  84. data/features/custom_matchers/define_matcher.feature +0 -368
  85. data/features/custom_matchers/define_matcher_outside_rspec.feature +0 -38
  86. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +0 -24
  87. data/features/customized_message.feature +0 -22
  88. data/features/diffing.feature +0 -85
  89. data/features/implicit_docstrings.feature +0 -52
  90. data/features/step_definitions/additional_cli_steps.rb +0 -22
  91. data/features/support/env.rb +0 -14
  92. data/features/syntax_configuration.feature +0 -71
  93. data/features/test_frameworks/test_unit.feature +0 -44
  94. data/lib/rspec/expectations/deprecation.rb +0 -17
  95. data/lib/rspec/expectations/differ.rb +0 -133
  96. data/lib/rspec/expectations/errors.rb +0 -9
  97. data/lib/rspec/expectations/extensions/array.rb +0 -9
  98. data/lib/rspec/expectations/extensions/object.rb +0 -29
  99. data/lib/rspec/expectations/extensions.rb +0 -2
  100. data/lib/rspec/matchers/be_close.rb +0 -9
  101. data/lib/rspec/matchers/built_in/have.rb +0 -124
  102. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  103. data/lib/rspec/matchers/built_in/start_and_end_with.rb +0 -48
  104. data/lib/rspec/matchers/compatibility.rb +0 -14
  105. data/lib/rspec/matchers/configuration.rb +0 -108
  106. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  107. data/lib/rspec/matchers/matcher.rb +0 -300
  108. data/lib/rspec/matchers/method_missing.rb +0 -12
  109. data/lib/rspec/matchers/operator_matcher.rb +0 -109
  110. data/lib/rspec/matchers/pretty.rb +0 -70
  111. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  112. data/lib/rspec-expectations.rb +0 -1
  113. data/spec/rspec/expectations/differ_spec.rb +0 -192
  114. data/spec/rspec/expectations/expectation_target_spec.rb +0 -82
  115. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -67
  116. data/spec/rspec/expectations/fail_with_spec.rb +0 -114
  117. data/spec/rspec/expectations/handler_spec.rb +0 -227
  118. data/spec/rspec/expectations/syntax_spec.rb +0 -139
  119. data/spec/rspec/matchers/base_matcher_spec.rb +0 -62
  120. data/spec/rspec/matchers/be_close_spec.rb +0 -22
  121. data/spec/rspec/matchers/be_instance_of_spec.rb +0 -63
  122. data/spec/rspec/matchers/be_kind_of_spec.rb +0 -41
  123. data/spec/rspec/matchers/be_spec.rb +0 -516
  124. data/spec/rspec/matchers/be_within_spec.rb +0 -137
  125. data/spec/rspec/matchers/change_spec.rb +0 -553
  126. data/spec/rspec/matchers/configuration_spec.rb +0 -206
  127. data/spec/rspec/matchers/cover_spec.rb +0 -69
  128. data/spec/rspec/matchers/description_generation_spec.rb +0 -190
  129. data/spec/rspec/matchers/dsl_spec.rb +0 -57
  130. data/spec/rspec/matchers/eq_spec.rb +0 -60
  131. data/spec/rspec/matchers/eql_spec.rb +0 -41
  132. data/spec/rspec/matchers/equal_spec.rb +0 -78
  133. data/spec/rspec/matchers/exist_spec.rb +0 -124
  134. data/spec/rspec/matchers/has_spec.rb +0 -122
  135. data/spec/rspec/matchers/have_spec.rb +0 -455
  136. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  137. data/spec/rspec/matchers/include_spec.rb +0 -531
  138. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  139. data/spec/rspec/matchers/match_spec.rb +0 -61
  140. data/spec/rspec/matchers/matcher_spec.rb +0 -471
  141. data/spec/rspec/matchers/matchers_spec.rb +0 -37
  142. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  143. data/spec/rspec/matchers/operator_matcher_spec.rb +0 -223
  144. data/spec/rspec/matchers/raise_error_spec.rb +0 -485
  145. data/spec/rspec/matchers/respond_to_spec.rb +0 -292
  146. data/spec/rspec/matchers/satisfy_spec.rb +0 -44
  147. data/spec/rspec/matchers/start_with_end_with_spec.rb +0 -186
  148. data/spec/rspec/matchers/throw_symbol_spec.rb +0 -116
  149. data/spec/rspec/matchers/yield_spec.rb +0 -514
  150. data/spec/spec_helper.rb +0 -54
  151. data/spec/support/classes.rb +0 -56
  152. data/spec/support/in_sub_process.rb +0 -38
  153. data/spec/support/matchers.rb +0 -22
  154. data/spec/support/ruby_version.rb +0 -10
  155. data/spec/support/shared_examples.rb +0 -13
@@ -1,24 +1,545 @@
1
+ RSpec::Support.require_rspec_support "with_keywords_when_needed"
2
+
1
3
  module RSpec
2
4
  module Matchers
5
+ # Defines the custom matcher DSL.
3
6
  module DSL
7
+ # Defines a matcher alias. The returned matcher's `description` will be overridden
8
+ # to reflect the phrasing of the new name, which will be used in failure messages
9
+ # when passed as an argument to another matcher in a composed matcher expression.
10
+ #
11
+ # @example
12
+ # RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
13
+ # sum_to(3).description # => "sum to 3"
14
+ # a_list_that_sums_to(3).description # => "a list that sums to 3"
15
+ #
16
+ # @example
17
+ # RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
18
+ # description.sub("be sorted by", "a list sorted by")
19
+ # end
20
+ #
21
+ # be_sorted_by(:age).description # => "be sorted by age"
22
+ # a_list_sorted_by(:age).description # => "a list sorted by age"
23
+ #
24
+ # @param new_name [Symbol] the new name for the matcher
25
+ # @param old_name [Symbol] the original name for the matcher
26
+ # @param options [Hash] options for the aliased matcher
27
+ # @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
28
+ # @yield [String] optional block that, when given, is used to define the overridden
29
+ # logic. The yielded arg is the original description or failure message. If no
30
+ # block is provided, a default override is used based on the old and new names.
31
+ # @see RSpec::Matchers
32
+ def alias_matcher(new_name, old_name, options={}, &description_override)
33
+ description_override ||= lambda do |old_desc|
34
+ old_desc.gsub(EnglishPhrasing.split_words(old_name), EnglishPhrasing.split_words(new_name))
35
+ end
36
+ klass = options.fetch(:klass) { AliasedMatcher }
37
+
38
+ define_method(new_name) do |*args, &block|
39
+ matcher = __send__(old_name, *args, &block)
40
+ matcher.matcher_name = new_name if matcher.respond_to?(:matcher_name=)
41
+ klass.new(matcher, description_override)
42
+ end
43
+ ruby2_keywords new_name if respond_to?(:ruby2_keywords, true)
44
+ end
45
+
46
+ # Defines a negated matcher. The returned matcher's `description` and `failure_message`
47
+ # will be overridden to reflect the phrasing of the new name, and the match logic will
48
+ # be based on the original matcher but negated.
49
+ #
50
+ # @example
51
+ # RSpec::Matchers.define_negated_matcher :exclude, :include
52
+ # include(1, 2).description # => "include 1 and 2"
53
+ # exclude(1, 2).description # => "exclude 1 and 2"
54
+ #
55
+ # @param negated_name [Symbol] the name for the negated matcher
56
+ # @param base_name [Symbol] the name of the original matcher that will be negated
57
+ # @yield [String] optional block that, when given, is used to define the overridden
58
+ # logic. The yielded arg is the original description or failure message. If no
59
+ # block is provided, a default override is used based on the old and new names.
60
+ # @see RSpec::Matchers
61
+ def define_negated_matcher(negated_name, base_name, &description_override)
62
+ alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
63
+ end
64
+
4
65
  # Defines a custom matcher.
66
+ #
67
+ # @param name [Symbol] the name for the matcher
68
+ # @yield [Object] block that is used to define the matcher.
69
+ # The block is evaluated in the context of your custom matcher class.
70
+ # When args are passed to your matcher, they will be yielded here,
71
+ # usually representing the expected value(s).
5
72
  # @see RSpec::Matchers
6
73
  def define(name, &declarations)
7
- matcher_template = RSpec::Matchers::DSL::Matcher.new(name, &declarations)
8
- define_method name do |*expected|
9
- matcher = matcher_template.for_expected(*expected)
10
- matcher.matcher_execution_context = @matcher_execution_context ||= self
11
- matcher
74
+ warn_about_block_args(name, declarations)
75
+ define_method name do |*expected, &block_arg|
76
+ RSpec::Matchers::DSL::Matcher.new(name, declarations, self, *expected, &block_arg)
12
77
  end
13
78
  end
14
-
15
79
  alias_method :matcher, :define
16
80
 
17
- if RSpec.respond_to?(:configure)
18
- RSpec.configure {|c| c.extend self}
81
+ private
82
+
83
+ if Proc.method_defined?(:parameters)
84
+ def warn_about_block_args(name, declarations)
85
+ declarations.parameters.each do |type, arg_name|
86
+ next unless type == :block
87
+ RSpec.warning("Your `#{name}` custom matcher receives a block argument (`#{arg_name}`), " \
88
+ "but due to limitations in ruby, RSpec cannot provide the block. Instead, " \
89
+ "use the `block_arg` method to access the block")
90
+ end
91
+ end
92
+ else
93
+ # :nocov:
94
+ def warn_about_block_args(*)
95
+ # There's no way to detect block params on 1.8 since the method reflection APIs don't expose it
96
+ end
97
+ # :nocov:
98
+ end
99
+
100
+ RSpec.configure { |c| c.extend self } if RSpec.respond_to?(:configure)
101
+
102
+ # Contains the methods that are available from within the
103
+ # `RSpec::Matchers.define` DSL for creating custom matchers.
104
+ module Macros
105
+ # Stores the block that is used to determine whether this matcher passes
106
+ # or fails. The block should return a boolean value. When the matcher is
107
+ # passed to `expect(...).to` and the block returns `true`, then the expectation
108
+ # passes. Similarly, when the matcher is passed to `expect(...).not_to` and the
109
+ # block returns `false`, then the expectation passes.
110
+ #
111
+ # @example
112
+ #
113
+ # RSpec::Matchers.define :be_even do
114
+ # match do |actual|
115
+ # actual.even?
116
+ # end
117
+ # end
118
+ #
119
+ # expect(4).to be_even # passes
120
+ # expect(3).not_to be_even # passes
121
+ # expect(3).to be_even # fails
122
+ # expect(4).not_to be_even # fails
123
+ #
124
+ # By default the match block will swallow expectation errors (e.g.
125
+ # caused by using an expectation such as `expect(1).to eq 2`), if you
126
+ # wish to allow these to bubble up, pass in the option
127
+ # `:notify_expectation_failures => true`.
128
+ #
129
+ # @param [Hash] options for defining the behavior of the match block.
130
+ # @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
131
+ def match(options={}, &match_block)
132
+ define_user_override(:matches?, match_block) do |actual|
133
+ @actual = actual
134
+ RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
135
+ begin
136
+ super(*actual_arg_for(match_block))
137
+ rescue RSpec::Expectations::ExpectationNotMetError
138
+ raise if options[:notify_expectation_failures]
139
+ false
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # @private
146
+ RAISE_NOTIFIER = Proc.new { |err, _opts| raise err }
147
+
148
+ # Use this to define the block for a negative expectation (`expect(...).not_to`)
149
+ # when the positive and negative forms require different handling. This
150
+ # is rarely necessary, but can be helpful, for example, when specifying
151
+ # asynchronous processes that require different timeouts.
152
+ #
153
+ # By default the match block will swallow expectation errors (e.g.
154
+ # caused by using an expectation such as `expect(1).to eq 2`), if you
155
+ # wish to allow these to bubble up, pass in the option
156
+ # `:notify_expectation_failures => true`.
157
+ #
158
+ # @param [Hash] options for defining the behavior of the match block.
159
+ # @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
160
+ def match_when_negated(options={}, &match_block)
161
+ define_user_override(:does_not_match?, match_block) do |actual|
162
+ begin
163
+ @actual = actual
164
+ RSpec::Support.with_failure_notifier(RAISE_NOTIFIER) do
165
+ super(*actual_arg_for(match_block))
166
+ end
167
+ rescue RSpec::Expectations::ExpectationNotMetError
168
+ raise if options[:notify_expectation_failures]
169
+ false
170
+ end
171
+ end
172
+ end
173
+
174
+ # Use this instead of `match` when the block will raise an exception
175
+ # rather than returning false to indicate a failure.
176
+ #
177
+ # @example
178
+ #
179
+ # RSpec::Matchers.define :accept_as_valid do |candidate_address|
180
+ # match_unless_raises ValidationException do |validator|
181
+ # validator.validate(candidate_address)
182
+ # end
183
+ # end
184
+ #
185
+ # expect(email_validator).to accept_as_valid("person@company.com")
186
+ #
187
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
188
+ def match_unless_raises(expected_exception=Exception, &match_block)
189
+ define_user_override(:matches?, match_block) do |actual|
190
+ @actual = actual
191
+ begin
192
+ super(*actual_arg_for(match_block))
193
+ rescue expected_exception => @rescued_exception
194
+ false
195
+ else
196
+ true
197
+ end
198
+ end
199
+ end
200
+
201
+ # Customizes the failure message to use when this matcher is
202
+ # asked to positively match. Only use this when the message
203
+ # generated by default doesn't suit your needs.
204
+ #
205
+ # @example
206
+ #
207
+ # RSpec::Matchers.define :have_strength do |expected|
208
+ # match { your_match_logic }
209
+ #
210
+ # failure_message do |actual|
211
+ # "Expected strength of #{expected}, but had #{actual.strength}"
212
+ # end
213
+ # end
214
+ #
215
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
216
+ def failure_message(&definition)
217
+ define_user_override(__method__, definition)
218
+ end
219
+
220
+ # Customize the failure message to use when this matcher is asked
221
+ # to negatively match. Only use this when the message generated by
222
+ # default doesn't suit your needs.
223
+ #
224
+ # @example
225
+ #
226
+ # RSpec::Matchers.define :have_strength do |expected|
227
+ # match { your_match_logic }
228
+ #
229
+ # failure_message_when_negated do |actual|
230
+ # "Expected not to have strength of #{expected}, but did"
231
+ # end
232
+ # end
233
+ #
234
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
235
+ def failure_message_when_negated(&definition)
236
+ define_user_override(__method__, definition)
237
+ end
238
+
239
+ # Customize the description to use for one-liners. Only use this when
240
+ # the description generated by default doesn't suit your needs.
241
+ #
242
+ # @example
243
+ #
244
+ # RSpec::Matchers.define :qualify_for do |expected|
245
+ # match { your_match_logic }
246
+ #
247
+ # description do
248
+ # "qualify for #{expected}"
249
+ # end
250
+ # end
251
+ #
252
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
253
+ def description(&definition)
254
+ define_user_override(__method__, definition)
255
+ end
256
+
257
+ # Tells the matcher to diff the actual and expected values in the failure
258
+ # message.
259
+ def diffable
260
+ define_method(:diffable?) { true }
261
+ end
262
+
263
+ # Declares that the matcher can be used in a block expectation.
264
+ # Users will not be able to use your matcher in a block
265
+ # expectation without declaring this.
266
+ # (e.g. `expect { do_something }.to matcher`).
267
+ def supports_block_expectations
268
+ define_method(:supports_block_expectations?) { true }
269
+ end
270
+
271
+ # Convenience for defining methods on this matcher to create a fluent
272
+ # interface. The trick about fluent interfaces is that each method must
273
+ # return self in order to chain methods together. `chain` handles that
274
+ # for you. If the method is invoked and the
275
+ # `include_chain_clauses_in_custom_matcher_descriptions` config option
276
+ # hash been enabled, the chained method name and args will be added to the
277
+ # default description and failure message.
278
+ #
279
+ # In the common case where you just want the chained method to store some
280
+ # value(s) for later use (e.g. in `match`), you can provide one or more
281
+ # attribute names instead of a block; the chained method will store its
282
+ # arguments in instance variables with those names, and the values will
283
+ # be exposed via getters.
284
+ #
285
+ # @example
286
+ #
287
+ # RSpec::Matchers.define :have_errors_on do |key|
288
+ # chain :with do |message|
289
+ # @message = message
290
+ # end
291
+ #
292
+ # match do |actual|
293
+ # actual.errors[key] == @message
294
+ # end
295
+ # end
296
+ #
297
+ # expect(minor).to have_errors_on(:age).with("Not old enough to participate")
298
+ def chain(method_name, *attr_names, &definition)
299
+ unless block_given? ^ attr_names.any?
300
+ raise ArgumentError, "You must pass either a block or some attribute names (but not both) to `chain`."
301
+ end
302
+
303
+ definition = assign_attributes(attr_names) if attr_names.any?
304
+
305
+ define_user_override(method_name, definition) do |*args, &block|
306
+ super(*args, &block)
307
+ @chained_method_clauses.push([method_name, args])
308
+ self
309
+ end
310
+ end
311
+
312
+ def assign_attributes(attr_names)
313
+ attr_reader(*attr_names)
314
+ private(*attr_names)
315
+
316
+ lambda do |*attr_values|
317
+ attr_names.zip(attr_values) do |attr_name, attr_value|
318
+ instance_variable_set(:"@#{attr_name}", attr_value)
319
+ end
320
+ end
321
+ end
322
+
323
+ # assign_attributes isn't defined in the private section below because
324
+ # that makes MRI 1.9.2 emit a warning about private attributes.
325
+ private :assign_attributes
326
+
327
+ private
328
+
329
+ # Does the following:
330
+ #
331
+ # - Defines the named method using a user-provided block
332
+ # in @user_method_defs, which is included as an ancestor
333
+ # in the singleton class in which we eval the `define` block.
334
+ # - Defines an overridden definition for the same method
335
+ # usign the provided `our_def` block.
336
+ # - Provides a default `our_def` block for the common case
337
+ # of needing to call the user's definition with `@actual`
338
+ # as an arg, but only if their block's arity can handle it.
339
+ #
340
+ # This compiles the user block into an actual method, allowing
341
+ # them to use normal method constructs like `return`
342
+ # (e.g. for an early guard statement), while allowing us to define
343
+ # an override that can provide the wrapped handling
344
+ # (e.g. assigning `@actual`, rescueing errors, etc) and
345
+ # can `super` to the user's definition.
346
+ def define_user_override(method_name, user_def, &our_def)
347
+ @user_method_defs.__send__(:define_method, method_name, &user_def)
348
+ our_def ||= lambda { super(*actual_arg_for(user_def)) }
349
+ define_method(method_name, &our_def)
350
+ end
351
+
352
+ # Defines deprecated macro methods from RSpec 2 for backwards compatibility.
353
+ # @deprecated Use the methods from {Macros} instead.
354
+ module Deprecated
355
+ # @deprecated Use {Macros#match} instead.
356
+ def match_for_should(&definition)
357
+ RSpec.deprecate("`match_for_should`", :replacement => "`match`")
358
+ match(&definition)
359
+ end
360
+
361
+ # @deprecated Use {Macros#match_when_negated} instead.
362
+ def match_for_should_not(&definition)
363
+ RSpec.deprecate("`match_for_should_not`", :replacement => "`match_when_negated`")
364
+ match_when_negated(&definition)
365
+ end
366
+
367
+ # @deprecated Use {Macros#failure_message} instead.
368
+ def failure_message_for_should(&definition)
369
+ RSpec.deprecate("`failure_message_for_should`", :replacement => "`failure_message`")
370
+ failure_message(&definition)
371
+ end
372
+
373
+ # @deprecated Use {Macros#failure_message_when_negated} instead.
374
+ def failure_message_for_should_not(&definition)
375
+ RSpec.deprecate("`failure_message_for_should_not`", :replacement => "`failure_message_when_negated`")
376
+ failure_message_when_negated(&definition)
377
+ end
378
+ end
379
+ end
380
+
381
+ # Defines default implementations of the matcher
382
+ # protocol methods for custom matchers. You can
383
+ # override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
384
+ # from within an `RSpec::Matchers.define` block.
385
+ module DefaultImplementations
386
+ include BuiltIn::BaseMatcher::DefaultFailureMessages
387
+
388
+ # @api private
389
+ # Used internally by objects returns by `should` and `should_not`.
390
+ def diffable?
391
+ false
392
+ end
393
+
394
+ # The default description.
395
+ def description
396
+ english_name = EnglishPhrasing.split_words(name)
397
+ expected_list = EnglishPhrasing.list(expected)
398
+ "#{english_name}#{expected_list}#{chained_method_clause_sentences}"
399
+ end
400
+
401
+ # Matchers do not support block expectations by default. You
402
+ # must opt-in.
403
+ def supports_block_expectations?
404
+ false
405
+ end
406
+
407
+ def supports_value_expectations?
408
+ true
409
+ end
410
+
411
+ # Most matchers do not expect call stack jumps.
412
+ def expects_call_stack_jump?
413
+ false
414
+ end
415
+
416
+ private
417
+
418
+ def chained_method_clause_sentences
419
+ return '' unless Expectations.configuration.include_chain_clauses_in_custom_matcher_descriptions?
420
+
421
+ @chained_method_clauses.map do |(method_name, method_args)|
422
+ english_name = EnglishPhrasing.split_words(method_name)
423
+ arg_list = EnglishPhrasing.list(method_args)
424
+ " #{english_name}#{arg_list}"
425
+ end.join
426
+ end
427
+ end
428
+
429
+ # The class used for custom matchers. The block passed to
430
+ # `RSpec::Matchers.define` will be evaluated in the context
431
+ # of the singleton class of an instance, and will have the
432
+ # {RSpec::Matchers::DSL::Macros Macros} methods available.
433
+ class Matcher
434
+ # Provides default implementations for the matcher protocol methods.
435
+ include DefaultImplementations
436
+
437
+ # Allows expectation expressions to be used in the match block.
438
+ include RSpec::Matchers
439
+
440
+ # Supports the matcher composability features of RSpec 3+.
441
+ include Composable
442
+
443
+ # Makes the macro methods available to an `RSpec::Matchers.define` block.
444
+ extend Macros
445
+ extend Macros::Deprecated
446
+
447
+ # Exposes the value being matched against -- generally the object
448
+ # object wrapped by `expect`.
449
+ attr_reader :actual
450
+
451
+ # Exposes the exception raised during the matching by `match_unless_raises`.
452
+ # Could be useful to extract details for a failure message.
453
+ attr_reader :rescued_exception
454
+
455
+ # The block parameter used in the expectation
456
+ attr_reader :block_arg
457
+
458
+ # The name of the matcher.
459
+ attr_reader :name
460
+
461
+ # @api private
462
+ def initialize(name, declarations, matcher_execution_context, *expected, &block_arg)
463
+ @name = name
464
+ @actual = nil
465
+ @expected_as_array = expected
466
+ @matcher_execution_context = matcher_execution_context
467
+ @chained_method_clauses = []
468
+ @block_arg = block_arg
469
+
470
+ klass = class << self
471
+ # See `Macros#define_user_override` above, for an explanation.
472
+ include(@user_method_defs = Module.new)
473
+ self
474
+ end
475
+ RSpec::Support::WithKeywordsWhenNeeded.class_exec(klass, *expected, &declarations)
476
+ end
477
+
478
+ # Provides the expected value. This will return an array if
479
+ # multiple arguments were passed to the matcher; otherwise it
480
+ # will return a single value.
481
+ # @see #expected_as_array
482
+ def expected
483
+ if expected_as_array.size == 1
484
+ expected_as_array[0]
485
+ else
486
+ expected_as_array
487
+ end
488
+ end
489
+
490
+ # Returns the expected value as an an array. This exists primarily
491
+ # to aid in upgrading from RSpec 2.x, since in RSpec 2, `expected`
492
+ # always returned an array.
493
+ # @see #expected
494
+ attr_reader :expected_as_array
495
+
496
+ # Adds the name (rather than a cryptic hex number)
497
+ # so we can identify an instance of
498
+ # the matcher in error messages (e.g. for `NoMethodError`)
499
+ def inspect
500
+ "#<#{self.class.name} #{name}>"
501
+ end
502
+
503
+ if RUBY_VERSION.to_f >= 1.9
504
+ # Indicates that this matcher responds to messages
505
+ # from the `@matcher_execution_context` as well.
506
+ # Also, supports getting a method object for such methods.
507
+ def respond_to_missing?(method, include_private=false)
508
+ super || @matcher_execution_context.respond_to?(method, include_private)
509
+ end
510
+ else # for 1.8.7
511
+ # :nocov:
512
+ # Indicates that this matcher responds to messages
513
+ # from the `@matcher_execution_context` as well.
514
+ def respond_to?(method, include_private=false)
515
+ super || @matcher_execution_context.respond_to?(method, include_private)
516
+ end
517
+ # :nocov:
518
+ end
519
+
520
+ private
521
+
522
+ def actual_arg_for(block)
523
+ block.arity.zero? ? [] : [@actual]
524
+ end
525
+
526
+ # Takes care of forwarding unhandled messages to the
527
+ # `@matcher_execution_context` (typically the current
528
+ # running `RSpec::Core::Example`). This is needed by
529
+ # rspec-rails so that it can define matchers that wrap
530
+ # Rails' test helper methods, but it's also a useful
531
+ # feature in its own right.
532
+ def method_missing(method, *args, &block)
533
+ if @matcher_execution_context.respond_to?(method)
534
+ @matcher_execution_context.__send__ method, *args, &block
535
+ else
536
+ super(method, *args, &block)
537
+ end
538
+ end
539
+ # The method_missing method should be refactored to pass kw args in RSpec 4
540
+ # then this can be removed
541
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
19
542
  end
20
543
  end
21
544
  end
22
545
  end
23
-
24
- RSpec::Matchers.extend RSpec::Matchers::DSL
@@ -0,0 +1,58 @@
1
+ module RSpec
2
+ module Matchers
3
+ # Facilitates converting ruby objects to English phrases.
4
+ module EnglishPhrasing
5
+ # Converts a symbol into an English expression.
6
+ #
7
+ # split_words(:banana_creme_pie) #=> "banana creme pie"
8
+ #
9
+ def self.split_words(sym)
10
+ sym.to_s.tr('_', ' ')
11
+ end
12
+
13
+ # @note The returned string has a leading space except
14
+ # when given an empty list.
15
+ #
16
+ # Converts an object (often a collection of objects)
17
+ # into an English list.
18
+ #
19
+ # list(['banana', 'kiwi', 'mango'])
20
+ # #=> " \"banana\", \"kiwi\", and \"mango\""
21
+ #
22
+ # Given an empty collection, returns the empty string.
23
+ #
24
+ # list([]) #=> ""
25
+ #
26
+ def self.list(obj)
27
+ return " #{RSpec::Support::ObjectFormatter.format(obj)}" if !obj || Struct === obj || Hash === obj
28
+ items = Array(obj).map { |w| RSpec::Support::ObjectFormatter.format(w) }
29
+ case items.length
30
+ when 0
31
+ ""
32
+ when 1
33
+ " #{items[0]}"
34
+ when 2
35
+ " #{items[0]} and #{items[1]}"
36
+ else
37
+ " #{items[0...-1].join(', ')}, and #{items[-1]}"
38
+ end
39
+ end
40
+
41
+ if RUBY_VERSION == '1.8.7'
42
+ # Not sure why, but on travis on 1.8.7 we have gotten these warnings:
43
+ # lib/rspec/matchers/english_phrasing.rb:28: warning: default `to_a' will be obsolete
44
+ # So it appears that `Array` can trigger that (e.g. by calling `to_a` on the passed object?)
45
+ # So here we replace `Kernel#Array` with our own warning-free implementation for 1.8.7.
46
+ # @private
47
+ # rubocop:disable Naming/MethodName
48
+ def self.Array(obj)
49
+ case obj
50
+ when Array then obj
51
+ else [obj]
52
+ end
53
+ end
54
+ # rubocop:enable Naming/MethodName
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ require 'rspec/expectations'
2
+
3
+ module RSpec
4
+ module Matchers
5
+ # Matchers for testing RSpec matchers. Include them with:
6
+ #
7
+ # require 'rspec/matchers/fail_matchers'
8
+ # RSpec.configure do |config|
9
+ # config.include RSpec::Matchers::FailMatchers
10
+ # end
11
+ #
12
+ module FailMatchers
13
+ # Matches if an expectation fails
14
+ #
15
+ # @example
16
+ # expect { some_expectation }.to fail
17
+ def fail(&block)
18
+ raise_error(RSpec::Expectations::ExpectationNotMetError, &block)
19
+ end
20
+
21
+ # Matches if an expectation fails with the provided message
22
+ #
23
+ # @example
24
+ # expect { some_expectation }.to fail_with("some failure message")
25
+ # expect { some_expectation }.to fail_with(/some failure message/)
26
+ def fail_with(message)
27
+ raise_error(RSpec::Expectations::ExpectationNotMetError, message)
28
+ end
29
+
30
+ # Matches if an expectation fails including the provided message
31
+ #
32
+ # @example
33
+ # expect { some_expectation }.to fail_including("portion of some failure message")
34
+ def fail_including(*snippets)
35
+ raise_error(
36
+ RSpec::Expectations::ExpectationNotMetError,
37
+ a_string_including(*snippets)
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end