lib/rspec/matchers/built_in/change.rb in rspec-expectations-3.0.0.beta2 vs lib/rspec/matchers/built_in/change.rb in rspec-expectations-3.0.0.rc1
- old
+ new
@@ -1,75 +1,107 @@
module RSpec
module Matchers
module BuiltIn
- # Describes an expected mutation.
+ # @api private
+ # Provides the implementation for `change`.
+ # Not intended to be instantiated directly.
class Change
include Composable
+ # @api public
# Specifies the delta of the expected change.
def by(expected_delta)
ChangeRelatively.new(@change_details, expected_delta, :by) do |actual_delta|
values_match?(expected_delta, actual_delta)
end
end
+ # @api public
# Specifies a minimum delta of the expected change.
def by_at_least(minimum)
ChangeRelatively.new(@change_details, minimum, :by_at_least) do |actual_delta|
actual_delta >= minimum
end
end
+ # @api public
# Specifies a maximum delta of the expected change.
def by_at_most(maximum)
ChangeRelatively.new(@change_details, maximum, :by_at_most) do |actual_delta|
actual_delta <= maximum
end
end
+ # @api public
# Specifies the new value you expect.
def to(value)
ChangeToValue.new(@change_details, value)
end
+ # @api public
# Specifies the original value.
def from(value)
ChangeFromValue.new(@change_details, value)
end
- # @api private
+ # @private
def matches?(event_proc)
+ @event_proc = event_proc
+ return false unless Proc === event_proc
raise_block_syntax_error if block_given?
@change_details.perform_change(event_proc)
@change_details.changed?
end
+ def does_not_match?(event_proc)
+ raise_block_syntax_error if block_given?
+ !matches?(event_proc) && Proc === event_proc
+ end
+
# @api private
+ # @return [String]
def failure_message
- "expected #{@change_details.message} to have changed, but is still #{description_of @change_details.actual_before}"
+ "expected #{@change_details.message} to have changed, but #{positive_failure_reason}"
end
# @api private
+ # @return [String]
def failure_message_when_negated
- "expected #{@change_details.message} not to have changed, but did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
+ "expected #{@change_details.message} not to have changed, but #{negative_failure_reason}"
end
# @api private
+ # @return [String]
def description
"change #{@change_details.message}"
end
+ # @private
+ def supports_block_expectations?
+ true
+ end
+
private
def initialize(receiver=nil, message=nil, &block)
@change_details = ChangeDetails.new(receiver, message, &block)
end
def raise_block_syntax_error
raise SyntaxError,
"The block passed to the `change` matcher must use `{ ... }` instead of do/end"
end
+
+ def positive_failure_reason
+ return "was not given a block" unless Proc === @event_proc
+ "is still #{description_of @change_details.actual_before}"
+ end
+
+ def negative_failure_reason
+ return "was not given a block" unless Proc === @event_proc
+ "did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
+ end
end
# Used to specify a relative change.
# @api private
class ChangeRelatively
@@ -80,57 +112,85 @@
@expected_delta = expected_delta
@relativity = relativity
@comparer = comparer
end
+ # @private
def failure_message
- "expected #{@change_details.message} to have changed #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}, " +
- "but was changed by #{description_of @change_details.actual_delta}"
+ "expected #{@change_details.message} to have changed #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}, but #{failure_reason}"
end
+ # @private
def matches?(event_proc)
+ @event_proc = event_proc
+ return false unless Proc === event_proc
@change_details.perform_change(event_proc)
@comparer.call(@change_details.actual_delta)
end
+ # @private
def does_not_match?(event_proc)
raise NotImplementedError, "`expect { }.not_to change { }.#{@relativity}()` is not supported"
end
- # @api private
+ # @private
def description
"change #{@change_details.message} #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}"
end
+
+ # @private
+ def supports_block_expectations?
+ true
+ end
+
+ private
+
+ def failure_reason
+ return "was not given a block" unless Proc === @event_proc
+ "was changed by #{description_of @change_details.actual_delta}"
+ end
end
- # Base class for specifying a change from and/or to specific values.
# @api private
+ # Base class for specifying a change from and/or to specific values.
class SpecificValuesChange
include Composable
+ # @private
MATCH_ANYTHING = ::Object.ancestors.last
def initialize(change_details, from, to)
@change_details = change_details
@expected_before = from
@expected_after = to
end
+ # @private
def matches?(event_proc)
+ @event_proc = event_proc
+ return false unless Proc === event_proc
@change_details.perform_change(event_proc)
@change_details.changed? && matches_before? && matches_after?
end
+ # @private
def description
"change #{@change_details.message} #{change_description}"
end
+ # @private
def failure_message
- return before_value_failure unless matches_before?
- return did_not_change_failure unless @change_details.changed?
+ return not_given_a_block_failure unless Proc === @event_proc
+ return before_value_failure unless matches_before?
+ return did_not_change_failure unless @change_details.changed?
after_value_failure
end
+ # @private
+ def supports_block_expectations?
+ true
+ end
+
private
def matches_before?
values_match?(@expected_before, @change_details.actual_before)
end
@@ -152,37 +212,48 @@
end
def did_change_failure
"expected #{@change_details.message} not to have changed, but did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
end
+
+ def not_given_a_block_failure
+ "expected #{@change_details.message} to have changed #{change_description}, but was not given a block"
+ end
end
+ # @api private
# Used to specify a change from a specific value
# (and, optionally, to a specific value).
- # @api private
class ChangeFromValue < SpecificValuesChange
def initialize(change_details, expected_before)
@description_suffix = nil
super(change_details, expected_before, MATCH_ANYTHING)
end
+ # @api public
+ # Specifies the new value you expect.
def to(value)
@expected_after = value
@description_suffix = " to #{description_of value}"
self
end
+ # @private
def does_not_match?(event_proc)
if @description_suffix
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
end
+ @event_proc = event_proc
+ return false unless Proc === event_proc
@change_details.perform_change(event_proc)
!@change_details.changed? && matches_before?
end
+ # @private
def failure_message_when_negated
+ return not_given_a_block_failure unless Proc === @event_proc
return before_value_failure unless matches_before?
did_change_failure
end
private
@@ -190,25 +261,28 @@
def change_description
"from #{description_of @expected_before}#{@description_suffix}"
end
end
+ # @api private
# Used to specify a change to a specific value
# (and, optionally, from a specific value).
- # @api private
class ChangeToValue < SpecificValuesChange
def initialize(change_details, expected_after)
@description_suffix = nil
super(change_details, MATCH_ANYTHING, expected_after)
end
+ # @api public
+ # Specifies the original value.
def from(value)
@expected_before = value
@description_suffix = " from #{description_of value}"
self
end
+ # @private
def does_not_match?(event_proc)
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
end
private
@@ -216,12 +290,12 @@
def change_description
"to #{description_of @expected_after}#{@description_suffix}"
end
end
+ # @private
# Encapsulates the details of the before/after values.
- # @api private
class ChangeDetails
attr_reader :message, :actual_before, :actual_after
def initialize(receiver=nil, message=nil, &block)
@message = message ? "##{message}" : "result"
@@ -244,9 +318,11 @@
private
def evaluate_value_proc
case val = @value_proc.call
+ when IO # enumerable, but we don't want to dup it.
+ val
when Enumerable, String
val.dup
else
val
end