rspec-expectations 3.5.0 → 3.9.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Changelog.md +138 -2
- data/README.md +37 -20
- data/lib/rspec/expectations.rb +2 -1
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +14 -0
- data/lib/rspec/expectations/expectation_target.rb +2 -2
- data/lib/rspec/expectations/fail_with.rb +9 -1
- data/lib/rspec/expectations/handler.rb +2 -2
- data/lib/rspec/expectations/minitest_integration.rb +1 -1
- data/lib/rspec/expectations/syntax.rb +2 -2
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/matchers.rb +97 -97
- data/lib/rspec/matchers/built_in/all.rb +1 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +14 -2
- data/lib/rspec/matchers/built_in/be.rb +2 -2
- data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
- data/lib/rspec/matchers/built_in/change.rb +127 -53
- data/lib/rspec/matchers/built_in/compound.rb +6 -2
- data/lib/rspec/matchers/built_in/contain_exactly.rb +18 -2
- data/lib/rspec/matchers/built_in/exist.rb +5 -1
- data/lib/rspec/matchers/built_in/has.rb +1 -1
- data/lib/rspec/matchers/built_in/include.rb +6 -0
- data/lib/rspec/matchers/built_in/raise_error.rb +1 -1
- data/lib/rspec/matchers/built_in/respond_to.rb +13 -4
- data/lib/rspec/matchers/built_in/satisfy.rb +27 -4
- data/lib/rspec/matchers/built_in/yield.rb +43 -30
- data/lib/rspec/matchers/composable.rb +6 -20
- data/lib/rspec/matchers/dsl.rb +72 -4
- data/lib/rspec/matchers/english_phrasing.rb +3 -3
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +16 -7
- data/lib/rspec/matchers/generated_descriptions.rb +1 -2
- metadata +22 -18
- metadata.gz.sig +0 -0
| @@ -14,7 +14,11 @@ module RSpec | |
| 14 14 | 
             
                  private
         | 
| 15 15 |  | 
| 16 16 | 
             
                    def match(expected, actual)
         | 
| 17 | 
            -
                      actual.instance_of? | 
| 17 | 
            +
                      actual.instance_of?(expected)
         | 
| 18 | 
            +
                    rescue NoMethodError
         | 
| 19 | 
            +
                      raise ::ArgumentError, "The #{matcher_name} matcher requires that " \
         | 
| 20 | 
            +
                                             "the actual object responds to #instance_of? method " \
         | 
| 21 | 
            +
                                             "but a `NoMethodError` was encountered instead."
         | 
| 18 22 | 
             
                    end
         | 
| 19 23 | 
             
                  end
         | 
| 20 24 | 
             
                end
         | 
| @@ -8,7 +8,11 @@ module RSpec | |
| 8 8 | 
             
                  private
         | 
| 9 9 |  | 
| 10 10 | 
             
                    def match(expected, actual)
         | 
| 11 | 
            -
                      actual.kind_of? | 
| 11 | 
            +
                      actual.kind_of?(expected)
         | 
| 12 | 
            +
                    rescue NoMethodError
         | 
| 13 | 
            +
                      raise ::ArgumentError, "The #{matcher_name} matcher requires that " \
         | 
| 14 | 
            +
                                             "the actual object responds to #kind_of? method " \
         | 
| 15 | 
            +
                                             "but a `NoMethodError` was encountered instead."
         | 
| 12 16 | 
             
                    end
         | 
| 13 17 | 
             
                  end
         | 
| 14 18 | 
             
                end
         | 
| @@ -8,7 +8,7 @@ module RSpec | |
| 8 8 | 
             
                    # @api public
         | 
| 9 9 | 
             
                    # Specifies the delta of the expected change.
         | 
| 10 10 | 
             
                    def by(expected_delta)
         | 
| 11 | 
            -
                      ChangeRelatively.new( | 
| 11 | 
            +
                      ChangeRelatively.new(change_details, expected_delta, :by) do |actual_delta|
         | 
| 12 12 | 
             
                        values_match?(expected_delta, actual_delta)
         | 
| 13 13 | 
             
                      end
         | 
| 14 14 | 
             
                    end
         | 
| @@ -16,7 +16,7 @@ module RSpec | |
| 16 16 | 
             
                    # @api public
         | 
| 17 17 | 
             
                    # Specifies a minimum delta of the expected change.
         | 
| 18 18 | 
             
                    def by_at_least(minimum)
         | 
| 19 | 
            -
                      ChangeRelatively.new( | 
| 19 | 
            +
                      ChangeRelatively.new(change_details, minimum, :by_at_least) do |actual_delta|
         | 
| 20 20 | 
             
                        actual_delta >= minimum
         | 
| 21 21 | 
             
                      end
         | 
| 22 22 | 
             
                    end
         | 
| @@ -24,7 +24,7 @@ module RSpec | |
| 24 24 | 
             
                    # @api public
         | 
| 25 25 | 
             
                    # Specifies a maximum delta of the expected change.
         | 
| 26 26 | 
             
                    def by_at_most(maximum)
         | 
| 27 | 
            -
                      ChangeRelatively.new( | 
| 27 | 
            +
                      ChangeRelatively.new(change_details, maximum, :by_at_most) do |actual_delta|
         | 
| 28 28 | 
             
                        actual_delta <= maximum
         | 
| 29 29 | 
             
                      end
         | 
| 30 30 | 
             
                    end
         | 
| @@ -32,47 +32,44 @@ module RSpec | |
| 32 32 | 
             
                    # @api public
         | 
| 33 33 | 
             
                    # Specifies the new value you expect.
         | 
| 34 34 | 
             
                    def to(value)
         | 
| 35 | 
            -
                      ChangeToValue.new( | 
| 35 | 
            +
                      ChangeToValue.new(change_details, value)
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 38 | 
             
                    # @api public
         | 
| 39 39 | 
             
                    # Specifies the original value.
         | 
| 40 40 | 
             
                    def from(value)
         | 
| 41 | 
            -
                      ChangeFromValue.new( | 
| 41 | 
            +
                      ChangeFromValue.new(change_details, value)
         | 
| 42 42 | 
             
                    end
         | 
| 43 43 |  | 
| 44 44 | 
             
                    # @private
         | 
| 45 45 | 
             
                    def matches?(event_proc)
         | 
| 46 | 
            -
                      @event_proc = event_proc
         | 
| 47 | 
            -
                      return false unless Proc === event_proc
         | 
| 48 46 | 
             
                      raise_block_syntax_error if block_given?
         | 
| 49 | 
            -
                       | 
| 50 | 
            -
                      @change_details.changed?
         | 
| 47 | 
            +
                      perform_change(event_proc) && change_details.changed?
         | 
| 51 48 | 
             
                    end
         | 
| 52 49 |  | 
| 53 50 | 
             
                    def does_not_match?(event_proc)
         | 
| 54 51 | 
             
                      raise_block_syntax_error if block_given?
         | 
| 55 | 
            -
                       | 
| 52 | 
            +
                      perform_change(event_proc) && !change_details.changed?
         | 
| 56 53 | 
             
                    end
         | 
| 57 54 |  | 
| 58 55 | 
             
                    # @api private
         | 
| 59 56 | 
             
                    # @return [String]
         | 
| 60 57 | 
             
                    def failure_message
         | 
| 61 | 
            -
                      "expected #{ | 
| 58 | 
            +
                      "expected #{change_details.value_representation} to have changed, " \
         | 
| 62 59 | 
             
                      "but #{positive_failure_reason}"
         | 
| 63 60 | 
             
                    end
         | 
| 64 61 |  | 
| 65 62 | 
             
                    # @api private
         | 
| 66 63 | 
             
                    # @return [String]
         | 
| 67 64 | 
             
                    def failure_message_when_negated
         | 
| 68 | 
            -
                      "expected #{ | 
| 65 | 
            +
                      "expected #{change_details.value_representation} not to have changed, " \
         | 
| 69 66 | 
             
                      "but #{negative_failure_reason}"
         | 
| 70 67 | 
             
                    end
         | 
| 71 68 |  | 
| 72 69 | 
             
                    # @api private
         | 
| 73 70 | 
             
                    # @return [String]
         | 
| 74 71 | 
             
                    def description
         | 
| 75 | 
            -
                      "change #{ | 
| 72 | 
            +
                      "change #{change_details.value_representation}"
         | 
| 76 73 | 
             
                    end
         | 
| 77 74 |  | 
| 78 75 | 
             
                    # @private
         | 
| @@ -83,7 +80,25 @@ module RSpec | |
| 83 80 | 
             
                  private
         | 
| 84 81 |  | 
| 85 82 | 
             
                    def initialize(receiver=nil, message=nil, &block)
         | 
| 86 | 
            -
                      @ | 
| 83 | 
            +
                      @receiver = receiver
         | 
| 84 | 
            +
                      @message = message
         | 
| 85 | 
            +
                      @block = block
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    def change_details
         | 
| 89 | 
            +
                      @change_details ||= ChangeDetails.new(matcher_name, @receiver, @message, &@block)
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def perform_change(event_proc)
         | 
| 93 | 
            +
                      @event_proc = event_proc
         | 
| 94 | 
            +
                      change_details.perform_change(event_proc) do |actual_before|
         | 
| 95 | 
            +
                        # pre-compute values derived from the `before` value before the
         | 
| 96 | 
            +
                        # mutation is applied, in case the specified mutation is mutation
         | 
| 97 | 
            +
                        # of a single object (rather than a changing what object a method
         | 
| 98 | 
            +
                        # returns). We need to cache these values before the `before` value
         | 
| 99 | 
            +
                        # they are based on potentially gets mutated.
         | 
| 100 | 
            +
                        @actual_before_description = description_of(actual_before)
         | 
| 101 | 
            +
                      end
         | 
| 87 102 | 
             
                    end
         | 
| 88 103 |  | 
| 89 104 | 
             
                    def raise_block_syntax_error
         | 
| @@ -93,13 +108,13 @@ module RSpec | |
| 93 108 |  | 
| 94 109 | 
             
                    def positive_failure_reason
         | 
| 95 110 | 
             
                      return "was not given a block" unless Proc === @event_proc
         | 
| 96 | 
            -
                      "is still #{ | 
| 111 | 
            +
                      "is still #{@actual_before_description}"
         | 
| 97 112 | 
             
                    end
         | 
| 98 113 |  | 
| 99 114 | 
             
                    def negative_failure_reason
         | 
| 100 115 | 
             
                      return "was not given a block" unless Proc === @event_proc
         | 
| 101 | 
            -
                      "did change from #{ | 
| 102 | 
            -
                      "to #{description_of  | 
| 116 | 
            +
                      "did change from #{@actual_before_description} " \
         | 
| 117 | 
            +
                      "to #{description_of change_details.actual_after}"
         | 
| 103 118 | 
             
                    end
         | 
| 104 119 | 
             
                  end
         | 
| 105 120 |  | 
| @@ -115,7 +130,7 @@ module RSpec | |
| 115 130 |  | 
| 116 131 | 
             
                    # @private
         | 
| 117 132 | 
             
                    def failure_message
         | 
| 118 | 
            -
                      "expected #{@change_details. | 
| 133 | 
            +
                      "expected #{@change_details.value_representation} to have changed " \
         | 
| 119 134 | 
             
                      "#{@relativity.to_s.tr('_', ' ')} " \
         | 
| 120 135 | 
             
                      "#{description_of @expected_delta}, but #{failure_reason}"
         | 
| 121 136 | 
             
                    end
         | 
| @@ -123,9 +138,7 @@ module RSpec | |
| 123 138 | 
             
                    # @private
         | 
| 124 139 | 
             
                    def matches?(event_proc)
         | 
| 125 140 | 
             
                      @event_proc = event_proc
         | 
| 126 | 
            -
                       | 
| 127 | 
            -
                      @change_details.perform_change(event_proc)
         | 
| 128 | 
            -
                      @comparer.call(@change_details.actual_delta)
         | 
| 141 | 
            +
                      @change_details.perform_change(event_proc) && @comparer.call(@change_details.actual_delta)
         | 
| 129 142 | 
             
                    end
         | 
| 130 143 |  | 
| 131 144 | 
             
                    # @private
         | 
| @@ -136,7 +149,7 @@ module RSpec | |
| 136 149 |  | 
| 137 150 | 
             
                    # @private
         | 
| 138 151 | 
             
                    def description
         | 
| 139 | 
            -
                      "change #{@change_details. | 
| 152 | 
            +
                      "change #{@change_details.value_representation} " \
         | 
| 140 153 | 
             
                      "#{@relativity.to_s.tr('_', ' ')} #{description_of @expected_delta}"
         | 
| 141 154 | 
             
                    end
         | 
| 142 155 |  | 
| @@ -167,21 +180,18 @@ module RSpec | |
| 167 180 |  | 
| 168 181 | 
             
                    # @private
         | 
| 169 182 | 
             
                    def matches?(event_proc)
         | 
| 170 | 
            -
                       | 
| 171 | 
            -
                      return false unless Proc === event_proc
         | 
| 172 | 
            -
                      @change_details.perform_change(event_proc)
         | 
| 173 | 
            -
                      @change_details.changed? && matches_before? && matches_after?
         | 
| 183 | 
            +
                      perform_change(event_proc) && @change_details.changed? && @matches_before && matches_after?
         | 
| 174 184 | 
             
                    end
         | 
| 175 185 |  | 
| 176 186 | 
             
                    # @private
         | 
| 177 187 | 
             
                    def description
         | 
| 178 | 
            -
                      "change #{@change_details. | 
| 188 | 
            +
                      "change #{@change_details.value_representation} #{change_description}"
         | 
| 179 189 | 
             
                    end
         | 
| 180 190 |  | 
| 181 191 | 
             
                    # @private
         | 
| 182 192 | 
             
                    def failure_message
         | 
| 183 193 | 
             
                      return not_given_a_block_failure unless Proc === @event_proc
         | 
| 184 | 
            -
                      return before_value_failure      unless matches_before | 
| 194 | 
            +
                      return before_value_failure      unless @matches_before
         | 
| 185 195 | 
             
                      return did_not_change_failure    unless @change_details.changed?
         | 
| 186 196 | 
             
                      after_value_failure
         | 
| 187 197 | 
             
                    end
         | 
| @@ -193,8 +203,17 @@ module RSpec | |
| 193 203 |  | 
| 194 204 | 
             
                  private
         | 
| 195 205 |  | 
| 196 | 
            -
                    def  | 
| 197 | 
            -
                       | 
| 206 | 
            +
                    def perform_change(event_proc)
         | 
| 207 | 
            +
                      @event_proc = event_proc
         | 
| 208 | 
            +
                      @change_details.perform_change(event_proc) do |actual_before|
         | 
| 209 | 
            +
                        # pre-compute values derived from the `before` value before the
         | 
| 210 | 
            +
                        # mutation is applied, in case the specified mutation is mutation
         | 
| 211 | 
            +
                        # of a single object (rather than a changing what object a method
         | 
| 212 | 
            +
                        # returns). We need to cache these values before the `before` value
         | 
| 213 | 
            +
                        # they are based on potentially gets mutated.
         | 
| 214 | 
            +
                        @matches_before = values_match?(@expected_before, actual_before)
         | 
| 215 | 
            +
                        @actual_before_description = description_of(actual_before)
         | 
| 216 | 
            +
                      end
         | 
| 198 217 | 
             
                    end
         | 
| 199 218 |  | 
| 200 219 | 
             
                    def matches_after?
         | 
| @@ -202,30 +221,30 @@ module RSpec | |
| 202 221 | 
             
                    end
         | 
| 203 222 |  | 
| 204 223 | 
             
                    def before_value_failure
         | 
| 205 | 
            -
                      "expected #{@change_details. | 
| 224 | 
            +
                      "expected #{@change_details.value_representation} " \
         | 
| 206 225 | 
             
                      "to have initially been #{description_of @expected_before}, " \
         | 
| 207 | 
            -
                      "but was #{ | 
| 226 | 
            +
                      "but was #{@actual_before_description}"
         | 
| 208 227 | 
             
                    end
         | 
| 209 228 |  | 
| 210 229 | 
             
                    def after_value_failure
         | 
| 211 | 
            -
                      "expected #{@change_details. | 
| 230 | 
            +
                      "expected #{@change_details.value_representation} " \
         | 
| 212 231 | 
             
                      "to have changed to #{description_of @expected_after}, " \
         | 
| 213 232 | 
             
                      "but is now #{description_of @change_details.actual_after}"
         | 
| 214 233 | 
             
                    end
         | 
| 215 234 |  | 
| 216 235 | 
             
                    def did_not_change_failure
         | 
| 217 | 
            -
                      "expected #{@change_details. | 
| 236 | 
            +
                      "expected #{@change_details.value_representation} " \
         | 
| 218 237 | 
             
                      "to have changed #{change_description}, but did not change"
         | 
| 219 238 | 
             
                    end
         | 
| 220 239 |  | 
| 221 240 | 
             
                    def did_change_failure
         | 
| 222 | 
            -
                      "expected #{@change_details. | 
| 223 | 
            -
                      "did change from #{ | 
| 241 | 
            +
                      "expected #{@change_details.value_representation} not to have changed, but " \
         | 
| 242 | 
            +
                      "did change from #{@actual_before_description} " \
         | 
| 224 243 | 
             
                      "to #{description_of @change_details.actual_after}"
         | 
| 225 244 | 
             
                    end
         | 
| 226 245 |  | 
| 227 246 | 
             
                    def not_given_a_block_failure
         | 
| 228 | 
            -
                      "expected #{@change_details. | 
| 247 | 
            +
                      "expected #{@change_details.value_representation} to have changed " \
         | 
| 229 248 | 
             
                      "#{change_description}, but was not given a block"
         | 
| 230 249 | 
             
                    end
         | 
| 231 250 | 
             
                  end
         | 
| @@ -254,16 +273,13 @@ module RSpec | |
| 254 273 | 
             
                          "is not supported"
         | 
| 255 274 | 
             
                      end
         | 
| 256 275 |  | 
| 257 | 
            -
                       | 
| 258 | 
            -
                      return false unless Proc === event_proc
         | 
| 259 | 
            -
                      @change_details.perform_change(event_proc)
         | 
| 260 | 
            -
                      !@change_details.changed? && matches_before?
         | 
| 276 | 
            +
                      perform_change(event_proc) && !@change_details.changed? && @matches_before
         | 
| 261 277 | 
             
                    end
         | 
| 262 278 |  | 
| 263 279 | 
             
                    # @private
         | 
| 264 280 | 
             
                    def failure_message_when_negated
         | 
| 265 281 | 
             
                      return not_given_a_block_failure unless Proc === @event_proc
         | 
| 266 | 
            -
                      return before_value_failure unless matches_before | 
| 282 | 
            +
                      return before_value_failure unless @matches_before
         | 
| 267 283 | 
             
                      did_change_failure
         | 
| 268 284 | 
             
                    end
         | 
| 269 285 |  | 
| @@ -306,10 +322,22 @@ module RSpec | |
| 306 322 |  | 
| 307 323 | 
             
                  # @private
         | 
| 308 324 | 
             
                  # Encapsulates the details of the before/after values.
         | 
| 325 | 
            +
                  #
         | 
| 326 | 
            +
                  # Note that this class exposes the `actual_after` value, to allow the
         | 
| 327 | 
            +
                  # matchers above to derive failure messages, etc from the value on demand
         | 
| 328 | 
            +
                  # as needed, but it intentionally does _not_ expose the `actual_before`
         | 
| 329 | 
            +
                  # value. Some usages of the `change` matcher mutate a specific object
         | 
| 330 | 
            +
                  # returned by the value proc, which means that failure message snippets,
         | 
| 331 | 
            +
                  # etc, which are derived from the `before` value may not be accurate if
         | 
| 332 | 
            +
                  # they are lazily computed as needed. We must pre-compute them before
         | 
| 333 | 
            +
                  # applying the change in the `expect` block. To ensure that all `change`
         | 
| 334 | 
            +
                  # matchers do that properly, we do not expose the `actual_before` value.
         | 
| 335 | 
            +
                  # Instead, matchers must pass a block to `perform_change`, which yields
         | 
| 336 | 
            +
                  # the `actual_before` value before applying the change.
         | 
| 309 337 | 
             
                  class ChangeDetails
         | 
| 310 | 
            -
                    attr_reader : | 
| 338 | 
            +
                    attr_reader :actual_after
         | 
| 311 339 |  | 
| 312 | 
            -
                    def initialize(receiver=nil, message=nil, &block)
         | 
| 340 | 
            +
                    def initialize(matcher_name, receiver=nil, message=nil, &block)
         | 
| 313 341 | 
             
                      if receiver && !message
         | 
| 314 342 | 
             
                        raise(
         | 
| 315 343 | 
             
                          ArgumentError,
         | 
| @@ -318,18 +346,51 @@ module RSpec | |
| 318 346 | 
             
                          "You passed an object but no message."
         | 
| 319 347 | 
             
                        )
         | 
| 320 348 | 
             
                      end
         | 
| 321 | 
            -
             | 
| 322 | 
            -
                      @ | 
| 349 | 
            +
             | 
| 350 | 
            +
                      @matcher_name = matcher_name
         | 
| 351 | 
            +
                      @receiver = receiver
         | 
| 352 | 
            +
                      @message = message
         | 
| 353 | 
            +
                      @value_proc = block
         | 
| 354 | 
            +
                    end
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                    def value_representation
         | 
| 357 | 
            +
                      @value_representation ||=
         | 
| 358 | 
            +
                        if @message
         | 
| 359 | 
            +
                          "`#{message_notation(@receiver, @message)}`"
         | 
| 360 | 
            +
                        elsif (value_block_snippet = extract_value_block_snippet)
         | 
| 361 | 
            +
                          "`#{value_block_snippet}`"
         | 
| 362 | 
            +
                        else
         | 
| 363 | 
            +
                          'result'
         | 
| 364 | 
            +
                        end
         | 
| 323 365 | 
             
                    end
         | 
| 324 366 |  | 
| 325 367 | 
             
                    def perform_change(event_proc)
         | 
| 326 368 | 
             
                      @actual_before = evaluate_value_proc
         | 
| 369 | 
            +
                      @before_hash = @actual_before.hash
         | 
| 370 | 
            +
                      yield @actual_before if block_given?
         | 
| 371 | 
            +
             | 
| 372 | 
            +
                      return false unless Proc === event_proc
         | 
| 327 373 | 
             
                      event_proc.call
         | 
| 374 | 
            +
             | 
| 328 375 | 
             
                      @actual_after = evaluate_value_proc
         | 
| 376 | 
            +
                      @actual_hash = @actual_after.hash
         | 
| 377 | 
            +
                      true
         | 
| 329 378 | 
             
                    end
         | 
| 330 379 |  | 
| 331 380 | 
             
                    def changed?
         | 
| 332 | 
            -
                       | 
| 381 | 
            +
                      # Consider it changed if either:
         | 
| 382 | 
            +
                      #
         | 
| 383 | 
            +
                      # - The before/after values are unequal
         | 
| 384 | 
            +
                      # - The before/after values have different hash values
         | 
| 385 | 
            +
                      #
         | 
| 386 | 
            +
                      # The latter case specifically handles the case when the value proc
         | 
| 387 | 
            +
                      # returns the exact same object, but it has been mutated.
         | 
| 388 | 
            +
                      #
         | 
| 389 | 
            +
                      # Note that it is not sufficient to only check the hashes; it is
         | 
| 390 | 
            +
                      # possible for two values to be unequal (and of different classes)
         | 
| 391 | 
            +
                      # but to return the same hash value. Also, some objects may change
         | 
| 392 | 
            +
                      # their hash after being compared with `==`/`!=`.
         | 
| 393 | 
            +
                      @actual_before != @actual_after || @before_hash != @actual_hash
         | 
| 333 394 | 
             
                    end
         | 
| 334 395 |  | 
| 335 396 | 
             
                    def actual_delta
         | 
| @@ -339,13 +400,26 @@ module RSpec | |
| 339 400 | 
             
                  private
         | 
| 340 401 |  | 
| 341 402 | 
             
                    def evaluate_value_proc
         | 
| 342 | 
            -
                       | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 403 | 
            +
                      @value_proc ? @value_proc.call : @receiver.__send__(@message)
         | 
| 404 | 
            +
                    end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                    def message_notation(receiver, message)
         | 
| 407 | 
            +
                      case receiver
         | 
| 408 | 
            +
                      when Module
         | 
| 409 | 
            +
                        "#{receiver}.#{message}"
         | 
| 347 410 | 
             
                      else
         | 
| 348 | 
            -
                         | 
| 411 | 
            +
                        "#{Support.class_of(receiver)}##{message}"
         | 
| 412 | 
            +
                      end
         | 
| 413 | 
            +
                    end
         | 
| 414 | 
            +
             | 
| 415 | 
            +
                    if RSpec::Support::RubyFeatures.ripper_supported?
         | 
| 416 | 
            +
                      def extract_value_block_snippet
         | 
| 417 | 
            +
                        return nil unless @value_proc
         | 
| 418 | 
            +
                        Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
         | 
| 419 | 
            +
                      end
         | 
| 420 | 
            +
                    else
         | 
| 421 | 
            +
                      def extract_value_block_snippet
         | 
| 422 | 
            +
                        nil
         | 
| 349 423 | 
             
                      end
         | 
| 350 424 | 
             
                    end
         | 
| 351 425 | 
             
                  end
         | 
| @@ -3,7 +3,6 @@ module RSpec | |
| 3 3 | 
             
                module BuiltIn
         | 
| 4 4 | 
             
                  # @api private
         | 
| 5 5 | 
             
                  # Base class for `and` and `or` compound matchers.
         | 
| 6 | 
            -
                  # rubocop:disable ClassLength
         | 
| 7 6 | 
             
                  class Compound < BaseMatcher
         | 
| 8 7 | 
             
                    # @private
         | 
| 9 8 | 
             
                    attr_reader :matcher_1, :matcher_2, :evaluator
         | 
| @@ -155,7 +154,12 @@ module RSpec | |
| 155 154 | 
             
                      end
         | 
| 156 155 |  | 
| 157 156 | 
             
                      def matcher_matches?(matcher)
         | 
| 158 | 
            -
                        @match_results.fetch(matcher)
         | 
| 157 | 
            +
                        @match_results.fetch(matcher) do
         | 
| 158 | 
            +
                          raise ArgumentError, "Your #{matcher.description} has no match " \
         | 
| 159 | 
            +
                           "results, this can occur when an unexpected call stack or " \
         | 
| 160 | 
            +
                           "local jump occurs. Prehaps one of your matchers needs to " \
         | 
| 161 | 
            +
                           "declare `expects_call_stack_jump?` as `true`?"
         | 
| 162 | 
            +
                        end
         | 
| 159 163 | 
             
                      end
         | 
| 160 164 |  | 
| 161 165 | 
             
                    private
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            module RSpec
         | 
| 2 2 | 
             
              module Matchers
         | 
| 3 3 | 
             
                module BuiltIn
         | 
| 4 | 
            +
                  # rubocop:disable ClassLength
         | 
| 4 5 | 
             
                  # @api private
         | 
| 5 6 | 
             
                  # Provides the implementation for `contain_exactly` and `match_array`.
         | 
| 6 7 | 
             
                  # Not intended to be instantiated directly.
         | 
| @@ -85,10 +86,10 @@ module RSpec | |
| 85 86 | 
             
                    def convert_actual_to_an_array
         | 
| 86 87 | 
             
                      if actual.respond_to?(:to_ary)
         | 
| 87 88 | 
             
                        @actual = actual.to_ary
         | 
| 88 | 
            -
                      elsif  | 
| 89 | 
            +
                      elsif actual.respond_to?(:to_a) && !to_a_disallowed?(actual)
         | 
| 89 90 | 
             
                        @actual = actual.to_a
         | 
| 90 91 | 
             
                      else
         | 
| 91 | 
            -
                         | 
| 92 | 
            +
                        false
         | 
| 92 93 | 
             
                      end
         | 
| 93 94 | 
             
                    end
         | 
| 94 95 |  | 
| @@ -98,6 +99,19 @@ module RSpec | |
| 98 99 | 
             
                      array
         | 
| 99 100 | 
             
                    end
         | 
| 100 101 |  | 
| 102 | 
            +
                    if RUBY_VERSION == "1.8.7"
         | 
| 103 | 
            +
                      def to_a_disallowed?(object)
         | 
| 104 | 
            +
                        case object
         | 
| 105 | 
            +
                        when NilClass, String then true
         | 
| 106 | 
            +
                        else Kernel == RSpec::Support.method_handle_for(object, :to_a).owner
         | 
| 107 | 
            +
                        end
         | 
| 108 | 
            +
                      end
         | 
| 109 | 
            +
                    else
         | 
| 110 | 
            +
                      def to_a_disallowed?(object)
         | 
| 111 | 
            +
                        NilClass === object
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
             | 
| 101 115 | 
             
                    def missing_items
         | 
| 102 116 | 
             
                      @missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
         | 
| 103 117 | 
             
                        expected[index]
         | 
| @@ -162,6 +176,7 @@ module RSpec | |
| 162 176 | 
             
                    #
         | 
| 163 177 | 
             
                    # @private
         | 
| 164 178 | 
             
                    class PairingsMaximizer
         | 
| 179 | 
            +
                      # @private
         | 
| 165 180 | 
             
                      Solution = Struct.new(:unmatched_expected_indexes,     :unmatched_actual_indexes,
         | 
| 166 181 | 
             
                                            :indeterminate_expected_indexes, :indeterminate_actual_indexes) do
         | 
| 167 182 | 
             
                        def worse_than?(other)
         | 
| @@ -281,6 +296,7 @@ module RSpec | |
| 281 296 | 
             
                      end
         | 
| 282 297 | 
             
                    end
         | 
| 283 298 | 
             
                  end
         | 
| 299 | 
            +
                  # rubocop:enable ClassLength
         | 
| 284 300 | 
             
                end
         | 
| 285 301 | 
             
              end
         | 
| 286 302 | 
             
            end
         |