Skip to content

Instantly share code, notes, and snippets.

@tcopeland
Created February 12, 2018 05:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcopeland/22040ee4230bc531c04718aefdc64abe to your computer and use it in GitHub Desktop.
Save tcopeland/22040ee4230bc531c04718aefdc64abe to your computer and use it in GitHub Desktop.
increment decrement matcher
module IncDec
# Mostly copied from RSpec::Matchers::BuiltIn
class ChangeDetails
attr_reader :actual_before, :actual_after
def initialize(matcher_name, receiver=nil, message=nil, &block)
if receiver && !message
raise(
ArgumentError,
"`#{matcher_name}` requires either an object and message " \
"(`#{matcher_name}(obj, :msg)`) or a block (`#{matcher_name} { }`). " \
"You passed an object but no message."
)
end
@matcher_name = matcher_name
@receiver = receiver
@message = message
@value_proc = block
end
def value_representation
@value_representation ||=
if @message
"##{@message}"
elsif (value_block_snippet = extract_value_block_snippet)
"`#{value_block_snippet}`"
else
'result'
end
end
def perform_change(event_proc)
@actual_before = evaluate_value_proc
event_proc.call
@actual_after = evaluate_value_proc
end
def decremented?
(@actual_before - @actual_after) == 1
end
def incremented?
(@actual_after - @actual_before) == 1
end
def actual_delta
@actual_after - @actual_before
end
private
def evaluate_value_proc
value_proc = @value_proc || lambda { @receiver.__send__(@message) }
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
end
if RSpec::Support::RubyFeatures.ripper_supported?
def extract_value_block_snippet
return nil unless @value_proc
Expectations::BlockSnippetExtractor.try_extracting_single_line_body_of(@value_proc, @matcher_name)
end
else
def extract_value_block_snippet
nil
end
end
end
class Adapter
include RSpec::Matchers::Composable
attr_reader :name
def initialize(name:, receiver:, message:, &block)
@name = name
@receiver = receiver
@message = message
@block = block
end
def supports_block_expectations?
true
end
def matches?(event_proc)
@event_proc = event_proc
return false unless Proc === event_proc
change_details = IncDec::ChangeDetails.new(name, @receiver, @message, &@block)
change_details.perform_change(event_proc)
change_details.send("#{name}ed?")
end
def failure_message
"expected #{@event_proc} to be #{name}ed"
end
def failure_message_when_negated
"expected #{@event_proc} not to be #{name}ed"
end
end
end
module RSpec
module Matchers
def increment(receiver=nil, message=nil, &block)
IncDec::Adapter.new(name: "increment", receiver: receiver, message: message, &block)
end
def decrement(receiver=nil, message=nil, &block)
IncDec::Adapter.new(name: "decrement", receiver: receiver, message: message, &block)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment