rspec-expectations 2.99.2 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +14 -6
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/Changelog.md +63 -104
  5. data/License.txt +1 -0
  6. data/README.md +14 -8
  7. data/features/README.md +1 -2
  8. data/features/built_in_matchers/README.md +3 -0
  9. data/features/built_in_matchers/be.feature +44 -44
  10. data/features/built_in_matchers/be_within.feature +1 -1
  11. data/features/built_in_matchers/comparisons.feature +97 -0
  12. data/features/built_in_matchers/cover.feature +3 -3
  13. data/features/built_in_matchers/end_with.feature +3 -3
  14. data/features/built_in_matchers/equality.feature +20 -23
  15. data/features/built_in_matchers/exist.feature +5 -5
  16. data/features/built_in_matchers/expect_error.feature +14 -14
  17. data/features/built_in_matchers/include.feature +15 -15
  18. data/features/built_in_matchers/match.feature +4 -5
  19. data/features/built_in_matchers/match_array.feature +37 -0
  20. data/features/built_in_matchers/predicates.feature +30 -6
  21. data/features/built_in_matchers/respond_to.feature +4 -4
  22. data/features/built_in_matchers/satisfy.feature +2 -2
  23. data/features/built_in_matchers/start_with.feature +3 -3
  24. data/features/built_in_matchers/types.feature +6 -6
  25. data/features/custom_matchers/access_running_example.feature +3 -3
  26. data/features/custom_matchers/define_matcher.feature +6 -34
  27. data/features/custom_matchers/define_matcher_outside_rspec.feature +2 -2
  28. data/features/custom_matchers/define_matcher_with_fluent_interface.feature +1 -1
  29. data/features/customized_message.feature +18 -1
  30. data/features/diffing.feature +3 -3
  31. data/features/implicit_docstrings.feature +9 -9
  32. data/features/step_definitions/additional_cli_steps.rb +0 -10
  33. data/features/support/env.rb +10 -3
  34. data/features/test_frameworks/test_unit.feature +0 -40
  35. data/lib/rspec-expectations.rb +0 -5
  36. data/lib/rspec/expectations.rb +4 -18
  37. data/lib/rspec/expectations/expectation_target.rb +10 -77
  38. data/lib/rspec/expectations/extensions.rb +0 -1
  39. data/lib/rspec/expectations/handler.rb +1 -5
  40. data/lib/rspec/expectations/syntax.rb +25 -5
  41. data/lib/rspec/expectations/version.rb +1 -1
  42. data/lib/rspec/matchers.rb +7 -102
  43. data/lib/rspec/matchers/built_in/base_matcher.rb +10 -17
  44. data/lib/rspec/matchers/built_in/be.rb +5 -18
  45. data/lib/rspec/matchers/built_in/be_within.rb +2 -8
  46. data/lib/rspec/matchers/built_in/change.rb +1 -39
  47. data/lib/rspec/matchers/built_in/has.rb +7 -40
  48. data/lib/rspec/matchers/built_in/include.rb +1 -1
  49. data/lib/rspec/matchers/built_in/match_array.rb +1 -1
  50. data/lib/rspec/matchers/built_in/raise_error.rb +44 -23
  51. data/lib/rspec/matchers/built_in/respond_to.rb +1 -7
  52. data/lib/rspec/matchers/built_in/satisfy.rb +1 -7
  53. data/lib/rspec/matchers/built_in/throw_symbol.rb +2 -10
  54. data/lib/rspec/matchers/built_in/yield.rb +4 -25
  55. data/lib/rspec/matchers/compatibility.rb +2 -2
  56. data/lib/rspec/{expectations → matchers}/configuration.rb +9 -6
  57. data/lib/rspec/matchers/dsl.rb +2 -4
  58. data/lib/rspec/matchers/matcher.rb +163 -283
  59. data/lib/rspec/matchers/operator_matcher.rb +57 -71
  60. data/lib/rspec/matchers/pretty.rb +0 -4
  61. data/lib/rspec/matchers/test_unit_integration.rb +5 -22
  62. data/spec/rspec/expectations/expectation_target_spec.rb +0 -62
  63. data/spec/rspec/expectations/extensions/kernel_spec.rb +0 -4
  64. data/spec/rspec/expectations_spec.rb +2 -43
  65. data/spec/rspec/matchers/base_matcher_spec.rb +12 -27
  66. data/spec/rspec/matchers/be_spec.rb +2 -71
  67. data/spec/rspec/matchers/change_spec.rb +1 -76
  68. data/spec/rspec/{expectations → matchers}/configuration_spec.rb +41 -21
  69. data/spec/rspec/matchers/description_generation_spec.rb +2 -21
  70. data/spec/rspec/matchers/equal_spec.rb +0 -26
  71. data/spec/rspec/matchers/has_spec.rb +0 -24
  72. data/spec/rspec/matchers/match_array_spec.rb +0 -13
  73. data/spec/rspec/matchers/matcher_spec.rb +325 -279
  74. data/spec/rspec/matchers/matchers_spec.rb +36 -0
  75. data/spec/rspec/matchers/operator_matcher_spec.rb +8 -27
  76. data/spec/rspec/matchers/raise_error_spec.rb +65 -209
  77. data/spec/rspec/matchers/yield_spec.rb +32 -9
  78. data/spec/spec_helper.rb +21 -6
  79. data/spec/support/classes.rb +7 -7
  80. data/spec/support/in_sub_process.rb +7 -8
  81. data/spec/support/shared_examples.rb +0 -42
  82. metadata +113 -84
  83. metadata.gz.sig +4 -0
  84. data/features/built_in_matchers/have.feature +0 -109
  85. data/features/built_in_matchers/operators.feature +0 -227
  86. data/lib/rspec/expectations/caller_filter.rb +0 -60
  87. data/lib/rspec/expectations/deprecation.rb +0 -27
  88. data/lib/rspec/expectations/extensions/array.rb +0 -9
  89. data/lib/rspec/matchers/be_close.rb +0 -12
  90. data/lib/rspec/matchers/built_in/have.rb +0 -273
  91. data/lib/rspec/matchers/differentiate_block_method_types.rb +0 -55
  92. data/lib/rspec/matchers/extensions/instance_eval_with_args.rb +0 -39
  93. data/lib/rspec/matchers/match_aliases.rb +0 -22
  94. data/spec/rspec/matchers/be_close_spec.rb +0 -25
  95. data/spec/rspec/matchers/differentiate_block_method_types_spec.rb +0 -39
  96. data/spec/rspec/matchers/have_spec.rb +0 -853
  97. data/spec/rspec/matchers/pretty_spec.rb +0 -23
  98. data/spec/support/helper_methods.rb +0 -42
@@ -2,8 +2,6 @@ module RSpec
2
2
  module Matchers
3
3
  module BuiltIn
4
4
  class ThrowSymbol
5
- include MatchAliases
6
-
7
5
  def initialize(expected_symbol = nil, expected_arg=nil)
8
6
  @expected_symbol = expected_symbol
9
7
  @expected_arg = expected_arg
@@ -53,6 +51,7 @@ module RSpec
53
51
  end
54
52
  end
55
53
  end
54
+ alias == matches?
56
55
 
57
56
  def failure_message_for_should
58
57
  "expected #{expected} to be thrown, got #{caught}"
@@ -66,14 +65,7 @@ module RSpec
66
65
  "throw #{expected}"
67
66
  end
68
67
 
69
- # @api private
70
- # Indicates this matcher matches against a block.
71
- # @return [True]
72
- def supports_block_expectations?
73
- true
74
- end
75
-
76
- private
68
+ private
77
69
 
78
70
  def expected(symbol_desc = 'a Symbol')
79
71
  throw_description(@expected_symbol || symbol_desc, @expected_arg)
@@ -24,6 +24,7 @@ module RSpec
24
24
  Proc.new do |*args|
25
25
  probe.num_yields += 1
26
26
  probe.yielded_args << args
27
+ nil # to indicate the block does not return a meaningful value
27
28
  end
28
29
  end
29
30
 
@@ -121,12 +122,7 @@ module RSpec
121
122
  end
122
123
  end
123
124
 
124
- # @private
125
- def supports_block_expectations?
126
- true
127
- end
128
-
129
- private
125
+ private
130
126
 
131
127
  def set_expected_yields_count(relativity, n)
132
128
  @expectation_type = relativity
@@ -174,11 +170,6 @@ module RSpec
174
170
  "expected given block not to yield with no arguments, but did"
175
171
  end
176
172
 
177
- # @private
178
- def supports_block_expectations?
179
- true
180
- end
181
-
182
173
  private
183
174
 
184
175
  def failure_reason
@@ -191,8 +182,6 @@ module RSpec
191
182
  end
192
183
 
193
184
  class YieldWithArgs
194
- include MatchAliases
195
-
196
185
  def initialize(*args)
197
186
  @expected = args
198
187
  end
@@ -202,6 +191,7 @@ module RSpec
202
191
  @actual = @probe.single_yield_args
203
192
  @probe.yielded_once?(:yield_with_args) && args_match?
204
193
  end
194
+ alias == matches?
205
195
 
206
196
  def failure_message_for_should
207
197
  "expected given block to yield with arguments, but #{positive_failure_reason}"
@@ -217,11 +207,6 @@ module RSpec
217
207
  desc
218
208
  end
219
209
 
220
- # @private
221
- def supports_block_expectations?
222
- true
223
- end
224
-
225
210
  private
226
211
 
227
212
  def positive_failure_reason
@@ -267,8 +252,6 @@ module RSpec
267
252
  end
268
253
 
269
254
  class YieldSuccessiveArgs
270
- include MatchAliases
271
-
272
255
  def initialize(*args)
273
256
  @expected = args
274
257
  end
@@ -278,6 +261,7 @@ module RSpec
278
261
  @actual = @probe.successive_yield_args
279
262
  args_match?
280
263
  end
264
+ alias == matches?
281
265
 
282
266
  def failure_message_for_should
283
267
  "expected given block to yield successively with arguments, but yielded with unexpected arguments" +
@@ -297,11 +281,6 @@ module RSpec
297
281
  desc
298
282
  end
299
283
 
300
- # @private
301
- def supports_block_expectations?
302
- true
303
- end
304
-
305
284
  private
306
285
 
307
286
  def args_match?
@@ -1,12 +1,12 @@
1
1
  RSpec::Matchers.constants.each do |c|
2
2
  if Class === (klass = RSpec::Matchers.const_get(c))
3
3
  if klass.public_instance_methods.any? {|m| ['failure_message_for_should',:failure_message_for_should].include?(m)}
4
- klass.class_eval do
4
+ klass.class_exec do
5
5
  alias_method :failure_message, :failure_message_for_should
6
6
  end
7
7
  end
8
8
  if klass.public_instance_methods.any? {|m| ['failure_message_for_should_not',:failure_message_for_should_not].include?(m)}
9
- klass.class_eval do
9
+ klass.class_exec do
10
10
  alias_method :negative_failure_message, :failure_message_for_should_not
11
11
  end
12
12
  end
@@ -1,7 +1,7 @@
1
1
  require 'rspec/expectations/syntax'
2
2
 
3
3
  module RSpec
4
- module Expectations
4
+ module Matchers
5
5
  # Provides configuration options for rspec-expectations.
6
6
  class Configuration
7
7
  # Configures the supported syntax.
@@ -87,6 +87,11 @@ module RSpec
87
87
  end
88
88
  end
89
89
 
90
+ def reset_syntaxes_to_default
91
+ self.syntax = [:should, :expect]
92
+ RSpec::Expectations::Syntax.warn_about_should!
93
+ end
94
+
90
95
  # @api private
91
96
  NullBacktraceFormatter = Module.new do
92
97
  def self.format_backtrace(backtrace)
@@ -94,17 +99,15 @@ module RSpec
94
99
  end
95
100
  end
96
101
  end
97
- end
98
102
 
99
- module Matchers
100
103
  # The configuration object
101
- # @return [RSpec::Expectations::Configuration] the configuration object
104
+ # @return [RSpec::Matchers::Configuration] the configuration object
102
105
  def self.configuration
103
- @configuration ||= Expectations::Configuration.new
106
+ @configuration ||= Configuration.new
104
107
  end
105
108
 
106
109
  # set default syntax
107
- configuration.syntax = [:expect, :should]
110
+ configuration.reset_syntaxes_to_default
108
111
  end
109
112
  end
110
113
 
@@ -4,11 +4,9 @@ module RSpec
4
4
  # Defines a custom matcher.
5
5
  # @see RSpec::Matchers
6
6
  def define(name, &declarations)
7
- matcher_template = RSpec::Matchers::DSL::Matcher.new(name, &declarations)
8
7
  define_method name do |*expected|
9
- matcher = matcher_template.for_expected(*expected)
10
- @matcher_execution_context ||= self
11
- matcher.instance_variable_set(:@matcher_execution_context, @matcher_execution_context)
8
+ matcher = RSpec::Matchers::DSL::Matcher.new(name, declarations, *expected)
9
+ matcher.matcher_execution_context = @matcher_execution_context ||= self
12
10
  matcher
13
11
  end
14
12
  end
@@ -1,115 +1,18 @@
1
1
  require 'set'
2
- require 'rspec/matchers/differentiate_block_method_types'
3
2
 
4
3
  module RSpec
5
4
  module Matchers
6
5
  module DSL
7
- # Provides the context in which the block passed to RSpec::Matchers.define
8
- # will be evaluated.
9
- class Matcher
10
- include RSpec::Matchers::Extensions::InstanceEvalWithArgs
11
- include RSpec::Matchers::Pretty
12
- include RSpec::Matchers
13
-
14
- attr_reader :actual, :rescued_exception
15
-
16
- # @api private
17
- def initialize(name, &declarations)
18
- @name = name
19
- @declarations = declarations
20
- @actual = nil
21
- @diffable = false
22
- @supports_block_expectations = false
23
- @expected_exception, @rescued_exception = nil, nil
24
- @match_for_should_not_block = nil
25
- @messages = {}
26
- @define_block_executed = false
27
- @block_method_differentiator = nil
28
- @deprecated_methods = Set.new
29
- @matcher_execution_context = nil
30
- end
31
-
32
- PERSISTENT_INSTANCE_VARIABLES = [
33
- :@name, :@declarations, :@diffable,
34
- :@supports_block_expectations,
35
- :@match_block, :@match_for_should_not_block,
36
- :@expected_exception
37
- ].to_set
38
-
39
- def expected
40
- if @expected.size == 1
41
- RSpec.warn_deprecation(
42
- "Custom matchers in 3.x will set expected to be a single value "+
43
- "(when provided as such) rather than an array. This may change "+
44
- "the behaviour of your matcher.\n"+
45
- "To continue to access this as an array use `expected_as_array`\n"+
46
- "Called from: #{ RSpec::CallerFilter.first_non_rspec_line }\n\n"
47
- )
48
- end
49
- @expected
50
- end
51
-
52
- def expected_as_array
53
- @expected
54
- end
55
-
56
- def matcher_execution_context=(value)
57
- RSpec.deprecate("`matcher_execution_context=` on custom matchers")
58
- @matcher_execution_context = value
59
- end
60
-
61
- def matcher_execution_context
62
- RSpec.deprecate("`matcher_execution_context` on custom matchers")
63
- @matcher_execution_context
64
- end
65
-
66
- # @api private
67
- def for_expected(*expected)
68
- @expected = expected
69
- dup.instance_eval do
70
- instance_variables.map {|ivar| ivar.intern}.each do |ivar|
71
- instance_variable_set(ivar, nil) unless (PERSISTENT_INSTANCE_VARIABLES + [:@expected]).include?(ivar)
72
- end
73
- @messages = {}
74
- @deprecated_methods = Set.new
75
-
76
- @block_method_differentiator = DifferentiateBlockMethodTypes.new(*@expected, &@declarations)
77
- making_declared_methods_public do
78
- instance_eval_with_args(*@expected, &@declarations)
79
- end
80
-
81
- @define_block_executed = true
82
- self
83
- end
84
- end
85
-
86
- # @api private
87
- # Used internally by +should+ and +should_not+.
88
- def matches?(actual)
89
- @actual = actual
90
- if @expected_exception
91
- begin
92
- instance_eval_with_args(actual, &@match_block)
93
- true
94
- rescue @expected_exception => @rescued_exception
95
- false
96
- end
97
- else
98
- begin
99
- instance_eval_with_args(actual, &@match_block)
100
- rescue RSpec::Expectations::ExpectationNotMetError
101
- false
102
- end
103
- end
104
- end
105
-
6
+ # Contains the methods that are available from within the
7
+ # `RSpec::Matchers.define` DSL for creating custom matchers.
8
+ module Macros
106
9
  # Stores the block that is used to determine whether this matcher passes
107
10
  # or fails. The block should return a boolean value. When the matcher is
