Skip to content

Instantly share code, notes, and snippets.

@graywh
Forked from awinograd/delegate_matcher.rb
Last active November 30, 2016 08:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save graywh/7933217 to your computer and use it in GitHub Desktop.
Save graywh/7933217 to your computer and use it in GitHub Desktop.
# RSpec matcher to spec delegations.
# Forked from https://gist.github.com/ssimeonov/5942729 with fixes
# for arity + custom prefix.
#
# Usage:
#
# describe Post do
# it { should delegate(:name).to(:author).with_prefix } # post.author_name
# it { should delegate(:name).to(:author).with_prefix(:any) } # post.any_name
# it { should delegate(:month).to(:created_at) }
# it { should delegate(:year).to(:created_at) }
# it { should delegate(:something).to(:'@instance_var') }
# end
RSpec::Matchers.define :delegate do |method|
match do |delegator|
@method = @prefix ? :"#{@prefix}_#{method}" : method
@delegator = delegator
if @to.to_s[0] == '@'
# Delegation to an instance variable
old_value = @delegator.instance_variable_get(@to)
begin
@delegator.instance_variable_set(@to, receiver_double(method))
x = @delegator.send(@method) == :called
@delegator.instance_variable_set(@to, nil)
if @allow_nil
x &= @delegator.send(@method).nil?
else
begin
@delegator.send(@method)
x = false
rescue NoMethodError => e
x &= x.message =~ /for nil:NilClass/
end
end
x
ensure
@delegator.instance_variable_set(@to, old_value)
end
elsif @delegator.respond_to?(@to, true)
unless [0,-1].include?(@delegator.method(@to).arity)
raise "#{@delegator}'s' #{@to} method does not have zero or -1 arity (it expects parameters)"
end
@delegator.stub(@to).and_return(receiver_double(method))
x = @delegator.send(@method) == :called
@delegator.stub(@to).and_return(nil)
if @allow_nil
x &= @delegator.send(@method).nil?
else
begin
@delegator.send(@method)
x = false
rescue NoMethodError => e
x &= x.message =~ /for nil:NilClass/
end
end
x
else
raise "#{@delegator} does not respond to #{@to}"
end
end
description do
"delegate :#{@method} to its #{@to}#{@prefix ? ' with prefix' : ''} and #{@allow_nil ? 'allow nil' : 'throw an error when nil'}"
end
failure_message_for_should do |text|
"expected #{@delegator} to #{description}"
end
failure_message_for_should_not do |text|
"expected #{@delegator} not to #{description}"
end
chain(:to) { |receiver| @to = receiver }
chain(:with_prefix) { |prefix| @prefix = prefix || @to }
chain(:allow_nil) { @allow_nil = true }
def receiver_double(method)
double('receiver').tap do |receiver|
receiver.stub(method).and_return(:called)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment