contracts 0.16.1 → 0.17.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/code_style_checks.yaml +2 -2
- data/.github/workflows/tests.yaml +7 -13
- data/.rubocop.yml +34 -8
- data/.rubocop_todo.yml +7 -4
- data/CHANGELOG.markdown +2 -0
- data/Gemfile +8 -4
- data/README.md +10 -2
- data/Rakefile +2 -0
- data/TUTORIAL.md +14 -14
- data/contracts.gemspec +3 -4
- data/features/builtin_contracts/keyword_args_with_optional_positional_args.feature +76 -0
- data/features/builtin_contracts/none.feature +15 -9
- data/features/support/env.rb +2 -0
- data/lib/contracts/attrs.rb +2 -0
- data/lib/contracts/builtin_contracts.rb +43 -10
- data/lib/contracts/call_with.rb +52 -30
- data/lib/contracts/core.rb +3 -1
- data/lib/contracts/decorators.rb +5 -2
- data/lib/contracts/engine/base.rb +4 -3
- data/lib/contracts/engine/eigenclass.rb +3 -2
- data/lib/contracts/engine/target.rb +2 -0
- data/lib/contracts/engine.rb +2 -0
- data/lib/contracts/errors.rb +3 -0
- data/lib/contracts/formatters.rb +17 -10
- data/lib/contracts/invariants.rb +8 -4
- data/lib/contracts/method_handler.rb +19 -9
- data/lib/contracts/method_reference.rb +4 -2
- data/lib/contracts/support.rb +4 -2
- data/lib/contracts/validators.rb +6 -2
- data/lib/contracts/version.rb +3 -1
- data/lib/contracts.rb +31 -39
- data/spec/builtin_contracts_spec.rb +9 -13
- data/spec/contracts_spec.rb +12 -9
- data/spec/fixtures/fixtures.rb +11 -16
- data/spec/override_validators_spec.rb +3 -3
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +2 -2
- data/spec/validators_spec.rb +1 -1
- metadata +11 -9
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
# Handles class and instance methods addition
|
3
5
|
# Represents single such method
|
4
6
|
class MethodHandler
|
5
7
|
METHOD_REFERENCE_FACTORY = {
|
6
8
|
:class_methods => SingletonMethodReference,
|
7
|
-
:instance_methods => MethodReference
|
9
|
+
:instance_methods => MethodReference,
|
8
10
|
}
|
9
11
|
|
10
12
|
RAW_METHOD_STRATEGY = {
|
11
13
|
:class_methods => lambda { |target, name| target.method(name) },
|
12
|
-
:instance_methods => lambda { |target, name| target.instance_method(name) }
|
14
|
+
:instance_methods => lambda { |target, name| target.instance_method(name) },
|
13
15
|
}
|
14
16
|
|
15
17
|
# Creates new instance of MethodHandler
|
@@ -78,11 +80,13 @@ module Contracts
|
|
78
80
|
|
79
81
|
def pattern_matching?
|
80
82
|
return @_pattern_matching if defined?(@_pattern_matching)
|
83
|
+
|
81
84
|
@_pattern_matching = decorated_methods.any? { |x| x.method != method_reference }
|
82
85
|
end
|
83
86
|
|
84
87
|
def mark_pattern_matching_decorators
|
85
88
|
return unless pattern_matching?
|
89
|
+
|
86
90
|
decorated_methods.each(&:pattern_match!)
|
87
91
|
end
|
88
92
|
|
@@ -107,13 +111,13 @@ module Contracts
|
|
107
111
|
current_engine = engine
|
108
112
|
|
109
113
|
# We are gonna redefine original method here
|
110
|
-
method_reference.make_definition(target) do |*args, &blk|
|
114
|
+
method_reference.make_definition(target) do |*args, **kargs, &blk|
|
111
115
|
engine = current_engine.nearest_decorated_ancestor
|
112
116
|
|
113
117
|
# If we weren't able to find any ancestor that has decorated methods
|
114
118
|
# FIXME : this looks like untested code (commenting it out doesn't make specs red)
|
115
119
|
unless engine
|
116
|
-
fail "Couldn't find decorator for method
|
120
|
+
fail "Couldn't find decorator for method #{self.class.name}:#{name}.\nDoes this method look correct to you? If you are using contracts from rspec, rspec wraps classes in it's own class.\nLook at the specs for contracts.ruby as an example of how to write contracts in this case."
|
117
121
|
end
|
118
122
|
|
119
123
|
# Fetch decorated methods out of the contracts engine
|
@@ -130,17 +134,20 @@ module Contracts
|
|
130
134
|
last_error = nil
|
131
135
|
|
132
136
|
decorated_methods.each do |decorated_method|
|
133
|
-
result = decorated_method.call_with_inner(true, self, *args, &blk)
|
137
|
+
result = decorated_method.call_with_inner(true, self, *args, **kargs, &blk)
|
134
138
|
return result unless result.is_a?(ParamContractError)
|
139
|
+
|
135
140
|
last_error = result
|
136
141
|
end
|
137
142
|
|
138
143
|
begin
|
139
|
-
if ::Contract.failure_callback(last_error
|
140
|
-
decorated_methods.last.call_with_inner(false, self, *args, &blk)
|
144
|
+
if ::Contract.failure_callback(last_error&.data, use_pattern_matching: false)
|
145
|
+
decorated_methods.last.call_with_inner(false, self, *args, **kargs, &blk)
|
141
146
|
end
|
147
|
+
# rubocop:disable Naming/RescuedExceptionsVariableName
|
142
148
|
rescue expected_error => final_error
|
143
149
|
raise final_error.to_contract_error
|
150
|
+
# rubocop:enable Naming/RescuedExceptionsVariableName
|
144
151
|
end
|
145
152
|
end
|
146
153
|
end
|
@@ -173,7 +180,8 @@ https://github.com/egonSchiele/contracts.ruby/issues
|
|
173
180
|
|
174
181
|
return if matched.empty?
|
175
182
|
|
176
|
-
fail ContractError.new(
|
183
|
+
fail ContractError.new(
|
184
|
+
%{
|
177
185
|
It looks like you are trying to use pattern-matching, but
|
178
186
|
multiple definitions for function '#{method_name}' have the same
|
179
187
|
contract for input parameters:
|
@@ -181,7 +189,9 @@ contract for input parameters:
|
|
181
189
|
#{(matched + [decorator]).map(&:to_s).join("\n")}
|
182
190
|
|
183
191
|
Each definition needs to have a different contract for the parameters.
|
184
|
-
|
192
|
+
},
|
193
|
+
{},
|
194
|
+
)
|
185
195
|
end
|
186
196
|
end
|
187
197
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
# MethodReference represents original method reference that was
|
3
5
|
# decorated by contracts.ruby. Used for instance methods.
|
@@ -39,8 +41,8 @@ module Contracts
|
|
39
41
|
|
40
42
|
# Calls original method on specified `this` argument with
|
41
43
|
# specified arguments `args` and block `&blk`.
|
42
|
-
def send_to(this, *args, &blk)
|
43
|
-
this.send(aliased_name, *args, &blk)
|
44
|
+
def send_to(this, *args, **kargs, &blk)
|
45
|
+
this.send(aliased_name, *args, **kargs, &blk)
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
data/lib/contracts/support.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Support
|
3
5
|
class << self
|
@@ -8,7 +10,7 @@ module Contracts
|
|
8
10
|
if file.nil? || line.nil?
|
9
11
|
""
|
10
12
|
else
|
11
|
-
file
|
13
|
+
"#{file}:#{line}"
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
@@ -45,7 +47,7 @@ module Contracts
|
|
45
47
|
def indent_string(string, amount)
|
46
48
|
string.gsub(
|
47
49
|
/^(?!$)/,
|
48
|
-
(string[/^[ \t]/] || " ") * amount
|
50
|
+
(string[/^[ \t]/] || " ") * amount,
|
49
51
|
)
|
50
52
|
end
|
51
53
|
|
data/lib/contracts/validators.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Contracts
|
2
4
|
module Validators
|
3
5
|
DEFAULT_VALIDATOR_STRATEGIES = {
|
@@ -9,6 +11,7 @@ module Contracts
|
|
9
11
|
Array => lambda do |contract|
|
10
12
|
lambda do |arg|
|
11
13
|
return false unless arg.is_a?(Array) && arg.length == contract.length
|
14
|
+
|
12
15
|
arg.zip(contract).all? do |_arg, _contract|
|
13
16
|
Contract.valid?(_arg, _contract)
|
14
17
|
end
|
@@ -19,6 +22,7 @@ module Contracts
|
|
19
22
|
Hash => lambda do |contract|
|
20
23
|
lambda do |arg|
|
21
24
|
return false unless arg.is_a?(Hash)
|
25
|
+
|
22
26
|
contract.keys.all? do |k|
|
23
27
|
Contract.valid?(arg[k], contract[k])
|
24
28
|
end
|
@@ -59,7 +63,7 @@ module Contracts
|
|
59
63
|
|
60
64
|
:default => lambda do |contract|
|
61
65
|
lambda { |arg| contract == arg }
|
62
|
-
end
|
66
|
+
end,
|
63
67
|
}.freeze
|
64
68
|
|
65
69
|
# Allows to override validator with custom one.
|
@@ -90,7 +94,7 @@ module Contracts
|
|
90
94
|
else
|
91
95
|
if contract.respond_to? :valid?
|
92
96
|
:valid
|
93
|
-
elsif
|
97
|
+
elsif [Class, Module].include?(klass)
|
94
98
|
:class
|
95
99
|
else
|
96
100
|
:default
|
data/lib/contracts/version.rb
CHANGED
data/lib/contracts.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "contracts/version"
|
1
4
|
require "contracts/attrs"
|
2
5
|
require "contracts/builtin_contracts"
|
3
6
|
require "contracts/decorators"
|
@@ -50,8 +53,10 @@ class Contract < Contracts::Decorator
|
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
|
-
attr_reader :args_contracts, :ret_contract, :klass, :method
|
56
|
+
attr_reader :args_contracts, :kargs_contract, :ret_contract, :klass, :method
|
57
|
+
|
54
58
|
def initialize(klass, method, *contracts)
|
59
|
+
super(klass, method)
|
55
60
|
unless contracts.last.is_a?(Hash)
|
56
61
|
unless contracts.one?
|
57
62
|
fail %{
|
@@ -65,6 +70,9 @@ class Contract < Contracts::Decorator
|
|
65
70
|
|
66
71
|
# internally we just convert that return value syntax back to an array
|
67
72
|
@args_contracts = contracts[0, contracts.size - 1] + contracts[-1].keys
|
73
|
+
# Extract contract for keyword arguments
|
74
|
+
@kargs_contract = args_contracts.find { |c| c.is_a?(Contracts::Builtin::KeywordArgs) }
|
75
|
+
args_contracts.delete(kargs_contract) if kargs_contract
|
68
76
|
|
69
77
|
@ret_contract = contracts[-1].values[0]
|
70
78
|
|
@@ -72,6 +80,8 @@ class Contract < Contracts::Decorator
|
|
72
80
|
Contract.make_validator(contract)
|
73
81
|
end
|
74
82
|
|
83
|
+
@kargs_validator = kargs_contract ? Contract.make_validator(kargs_contract) : nil
|
84
|
+
|
75
85
|
@args_contract_index = args_contracts.index do |contract|
|
76
86
|
contract.is_a? Contracts::Args
|
77
87
|
end
|
@@ -89,21 +99,11 @@ class Contract < Contracts::Decorator
|
|
89
99
|
|
90
100
|
# ====
|
91
101
|
|
92
|
-
# == @has_options_contract
|
93
|
-
last_contract = args_contracts.last
|
94
|
-
penultimate_contract = args_contracts[-2]
|
95
|
-
@has_options_contract = if @has_proc_contract
|
96
|
-
penultimate_contract.is_a?(Hash) || penultimate_contract.is_a?(Contracts::Builtin::KeywordArgs)
|
97
|
-
else
|
98
|
-
last_contract.is_a?(Hash) || last_contract.is_a?(Contracts::Builtin::KeywordArgs)
|
99
|
-
end
|
100
|
-
# ===
|
101
|
-
|
102
102
|
@klass, @method = klass, method
|
103
103
|
end
|
104
104
|
|
105
|
-
def pretty_contract
|
106
|
-
|
105
|
+
def pretty_contract contract
|
106
|
+
contract.is_a?(Class) ? contract.name : contract.class.name
|
107
107
|
end
|
108
108
|
|
109
109
|
def to_s
|
@@ -130,15 +130,15 @@ class Contract < Contracts::Decorator
|
|
130
130
|
expected_prefix = "Expected: "
|
131
131
|
expected_value = Contracts::Support.indent_string(
|
132
132
|
Contracts::Formatters::Expected.new(data[:contract]).contract.pretty_inspect,
|
133
|
-
expected_prefix.length
|
133
|
+
expected_prefix.length,
|
134
134
|
).strip
|
135
|
-
expected_line = expected_prefix
|
135
|
+
expected_line = "#{expected_prefix}#{expected_value},"
|
136
136
|
|
137
137
|
# Actual
|
138
138
|
actual_prefix = "Actual: "
|
139
139
|
actual_value = Contracts::Support.indent_string(
|
140
140
|
data[:arg].pretty_inspect,
|
141
|
-
actual_prefix.length
|
141
|
+
actual_prefix.length,
|
142
142
|
).strip
|
143
143
|
actual_line = actual_prefix + actual_value
|
144
144
|
|
@@ -157,16 +157,19 @@ class Contract < Contracts::Decorator
|
|
157
157
|
position_value = Contracts::Support.method_position(data[:method])
|
158
158
|
position_line = position_prefix + position_value
|
159
159
|
|
160
|
-
|
161
|
-
|
160
|
+
[
|
161
|
+
header,
|
162
162
|
Contracts::Support.indent_string(
|
163
|
-
[
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
163
|
+
[
|
164
|
+
expected_line,
|
165
|
+
actual_line,
|
166
|
+
value_line,
|
167
|
+
contract_line,
|
168
|
+
position_line,
|
169
|
+
].join("\n"),
|
170
|
+
indent_amount,
|
171
|
+
),
|
172
|
+
].join("\n")
|
170
173
|
end
|
171
174
|
|
172
175
|
# Callback for when a contract fails. By default it raises
|
@@ -182,7 +185,7 @@ class Contract < Contracts::Decorator
|
|
182
185
|
# puts failure_msg(data)
|
183
186
|
# exit
|
184
187
|
# end
|
185
|
-
def self.failure_callback(data, use_pattern_matching
|
188
|
+
def self.failure_callback(data, use_pattern_matching: true)
|
186
189
|
if data[:contracts].pattern_match? && use_pattern_matching
|
187
190
|
return DEFAULT_FAILURE_CALLBACK.call(data)
|
188
191
|
end
|
@@ -242,20 +245,9 @@ class Contract < Contracts::Decorator
|
|
242
245
|
# returns true if it appended nil
|
243
246
|
def maybe_append_block! args, blk
|
244
247
|
return false unless @has_proc_contract && !blk &&
|
245
|
-
|
246
|
-
args << nil
|
247
|
-
true
|
248
|
-
end
|
248
|
+
(@args_contract_index || args.size < args_contracts.size)
|
249
249
|
|
250
|
-
|
251
|
-
# returns true if it appended nil
|
252
|
-
def maybe_append_options! args, blk
|
253
|
-
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 << {}
|
258
|
-
end
|
250
|
+
args << nil
|
259
251
|
true
|
260
252
|
end
|
261
253
|
|
@@ -30,7 +30,7 @@ RSpec.describe "Contracts:" do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
describe "Num:" do
|
33
|
-
it "should pass for
|
33
|
+
it "should pass for Integers" do
|
34
34
|
passes { @o.double(2) }
|
35
35
|
end
|
36
36
|
|
@@ -376,10 +376,6 @@ RSpec.describe "Contracts:" do
|
|
376
376
|
fails { @o.hash_keywordargs(:hash => nil) }
|
377
377
|
fails { @o.hash_keywordargs(:hash => 1) }
|
378
378
|
end
|
379
|
-
|
380
|
-
it "should pass if a method is overloaded with non-KeywordArgs" do
|
381
|
-
passes { @o.person_keywordargs("name", 10) }
|
382
|
-
end
|
383
379
|
end
|
384
380
|
|
385
381
|
describe "Optional:" do
|
@@ -405,15 +401,15 @@ RSpec.describe "Contracts:" do
|
|
405
401
|
end
|
406
402
|
|
407
403
|
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) }
|
410
406
|
end
|
411
407
|
|
412
408
|
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" }) } }
|
414
410
|
it { fails { @o.gives_max_value(nil) } }
|
415
411
|
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" }) } }
|
417
413
|
end
|
418
414
|
|
419
415
|
describe "#to_s" do
|
@@ -430,25 +426,25 @@ RSpec.describe "Contracts:" do
|
|
430
426
|
describe "StrictHash:" do
|
431
427
|
context "when given an exact correct input" do
|
432
428
|
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 }) }
|
434
430
|
end
|
435
431
|
end
|
436
432
|
|
437
433
|
context "when given an input with correct keys but wrong types" do
|
438
434
|
it "raises an error" do
|
439
|
-
fails { @o.strict_person(:name => "calvin", :age => "10") }
|
435
|
+
fails { @o.strict_person({ :name => "calvin", :age => "10" }) }
|
440
436
|
end
|
441
437
|
end
|
442
438
|
|
443
439
|
context "when given an input with missing keys" do
|
444
440
|
it "raises an error" do
|
445
|
-
fails { @o.strict_person(:name => "calvin") }
|
441
|
+
fails { @o.strict_person({ :name => "calvin" }) }
|
446
442
|
end
|
447
443
|
end
|
448
444
|
|
449
445
|
context "when given an input with extra keys" do
|
450
446
|
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 }) }
|
452
448
|
end
|
453
449
|
end
|
454
450
|
|
data/spec/contracts_spec.rb
CHANGED
@@ -349,19 +349,19 @@ RSpec.describe "Contracts:" do
|
|
349
349
|
|
350
350
|
describe "Hashes" do
|
351
351
|
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
|
353
353
|
end
|
354
354
|
|
355
355
|
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
|
357
357
|
end
|
358
358
|
|
359
359
|
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)
|
361
361
|
end
|
362
362
|
|
363
363
|
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)
|
365
365
|
end
|
366
366
|
end
|
367
367
|
|
@@ -612,16 +612,19 @@ RSpec.describe "Contracts:" do
|
|
612
612
|
|
613
613
|
it "should contain to_s representation within a Hash contract" do
|
614
614
|
expect do
|
615
|
-
@o.hash_complex_contracts(:rigged => "bad")
|
615
|
+
@o.hash_complex_contracts({ :rigged => "bad" })
|
616
616
|
end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
|
617
617
|
end
|
618
618
|
|
619
619
|
it "should contain to_s representation within a nested Hash contract" do
|
620
620
|
expect do
|
621
|
-
@o.nested_hash_complex_contracts(
|
622
|
-
|
623
|
-
|
624
|
-
|
621
|
+
@o.nested_hash_complex_contracts({
|
622
|
+
:rigged => true,
|
623
|
+
:contents => {
|
624
|
+
:kind => 0,
|
625
|
+
:total => 42,
|
626
|
+
},
|
627
|
+
})
|
625
628
|
end.to raise_error(ContractError, not_s(delim "String or Symbol"))
|
626
629
|
end
|
627
630
|
|
data/spec/fixtures/fixtures.rb
CHANGED
@@ -100,11 +100,11 @@ class GenericExample
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
Contract ({ :name => String, :age =>
|
103
|
+
Contract ({ :name => String, :age => Integer }) => nil
|
104
104
|
def person(data)
|
105
105
|
end
|
106
106
|
|
107
|
-
Contract C::StrictHash[{ :name => String, :age =>
|
107
|
+
Contract C::StrictHash[{ :name => String, :age => Integer }] => nil
|
108
108
|
def strict_person(data)
|
109
109
|
end
|
110
110
|
|
@@ -119,17 +119,12 @@ class GenericExample
|
|
119
119
|
def nested_hash_complex_contracts(data)
|
120
120
|
end
|
121
121
|
|
122
|
-
Contract C::KeywordArgs[:name => String, :age =>
|
123
|
-
def person_keywordargs(
|
124
|
-
end
|
125
|
-
|
126
|
-
# Testing overloaded method
|
127
|
-
Contract String, Fixnum => nil
|
128
|
-
def person_keywordargs(name, age)
|
122
|
+
Contract C::KeywordArgs[:name => String, :age => Integer] => nil
|
123
|
+
def person_keywordargs(name: "name", age: 10)
|
129
124
|
end
|
130
125
|
|
131
126
|
Contract C::KeywordArgs[:hash => C::HashOf[Symbol, C::Num]] => nil
|
132
|
-
def hash_keywordargs(
|
127
|
+
def hash_keywordargs(hash:)
|
133
128
|
end
|
134
129
|
|
135
130
|
Contract (/foo/) => nil
|
@@ -534,30 +529,30 @@ class MyBirthday
|
|
534
529
|
@month = month
|
535
530
|
end
|
536
531
|
|
537
|
-
Contract C::None =>
|
532
|
+
Contract C::None => Integer
|
538
533
|
def silly_next_day!
|
539
534
|
self.day += 1
|
540
535
|
end
|
541
536
|
|
542
|
-
Contract C::None =>
|
537
|
+
Contract C::None => Integer
|
543
538
|
def silly_next_month!
|
544
539
|
self.month += 1
|
545
540
|
end
|
546
541
|
|
547
|
-
Contract C::None =>
|
542
|
+
Contract C::None => Integer
|
548
543
|
def clever_next_day!
|
549
544
|
return clever_next_month! if day == 31
|
550
545
|
self.day += 1
|
551
546
|
end
|
552
547
|
|
553
|
-
Contract C::None =>
|
548
|
+
Contract C::None => Integer
|
554
549
|
def clever_next_month!
|
555
550
|
return next_year! if month == 12
|
556
551
|
self.month += 1
|
557
552
|
self.day = 1
|
558
553
|
end
|
559
554
|
|
560
|
-
Contract C::None =>
|
555
|
+
Contract C::None => Integer
|
561
556
|
def next_year!
|
562
557
|
self.month = 1
|
563
558
|
self.day = 1
|
@@ -615,7 +610,7 @@ with_enabled_no_contracts do
|
|
615
610
|
body + "!"
|
616
611
|
end
|
617
612
|
|
618
|
-
Contract
|
613
|
+
Contract Integer, String => String
|
619
614
|
def on_response(status, body)
|
620
615
|
"error #{status}: #{body}"
|
621
616
|
end
|
@@ -30,15 +30,15 @@ RSpec.describe Contract do
|
|
30
30
|
obj = klass.new
|
31
31
|
|
32
32
|
expect do
|
33
|
-
obj.something(:a => 35, :b => "hello")
|
33
|
+
obj.something({ :a => 35, :b => "hello" })
|
34
34
|
end.to raise_error(ContractError)
|
35
35
|
|
36
36
|
expect do
|
37
|
-
obj.something(
|
37
|
+
obj.something({
|
38
38
|
:a => 35,
|
39
39
|
:b => "hello",
|
40
40
|
:it_is_a_hash => true
|
41
|
-
)
|
41
|
+
})
|
42
42
|
end.not_to raise_error
|
43
43
|
end
|
44
44
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class GenericExample
|
2
|
-
Contract C::Args[String],
|
2
|
+
Contract C::Args[String], C::KeywordArgs[ repeat: C::Maybe[C::Num] ] => C::ArrayOf[String]
|
3
3
|
def splat_then_optional_named(*vals, repeat: 2)
|
4
4
|
vals.map { |v| v * repeat }
|
5
5
|
end
|
6
6
|
|
7
|
-
Contract
|
7
|
+
Contract C::KeywordArgs[ foo: C::Nat ] => nil
|
8
8
|
def nat_test_with_kwarg(foo: 10)
|
9
9
|
end
|
10
10
|
|
data/spec/validators_spec.rb
CHANGED
@@ -34,7 +34,7 @@ RSpec.describe "Contract validators" do
|
|
34
34
|
|
35
35
|
describe "within a hash" do
|
36
36
|
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
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contracts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.17.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aditya Bhargava
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This library provides contracts for Ruby. Contracts let you clearly express
|
14
14
|
how your code behaves, and free you from writing tons of boilerplate, defensive
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- features/builtin_contracts/hash_of.feature
|
57
57
|
- features/builtin_contracts/int.feature
|
58
58
|
- features/builtin_contracts/keyword_args.feature
|
59
|
+
- features/builtin_contracts/keyword_args_with_optional_positional_args.feature
|
59
60
|
- features/builtin_contracts/maybe.feature
|
60
61
|
- features/builtin_contracts/nat.feature
|
61
62
|
- features/builtin_contracts/nat_pos.feature
|
@@ -111,9 +112,7 @@ homepage: https://github.com/egonSchiele/contracts.ruby
|
|
111
112
|
licenses:
|
112
113
|
- BSD-2-Clause
|
113
114
|
metadata: {}
|
114
|
-
post_install_message:
|
115
|
-
frozen (only fixes will be released)\n For Ruby 3.x use 0.17.x or later (might
|
116
|
-
not be released yet)\n "
|
115
|
+
post_install_message:
|
117
116
|
rdoc_options: []
|
118
117
|
require_paths:
|
119
118
|
- lib
|
@@ -121,15 +120,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
120
|
requirements:
|
122
121
|
- - ">="
|
123
122
|
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
123
|
+
version: '3.0'
|
124
|
+
- - "<"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '4'
|
125
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
128
|
requirements:
|
127
129
|
- - ">="
|
128
130
|
- !ruby/object:Gem::Version
|
129
131
|
version: '0'
|
130
132
|
requirements: []
|
131
|
-
rubygems_version: 3.
|
132
|
-
signing_key:
|
133
|
+
rubygems_version: 3.4.10
|
134
|
+
signing_key:
|
133
135
|
specification_version: 4
|
134
136
|
summary: Contracts for Ruby.
|
135
137
|
test_files: []
|