1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
RSpec.describe Contract do
describe ".override_validator" do
around do |example|
Contract.reset_validators
example.run
Contract.reset_validators
end
it "allows to override simple validators" do
Contract.override_validator(Hash) do |contract|
lambda do |arg|
return false unless arg.is_a?(Hash)
# Any hash in my system should have :it_is_a_hash key!
return false unless arg.key?(:it_is_a_hash)
contract.keys.all? do |k|
Contract.valid?(arg[k], contract[k])
end
end
end
klass = Class.new do
include Contracts::Core
Contract ({ :a => Contracts::Num, :b => String }) => nil
def something(opts)
nil
end
end
obj = klass.new
expect do
obj.something({ :a => 35, :b => "hello" })
end.to raise_error(ContractError)
expect do
obj.something({
:a => 35,
:b => "hello",
:it_is_a_hash => true
})
end.not_to raise_error
end
it "allows to override valid contract" do
Contract.override_validator(:valid) do |contract|
if contract.respond_to?(:in_valid_state?)
lambda do |arg|
contract.in_valid_state? && contract.valid?(arg)
end
else
lambda { |arg| contract.valid?(arg) }
end
end
stateful_contract = Class.new(Contracts::CallableClass) do
def initialize(contract)
@contract = contract
@state = 0
end
def in_valid_state?
@state < 3
end
def valid?(arg)
@state += 1
Contract.valid?(arg, @contract)
end
end
klass = Class.new do
include Contracts::Core
Contract stateful_contract[Contracts::Num] => Contracts::Num
def only_three_times(x)
x * x
end
end
obj = klass.new
expect(obj.only_three_times(3)).to eq(9)
expect(obj.only_three_times(3)).to eq(9)
expect(obj.only_three_times(3)).to eq(9)
expect do
obj.only_three_times(3)
end.to raise_error(ContractError)
expect do
obj.only_three_times(3)
end.to raise_error(ContractError)
end
it "allows to override class validator" do
# Make contracts accept all rspec doubles
Contract.override_validator(:class) do |contract|
lambda do |arg|
arg.is_a?(RSpec::Mocks::Double) ||
arg.is_a?(contract)
end
end
klass = Class.new do
include Contracts::Core
Contract String => String
def greet(name)
"hello, #{name}"
end
end
obj = klass.new
expect(obj.greet("world")).to eq("hello, world")
expect do
obj.greet(4)
end.to raise_error(ContractError)
expect(obj.greet(double("name"))).to match(
/hello, #\[.*Double.*"name"\]/
)
end
it "allows to override default validator" do
spy = double("spy")
Contract.override_validator(:default) do |contract|
lambda do |arg|
spy.log("#{arg} == #{contract}")
arg == contract
end
end
klass = Class.new do
include Contracts::Core
Contract 1, Contracts::Num => Contracts::Num
def gcd(_, b)
b
end
Contract Contracts::Num, Contracts::Num => Contracts::Num
def gcd(a, b)
gcd(b % a, a)
end
end
obj = klass.new
expect(spy).to receive(:log).with("8 == 1").ordered.once
expect(spy).to receive(:log).with("5 == 1").ordered.once
expect(spy).to receive(:log).with("3 == 1").ordered.once
expect(spy).to receive(:log).with("2 == 1").ordered.once
expect(spy).to receive(:log).with("1 == 1").ordered.once
obj.gcd(8, 5)
end
end
end
|