108
- # passed to `should` and the block returns `true`, then the expectation
109
- # passes. Similarly, when the matcher is passed to `should_not` and the
11
+ # passed to `expect(...).to` and the block returns `true`, then the expectation
12
+ # passes. Similarly, when the matcher is passed to `expect(...).not_to` and the
110
13
  # block returns `false`, then the expectation passes.
111
14
  #
112
- # Use `match_for_should` when used in conjuntion with
15
+ # Use `match_for_should` when used in conjunction with
113
16
  # `match_for_should_not`.
114
17
  #
115
18
  # @example
@@ -120,26 +23,35 @@ module RSpec
120
23
  # end
121
24
  # end
122
25
  #
123
- # 4.should be_even # passes
124
- # 3.should_not be_even # passes
125
- # 3.should be_even # fails
126
- # 4.should_not be_even # fails
26
+ # expect(4).to be_even # passes
27
+ # expect(3).not_to be_even # passes
28
+ # expect(3).to be_even # fails
29
+ # expect(4).not_to be_even # fails
127
30
  #
128
- # @yield [Object] actual the actual value (or receiver of should)
129
- def match(&block)
130
- @match_block = block
31
+ # @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
32
+ def match(&match_block)
33
+ define_user_override(:matches?, match_block) do |actual|
34
+ begin
35
+ @actual = actual
36
+ super(*actual_arg_for(match_block))
37
+ rescue RSpec::Expectations::ExpectationNotMetError
38
+ false
39
+ end
40
+ end
131
41
  end
42
+ alias match_for_should match
132
43
 
133
- alias_method :match_for_should, :match
134
-
135
- # Use this to define the block for a negative expectation (`should_not`)
44
+ # Use this to define the block for a negative expectation (`expect(...).not_to`)
136
45
  # when the positive and negative forms require different handling. This
137
46
  # is rarely necessary, but can be helpful, for example, when specifying
138
47
  # asynchronous processes that require different timeouts.
139
48
  #
140
- # @yield [Object] actual the actual value (or receiver of should)
141
- def match_for_should_not(&block)
142
- @match_for_should_not_block = block
49
+ # @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
50
+ def match_for_should_not(&match_block)
51
+ define_user_override(:does_not_match?, match_block) do |actual|
52
+ @actual = actual
53
+ super(*actual_arg_for(match_block))
54
+ end
143
55
  end
144
56
 
145
57
  # Use this instead of `match` when the block will raise an exception
@@ -153,76 +65,82 @@ module RSpec
153
65
  # end
154
66
  # end
155
67
  #
156
- # email_validator.should accept_as_valid("person@company.com")
157
- def match_unless_raises(exception=Exception, &block)
158
- @expected_exception = exception
159
- match(&block)
68
+ # expect(email_validator).to accept_as_valid("person@company.com")
69
+ #
70
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
71
+ def match_unless_raises(expected_exception=Exception, &match_block)
72
+ define_user_override(:matches?, match_block) do |actual|
73
+ @actual = actual
74
+ begin
75
+ super(*actual_arg_for(match_block))
76
+ rescue expected_exception => @rescued_exception
77
+ false
78
+ else
79
+ true
80
+ end
81
+ end
160
82
  end
161
83
 
162
- # Customize the failure messsage to use when this matcher is invoked with
163
- # `should`. Only use this when the message generated by default doesn't
164
- # suit your needs.
84
+ # Customizes the failure messsage to use when this matcher is
85
+ # asked to positively match. Only use this when the message
86
+ # generated by default doesn't suit your needs.
165
87
  #
166
88
  # @example
167
89
  #
168
90
  # RSpec::Matchers.define :have_strength do |expected|
169
- # match { ... }
91
+ # match { your_match_logic }
170
92
  #
171
93
  # failure_message_for_should do |actual|
172
94
  # "Expected strength of #{expected}, but had #{actual.strength}"
173
95
  # end
174
96
  # end
175
97
  #
176
- # @yield [Object] actual the actual object
177
- def failure_message_for_should(&block)
178
- cache_or_call_cached(:failure_message_for_should, &block)
98
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
99
+ def failure_message_for_should(&definition)
100
+ define_user_override(__method__, definition)
179
101
  end
180
102
 
181
- # Customize the failure messsage to use when this matcher is invoked with
182
- # `should_not`. Only use this when the message generated by default
183
- # doesn't suit your needs.
103
+ # Customize the failure messsage to use when this matcher is asked
104
+ # to negatively match. Only use this when the message generated by
105
+ # default doesn't suit your needs.
184
106
  #
185
107
  # @example
186
108
  #
187
109
  # RSpec::Matchers.define :have_strength do |expected|
188
- # match { ... }
110
+ # match { your_match_logic }
189
111
  #
190
112
  # failure_message_for_should_not do |actual|
191
113
  # "Expected not to have strength of #{expected}, but did"
192
114
  # end
193
115
  # end
194
116
  #
195
- # @yield [Object] actual the actual object
196
- # @yield [Object] actual the actual object
197
- def failure_message_for_should_not(&block)
198
- cache_or_call_cached(:failure_message_for_should_not, &block)
117
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
118
+ def failure_message_for_should_not(&definition)
119
+ define_user_override(__method__, definition)
199
120
  end
200
121
 
201
-
202
122
  # Customize the description to use for one-liners. Only use this when
203
123
  # the description generated by default doesn't suit your needs.
204
124
  #
205
125
  # @example
206
126
  #
207
127
  # RSpec::Matchers.define :qualify_for do |expected|
208
- # match { ... }
128
+ # match { your_match_logic }
209
129
  #
210
130
  # description do
211
131
  # "qualify for #{expected}"
212
132
  # end
213
133
  # end
214
- def description(&block)
215
- cache_or_call_cached(:description, &block)
134
+ #
135
+ # @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
136
+ def description(&definition)
137
+ define_user_override(__method__, definition)
216
138
  end
217
139
 
218
140
  # Tells the matcher to diff the actual and expected values in the failure
219
141
  # message.
220
142
  def diffable
221
- @diffable = true
222
- end
223
-
224
- def supports_block_expectations
225
- @supports_block_expectations = true
143
+ define_method(:diffable?) { true }
226
144
  end
227
145
 
228
146
  # Convenience for defining methods on this matcher to create a fluent
@@ -242,178 +160,140 @@ module RSpec
242
160
  # end
243
161
  # end
244
162
  #
245
- # minor.should have_errors_on(:age).with("Not old enough to participate")
246
- def chain(method, &block)
247
- define_method method do |*args|
248
- block.call(*args)
163
+ # expect(minor).to have_errors_on(:age).with("Not old enough to participate")
164
+ def chain(name, &definition)
165
+ define_user_override(name, definition) do |*args, &block|
166
+ super(*args, &block)
249
167
  self
250
168
  end
251
169
  end
252
170
 
253
- # @api private
254
- # Used internally by objects returns by +should+ and +should_not+.
255
- def diffable?
256
- @diffable
257
- end
171
+ private
258
172
 
259
- # @api private
260
- def supports_block_expectations?
261
- @supports_block_expectations
173
+ # Does the following:
174
+ #
175
+ # - Defines the named method usign a user-provided block
176
+ # in @user_method_defs, which is included as an ancestor
177
+ # in the singleton class in which we eval the `define` block.
178
+ # - Defines an overriden definition for the same method
179
+ # usign the provided `our_def` block.
180
+ # - Provides a default `our_def` block for the common case
181
+ # of needing to call the user's definition with `@actual`
182
+ # as an arg, but only if their block's arity can handle it.
183
+ #
184
+ # This compiles the user block into an actual method, allowing
185
+ # them to use normal method constructs like `return`
186
+ # (e.g. for a early guard statement), while allowing us to define
187
+ # an override that can provide the wrapped handling
188
+ # (e.g. assigning `@actual`, rescueing errors, etc) and
189
+ # can `super` to the user's definition.
190
+ def define_user_override(method_name, user_def, &our_def)
191
+ @user_method_defs.__send__(:define_method, method_name, &user_def)
192
+ our_def ||= lambda { super(*actual_arg_for(user_def)) }
193
+ define_method(method_name, &our_def)
262
194
  end
195
+ end
263
196
 
197
+ # Defines default implementations of the matcher
198
+ # protocol methods for custom matchers. You can
199
+ # override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
200
+ # from within an `RSpec::Matchers.define` block.
201
+ module DefaultImplementations
264
202
  # @api private
265
- # Used internally by +should_not+
266
- def does_not_match?(actual)
267
- @actual = actual
268
- @match_for_should_not_block ?
269
- instance_eval_with_args(actual, &@match_for_should_not_block) :
270
- !matches?(actual)
271
- end
272
-
273
- def respond_to?(method, include_private=false)
274
- super || @matcher_execution_context.respond_to?(method, include_private)
203
+ # Used internally by objects returns by `should` and `should_not`.
204
+ def diffable?
205
+ false
275
206
  end
276
207
 
277
- private
278
-
279
- def method_missing(method, *args, &block)
280
- if @matcher_execution_context.respond_to?(method)
281
- @matcher_execution_context.__send__ method, *args, &block
282
- else
283
- super(method, *args, &block)
284
- end
208
+ # The default description.
209
+ def description
210
+ "#{name_to_sentence}#{expected_to_sentence}"
285
211
  end
286
212
 
287
- def include(*modules)
288
- return_value = singleton_class.__send__(:include, *modules)
289
-
290
- modules.each do |mod|
291
- mod.instance_methods.each do |name|
292
- add_deprecation_warning_to(name,
293
- "Calling a helper method (`#{name}`) from a module included in a custom matcher as a macro",
294
- "`extend #{mod.name || "TheModule"}`",
295
- "included in the custom matcher",
296
- :unless
297
- )
298
- end
299
- end
300
-
301
- return_value
213
+ # The default failure message for positive expectations.
214
+ def failure_message_for_should
215
+ "expected #{actual.inspect} to #{name_to_sentence}#{expected_to_sentence}"
302
216
  end
303
217
 
304
- def extend(*modules)
305
- return_value = super
306
-
307
- modules.each do |mod|
308
- mod.instance_methods.each do |name|
309
- add_deprecation_warning_to(name,
310
- "Calling a helper method (`#{name}`) from a module extended onto a custom matcher",
311
- "`include #{mod.name || "TheModule"}`",
312
- "extended onto the custom matcher",
313
- :if
314
- )
315
- end
316
- end unless @define_block_executed
317
-
318
- return_value
218
+ # The default failure message for negative expectations.
219
+ def failure_message_for_should_not
220
+ "expected #{actual.inspect} not to #{name_to_sentence}#{expected_to_sentence}"
319
221
  end
222
+ end
320
223
 
321
- def define_method(name, &block)
322
- singleton_class.__send__(:define_method, name, &block)
323
- end
224
+ # The class used for custom matchers. The block passed to
225
+ # `RSpec::Matchers.define` will be evaluated in the context
226
+ # of the singleton class of an instance, and will have the
227
+ # {RSpec::Matchers::DSL::Macros Macros} methods available.
228
+ class Matcher
229
+ # Provides default implementations for the matcher protocol methods.
230
+ include DefaultImplementations
324
231
 
325
- def making_declared_methods_public
326
- # Our home-grown instance_exec in ruby 1.8.6 results in any methods
327
- # declared in the block eval'd by instance_exec in the block to which we
328
- # are yielding here are scoped private. This is NOT the case for Ruby
329
- # 1.8.7 or 1.9.
330
- #
331
- # Also, due some crazy scoping that I don't understand, these methods
332
- # are actually available in the specs (something about the matcher being
333
- # defined in the scope of RSpec::Matchers or within an example), so not
334
- # doing the following will not cause specs to fail, but they *will*
335
- # cause features to fail and that will make users unhappy. So don't.
336
- orig_private_methods = private_methods
337
- yield
338
- (private_methods - orig_private_methods).each {|m| singleton_class.__send__ :public, m}
339
- end
232
+ # Allows expectation expressions to be used in the match block.
233
+ include RSpec::Matchers
340
234
 
341
- def cache_or_call_cached(key, &block)
342
- block ? cache(key, &block) : call_cached(key)
343
- end
235
+ # Converts matcher name and expected args to an English expresion.
236
+ include RSpec::Matchers::Pretty
344
237
 
345
- def cache(key, &block)
346
- @messages[key] = block
347
- end
238
+ # Makes the macro methods available to an `RSpec::Matchers.define` block.
239
+ extend Macros
348
240
 
349
- def call_cached(key)
350
- if @messages.has_key?(key)
351
- @messages[key].arity == 1 ? @messages[key].call(@actual) : @messages[key].call
352
- else
353
- __send__("default_#{key}")
354
- end
355
- end
241
+ attr_reader :expected, :actual, :rescued_exception
242
+ attr_accessor :matcher_execution_context
356
243
 
357
- def default_description
358
- "#{name_to_sentence}#{to_sentence expected_as_array}"
359
- end
244
+ # @api private
245
+ def initialize(name, declarations, *expected)
246
+ @name = name
247
+ @actual = nil
248
+ @expected = expected
360
249
 
361
- def default_failure_message_for_should
362
- "expected #{actual.inspect} to #{name_to_sentence}#{to_sentence expected_as_array}"
250
+ class << self
251
+ # See `Macros#define_user_override` above, for an explanation.
252
+ include(@user_method_defs = Module.new)
253
+ self
254
+ end.class_exec(*expected, &declarations)
363
255
  end
364
256
 
365
- def default_failure_message_for_should_not
366
- "expected #{actual.inspect} not to #{name_to_sentence}#{to_sentence expected_as_array}"
257
+ # Adds the name (rather than a cryptic hex number)
258
+ # so we can identify an instance of
259
+ # the matcher in error messages (e.g. for `NoMethodError`)
260
+ def inspect
261
+ "#<#{self.class.name} #{name}>"
367
262
  end
368
263
 
369
- unless method_defined?(:singleton_class)
370
- def singleton_class
371
- class << self; self; end
264
+ if RUBY_VERSION.to_f >= 1.9
265
+ # Indicates that this matcher responds to messages
266
+ # from the `matcher_execution_context` as well.
267
+ # Also, supports getting a method object for such methods.
268
+ def respond_to_missing?(method, include_private=false)
269
+ super || matcher_execution_context.respond_to?(method, include_private)
372
270
  end
373
- end
374
-
375
- def singleton_method_added(name)
376
- return unless @block_method_differentiator
377
-
378
- if @block_method_differentiator.instance_methods.include?(name)
379
- add_deprecation_warning_to(name,
380
- "Calling a helper method (`#{name}`) defined as an instance method (using `def #{name}`) as a macro from a custom matcher `define` block",
381
- "`def self.#{name}` (to define it as a singleton method)",
382
- "defined in the custom matcher definition block",
383
- :unless
384
- )
385
- elsif @block_method_differentiator.singleton_methods.include?(name)
386
- add_deprecation_warning_to(name,
387
- "Calling a helper method (`#{name}`) defined as a singleton method (using `def self.#{name}`) on a custom matcher",
388
- "`def #{name}` (to define it as an instance method)",
389
- "defined in the custom matcher definition block",
390
- :if
391
- )
271
+ else # for 1.8.7
272
+ # Indicates that this matcher responds to messages
273
+ # from the `matcher_execution_context` as well.
274
+ def respond_to?(method, include_private=false)
275
+ super || matcher_execution_context.respond_to?(method, include_private)
392
276
  end
393
277
  end
394
278
 
395
- def add_deprecation_warning_to(method_name, msg, replacement, extra_call_site_msg, condition)
396
- return if @deprecated_methods.include?(method_name)
397
- @deprecated_methods << method_name
398
-
399
- aliased_name = aliased_name_for(method_name)
400
- singleton_class.__send__(:alias_method, aliased_name, method_name)
279
+ private
401
280
 
402
- singleton_class.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
403
- def #{method_name}(*a, &b)
404
- ::RSpec.deprecate(#{msg.inspect},
405
- :replacement => #{replacement.inspect},
406
- :call_site => CallerFilter.first_non_rspec_line + " and #{extra_call_site_msg} at #{CallerFilter.first_non_rspec_line}"
407
- ) #{condition} @define_block_executed
408
-
409
- __send__(#{aliased_name.inspect}, *a, &b)
410
- end
411
- EOS
281
+ def actual_arg_for(block)
282
+ block.arity.zero? ? [] : [@actual]
412
283
  end
413
284
 
414
- def aliased_name_for(method_name)
415
- target, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
416
- "#{target}_without_rspec_deprecation_warning#{punctuation}"
285
+ # Takes care of forwarding unhandled messages to the
286
+ # `matcher_execution_context` (typically the current
287
+ # running `RSpec::Core::Example`). This is needed by
288
+ # rspec-rails so that it can define matchers that wrap
289
+ # Rails' test helper methods, but it's also a useful
290
+ # feature in its own right.
291
+ def method_missing(method, *args, &block)
292
+ if matcher_execution_context.respond_to?(method)
293
+ matcher_execution_context.__send__ method, *args, &block
294
+ else
295
+ super(method, *args, &block)
296
+ end
417
297
  end
418
298
  end
419
299
  end