Skip to content

Instantly share code, notes, and snippets.

@garrow
Last active August 29, 2015 13:56
Show Gist options
  • Save garrow/9146731 to your computer and use it in GitHub Desktop.
Save garrow/9146731 to your computer and use it in GitHub Desktop.
A quick and crazy go at implementing contracts using Ruby 2.1 and naive evals.
require 'rspec'
require 'pry'
module Trifle
class BrokenContract < Exception ; end
def contract(*contracts, method_name)
old_method_name = :"__contractually_obligated_#{method_name}"
alias_method old_method_name, method_name
named_parameters = instance_method(method_name).parameters.collect { |dep, name| name }
parameter_call = named_parameters.join(', ')
guards = Hash[named_parameters.zip contracts].collect { |argument_name, klass|
"raise BrokenContract unless #{argument_name}.is_a?(#{klass})"
}
class_eval <<-RUBBY, __FILE__, __LINE__ + 1
def #{method_name}(#{parameter_call})
#{guards.join("\n")}
#{old_method_name}(#{parameter_call})
end
RUBBY
end
end
class Tower
extend Trifle
contract Integer, Integer,
def add(a, b)
a + b
end
contract String, String,
def concat(two, things)
"#{two} #{things}"
end
end
describe 'simple class checking' do
subject { Tower.new }
context 'Integers' do
specify do
expect{ subject.add(3,'f') }.to raise_exception Trifle::BrokenContract
end
specify do
expect{ subject.add('a', 2) }.to raise_exception Trifle::BrokenContract
end
specify do
expect(subject.add(3,3)).to eq 6
end
end
context 'Strings' do
specify do
expect{ subject.concat('CON', nil) }.to raise_exception Trifle::BrokenContract
end
specify do
expect{ subject.concat(Object.new, 'CAT') }.to raise_exception Trifle::BrokenContract
end
specify do
expect(subject.concat('CON', 'CAT')).to eq 'CON CAT'
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment