Skip to content

Commit 06c7c0c

Browse files
committed
* Update implementation & spec to be 3.0 compatible
Mainly around the breaking behaviour change about keyword arguments
1 parent 7dfb48c commit 06c7c0c

11 files changed

+63
-64
lines changed

lib/contracts.rb

+7-7
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ def initialize(klass, method, *contracts)
9393
last_contract = args_contracts.last
9494
penultimate_contract = args_contracts[-2]
9595
@has_options_contract = if @has_proc_contract
96-
penultimate_contract.is_a?(Hash) || penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
96+
penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
9797
else
98-
last_contract.is_a?(Hash) || last_contract.is_a?(Contracts::Builtin::KeywordArgs)
98+
last_contract.is_a?(Contracts::Builtin::KeywordArgs)
9999
end
100100
# ===
101101

@@ -249,12 +249,12 @@ def maybe_append_block! args, blk
249249

250250
# Same thing for when we have named params but didn't pass any in.
251251
# returns true if it appended nil
252-
def maybe_append_options! args, blk
252+
def maybe_append_options! args, kargs, blk
253253
return false unless @has_options_contract
254-
if @has_proc_contract && (args_contracts[-2].is_a?(Hash) || args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-2].is_a?(Hash)
255-
args.insert(-2, {})
256-
elsif (args_contracts[-1].is_a?(Hash) || args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-1].is_a?(Hash)
257-
args << {}
254+
if @has_proc_contract && args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)
255+
args.insert(-2, kargs)
256+
elsif args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)
257+
args << kargs
258258
end
259259
true
260260
end

lib/contracts/call_with.rb

+17-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
module Contracts
22
module CallWith
3-
def call_with(this, *args, &blk)
4-
call_with_inner(false, this, *args, &blk)
3+
def call_with(this, *args, **kargs, &blk)
4+
call_with_inner(false, this, *args, **kargs, &blk)
55
end
66

7-
def call_with_inner(returns, this, *args, &blk)
7+
def call_with_inner(returns, this, *args, **kargs, &blk)
88
args << blk if blk
99

1010
# Explicitly append blk=nil if nil != Proc contract violation anticipated
1111
nil_block_appended = maybe_append_block!(args, blk)
1212

1313
# Explicitly append options={} if Hash contract is present
14-
maybe_append_options!(args, blk)
14+
kargs_appended = maybe_append_options!(args, kargs, blk)
1515

1616
# Loop forward validating the arguments up to the splat (if there is one)
1717
(@args_contract_index || args.size).times do |i|
@@ -57,14 +57,16 @@ def call_with_inner(returns, this, *args, &blk)
5757
validator = @args_validators[args_contracts.size - 1 - j]
5858

5959
unless validator && validator[arg]
60-
return unless Contract.failure_callback(:arg => arg,
61-
:contract => contract,
62-
:class => klass,
63-
:method => method,
64-
:contracts => self,
65-
:arg_pos => i-1,
66-
:total_args => args.size,
67-
:return_value => false)
60+
return unless Contract.failure_callback({
61+
:arg => arg,
62+
:contract => contract,
63+
:class => klass,
64+
:method => method,
65+
:contracts => self,
66+
:arg_pos => i - 1,
67+
:total_args => args.size,
68+
:return_value => false,
69+
})
6870
end
6971

7072
if contract.is_a?(Contracts::Func)
@@ -76,15 +78,16 @@ def call_with_inner(returns, this, *args, &blk)
7678
# If we put the block into args for validating, restore the args
7779
# OR if we added a fake nil at the end because a block wasn't passed in.
7880
args.slice!(-1) if blk || nil_block_appended
81+
args.slice!(-1) if kargs_appended
7982
result = if method.respond_to?(:call)
8083
# proc, block, lambda, etc
81-
method.call(*args, &blk)
84+
method.call(*args, **kargs, &blk)
8285
else
8386
# original method name reference
8487
# Don't reassign blk, else Travis CI shows "stack level too deep".
8588
target_blk = blk
8689
target_blk = lambda { |*params| blk.call(*params) } if blk && blk.is_a?(Contract)
87-
method.send_to(this, *args, &target_blk)
90+
method.send_to(this, *args, **kargs, &target_blk)
8891
end
8992

9093
unless @ret_validator[result]

lib/contracts/invariants.rb

+6-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ def expected
4646
def check_on(target, method)
4747
return if target.instance_eval(&@condition)
4848

49-
self.class.failure_callback(:expected => expected,
50-
:actual => false,
51-
:target => target,
52-
:method => method)
49+
self.class.failure_callback({
50+
:expected => expected,
51+
:actual => false,
52+
:target => target,
53+
:method => method,
54+
})
5355
end
5456

5557
def self.failure_callback(data)

lib/contracts/method_handler.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def redefine_method
107107
current_engine = engine
108108

109109
# We are gonna redefine original method here
110-
method_reference.make_definition(target) do |*args, &blk|
110+
method_reference.make_definition(target) do |*args, **kargs, &blk|
111111
engine = current_engine.nearest_decorated_ancestor
112112

113113
# If we weren't able to find any ancestor that has decorated methods
@@ -130,14 +130,14 @@ def redefine_method
130130
last_error = nil
131131

132132
decorated_methods.each do |decorated_method|
133-
result = decorated_method.call_with_inner(true, self, *args, &blk)
133+
result = decorated_method.call_with_inner(true, self, *args, **kargs, &blk)
134134
return result unless result.is_a?(ParamContractError)
135135
last_error = result
136136
end
137137

138138
begin
139139
if ::Contract.failure_callback(last_error.data, false)
140-
decorated_methods.last.call_with_inner(false, self, *args, &blk)
140+
decorated_methods.last.call_with_inner(false, self, *args, **kargs, &blk)
141141
end
142142
rescue expected_error => final_error
143143
raise final_error.to_contract_error

lib/contracts/method_reference.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def make_alias(this)
3939

4040
# Calls original method on specified `this` argument with
4141
# specified arguments `args` and block `&blk`.
42-
def send_to(this, *args, &blk)
43-
this.send(aliased_name, *args, &blk)
42+
def send_to(this, *args, **kargs, &blk)
43+
this.send(aliased_name, *args, **kargs, &blk)
4444
end
4545

4646
private

spec/builtin_contracts_spec.rb

+8-12
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,6 @@ def passes(&some)
376376
fails { @o.hash_keywordargs(:hash => nil) }
377377
fails { @o.hash_keywordargs(:hash => 1) }
378378
end
379-
380-
it "should pass if a method is overloaded with non-KeywordArgs" do
381-
passes { @o.person_keywordargs("name", 10) }
382-
end
383379
end
384380

385381
describe "Optional:" do
@@ -405,15 +401,15 @@ def something(hash)
405401
end
406402

407403
context "given a fulfilled contract" do
408-
it { expect(@o.gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
409-
it { expect(@o.pretty_gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
404+
it { expect(@o.gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
405+
it { expect(@o.pretty_gives_max_value({ :panda => 1, :bamboo => 2 })).to eq(2) }
410406
end
411407

412408
context "given an unfulfilled contract" do
413-
it { fails { @o.gives_max_value(:panda => "1", :bamboo => "2") } }
409+
it { fails { @o.gives_max_value({ :panda => "1", :bamboo => "2" }) } }
414410
it { fails { @o.gives_max_value(nil) } }
415411
it { fails { @o.gives_max_value(1) } }
416-
it { fails { @o.pretty_gives_max_value(:panda => "1", :bamboo => "2") } }
412+
it { fails { @o.pretty_gives_max_value({ :panda => "1", :bamboo => "2" }) } }
417413
end
418414

419415
describe "#to_s" do
@@ -430,25 +426,25 @@ def something(hash)
430426
describe "StrictHash:" do
431427
context "when given an exact correct input" do
432428
it "does not raise an error" do
433-
passes { @o.strict_person(:name => "calvin", :age => 10) }
429+
passes { @o.strict_person({ :name => "calvin", :age => 10 }) }
434430
end
435431
end
436432

437433
context "when given an input with correct keys but wrong types" do
438434
it "raises an error" do
439-
fails { @o.strict_person(:name => "calvin", :age => "10") }
435+
fails { @o.strict_person({ :name => "calvin", :age => "10" }) }
440436
end
441437
end
442438

443439
context "when given an input with missing keys" do
444440
it "raises an error" do
445-
fails { @o.strict_person(:name => "calvin") }
441+
fails { @o.strict_person({ :name => "calvin" }) }
446442
end
447443
end
448444

449445
context "when given an input with extra keys" do
450446
it "raises an error" do
451-
fails { @o.strict_person(:name => "calvin", :age => 10, :soft => true) }
447+
fails { @o.strict_person({ :name => "calvin", :age => 10, :soft => true }) }
452448
end
453449
end
454450

spec/contracts_spec.rb

+12-9
Original file line numberDiff line numberDiff line change
@@ -349,19 +349,19 @@ def self.greeting(name)
349349

350350
describe "Hashes" do
351351
it "should pass for exact correct input" do
352-
expect { @o.person(:name => "calvin", :age => 10) }.to_not raise_error
352+
expect { @o.person({ :name => "calvin", :age => 10 }) }.to_not raise_error
353353
end
354354

355355
it "should pass even if some keys don't have contracts" do
356-
expect { @o.person(:name => "calvin", :age => 10, :foo => "bar") }.to_not raise_error
356+
expect { @o.person({ :name => "calvin", :age => 10, :foo => "bar" }) }.to_not raise_error
357357
end
358358

359359
it "should fail if a key with a contract on it isn't provided" do
360-
expect { @o.person(:name => "calvin") }.to raise_error(ContractError)
360+
expect { @o.person({ :name => "calvin" }) }.to raise_error(ContractError)
361361
end
362362

363363
it "should fail for incorrect input" do
364-
expect { @o.person(:name => 50, :age => 10) }.to raise_error(ContractError)
364+
expect { @o.person({ :name => 50, :age => 10 }) }.to raise_error(ContractError)
365365
end
366366
end
367367

@@ -612,16 +612,19 @@ def delim(match)
612612

613613
it "should contain to_s representation within a Hash contract" do
614614
expect do
615-
@o.hash_complex_contracts(:rigged => "bad")
615+
@o.hash_complex_contracts({ :rigged => "bad" })
616616
end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
617617
end
618618

619619
it "should contain to_s representation within a nested Hash contract" do
620620
expect do
621-
@o.nested_hash_complex_contracts(:rigged => true,
622-
:contents => {
623-
:kind => 0,
624-
:total => 42 })
621+
@o.nested_hash_complex_contracts({
622+
:rigged => true,
623+
:contents => {
624+
:kind => 0,
625+
:total => 42,
626+
},
627+
})
625628
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
626629
end
627630

spec/fixtures/fixtures.rb

+2-7
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,11 @@ def nested_hash_complex_contracts(data)
120120
end
121121

122122
Contract C::KeywordArgs[:name => String, :age => Fixnum] => nil
123-
def person_keywordargs(data)
124-
end
125-
126-
# Testing overloaded method
127-
Contract String, Fixnum => nil
128-
def person_keywordargs(name, age)
123+
def person_keywordargs(name: "name", age: 10)
129124
end
130125

131126
Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
132-
def hash_keywordargs(data)
127+
def hash_keywordargs(hash:)
133128
end
134129

135130
Contract (/foo/) => nil

spec/override_validators_spec.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ def something(opts)
3030
obj = klass.new
3131

3232
expect do
33-
obj.something(:a => 35, :b => "hello")
33+
obj.something({ :a => 35, :b => "hello" })
3434
end.to raise_error(ContractError)
3535

3636
expect do
37-
obj.something(
37+
obj.something({
3838
:a => 35,
3939
:b => "hello",
4040
:it_is_a_hash => true
41-
)
41+
})
4242
end.not_to raise_error
4343
end
4444

spec/ruby_version_specific/contracts_spec_2.0.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
class GenericExample
2-
Contract C::Args[String], { repeat: C::Maybe[C::Num] } => C::ArrayOf[String]
2+
Contract C::Args[String], C::KeywordArgs[ repeat: C::Maybe[C::Num] ] => C::ArrayOf[String]
33
def splat_then_optional_named(*vals, repeat: 2)
44
vals.map { |v| v * repeat }
55
end
66

7-
Contract ({foo: C::Nat}) => nil
7+
Contract C::KeywordArgs[ foo: C::Nat ] => nil
88
def nat_test_with_kwarg(foo: 10)
99
end
1010

spec/validators_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
describe "within a hash" do
3636
it "should pass for a matching string" do
37-
expect { o.hash_containing_foo(:host => "foo.example.org") }.to_not raise_error
37+
expect { o.hash_containing_foo({ :host => "foo.example.org" }) }.to_not raise_error
3838
end
3939
end
4040

0 commit comments

Comments
 (0)