contracts-lite 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +30 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rubocop.yml +131 -0
- data/.travis.yml +22 -0
- data/Gemfile +8 -4
- data/README.md +13 -3
- data/Rakefile +1 -0
- data/TUTORIAL.md +6 -6
- data/bin/console +11 -0
- data/{script → bin}/rubocop +1 -2
- data/contracts.gemspec +1 -1
- data/docs/_config.yml +1 -0
- data/docs/index.md +112 -0
- data/lib/contracts.rb +8 -210
- data/lib/contracts/args_validator.rb +96 -0
- data/lib/contracts/builtin_contracts.rb +42 -35
- data/lib/contracts/contract.rb +136 -0
- data/lib/contracts/contract/call_with.rb +119 -0
- data/lib/contracts/contract/failure_callback.rb +61 -0
- data/lib/contracts/{validators.rb → contract/validators.rb} +9 -14
- data/lib/contracts/core.rb +4 -25
- data/lib/contracts/decorators.rb +1 -1
- data/lib/contracts/engine/base.rb +4 -5
- data/lib/contracts/engine/eigenclass.rb +3 -4
- data/lib/contracts/engine/target.rb +1 -3
- data/lib/contracts/error_formatter.rb +6 -6
- data/lib/contracts/errors.rb +2 -2
- data/lib/contracts/formatters.rb +20 -21
- data/lib/contracts/invariants.rb +10 -6
- data/lib/contracts/method_handler.rb +26 -31
- data/lib/contracts/method_reference.rb +13 -14
- data/lib/contracts/support.rb +2 -16
- data/lib/contracts/version.rb +1 -1
- data/spec/contracts_spec.rb +25 -6
- data/spec/error_formatter_spec.rb +0 -1
- data/spec/fixtures/fixtures.rb +5 -5
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +17 -1
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +1 -1
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +1 -1
- data/spec/spec_helper.rb +17 -68
- metadata +15 -8
- data/TODO.markdown +0 -6
- data/lib/contracts/call_with.rb +0 -97
data/lib/contracts.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "set"
|
2
|
+
require "contracts/formatters"
|
1
3
|
require "contracts/builtin_contracts"
|
2
4
|
require "contracts/decorators"
|
3
5
|
require "contracts/errors"
|
@@ -8,10 +10,14 @@ require "contracts/method_reference"
|
|
8
10
|
require "contracts/support"
|
9
11
|
require "contracts/engine"
|
10
12
|
require "contracts/method_handler"
|
11
|
-
require "contracts/validators"
|
12
|
-
require "contracts/call_with"
|
13
13
|
require "contracts/core"
|
14
14
|
|
15
|
+
require "contracts/args_validator"
|
16
|
+
require "contracts/contract/call_with"
|
17
|
+
require "contracts/contract/validators"
|
18
|
+
require "contracts/contract/failure_callback"
|
19
|
+
require "contracts/contract"
|
20
|
+
|
15
21
|
module Contracts
|
16
22
|
def self.included(base)
|
17
23
|
base.send(:include, Core)
|
@@ -21,211 +27,3 @@ module Contracts
|
|
21
27
|
base.send(:extend, Core)
|
22
28
|
end
|
23
29
|
end
|
24
|
-
|
25
|
-
# This is the main Contract class. When you write a new contract, you'll
|
26
|
-
# write it as:
|
27
|
-
#
|
28
|
-
# Contract [contract names] => return_value
|
29
|
-
#
|
30
|
-
# This class also provides useful callbacks and a validation method.
|
31
|
-
#
|
32
|
-
# For #make_validator and related logic see file
|
33
|
-
# lib/contracts/validators.rb
|
34
|
-
# For #call_with and related logic see file
|
35
|
-
# lib/contracts/call_with.rb
|
36
|
-
class Contract < Contracts::Decorator
|
37
|
-
extend Contracts::Validators
|
38
|
-
include Contracts::CallWith
|
39
|
-
|
40
|
-
# Default implementation of failure_callback. Provided as a block to be able
|
41
|
-
# to monkey patch #failure_callback only temporary and then switch it back.
|
42
|
-
# First important usage - for specs.
|
43
|
-
DEFAULT_FAILURE_CALLBACK = proc do |data|
|
44
|
-
if data[:return_value]
|
45
|
-
# this failed on the return contract
|
46
|
-
fail ReturnContractError.new(failure_msg(data), data)
|
47
|
-
else
|
48
|
-
# this failed for a param contract
|
49
|
-
fail data[:contracts].failure_exception.new(failure_msg(data), data)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
attr_reader :args_contracts, :ret_contract, :klass, :method
|
54
|
-
def initialize(klass, method, *contracts)
|
55
|
-
unless contracts.last.is_a?(Hash)
|
56
|
-
unless contracts.one?
|
57
|
-
fail %{
|
58
|
-
It looks like your contract for #{method.name} doesn't have a return
|
59
|
-
value. A contract should be written as `Contract arg1, arg2 =>
|
60
|
-
return_value`.
|
61
|
-
}.strip
|
62
|
-
end
|
63
|
-
contracts = [nil => contracts[-1]]
|
64
|
-
end
|
65
|
-
|
66
|
-
# internally we just convert that return value syntax back to an array
|
67
|
-
@args_contracts = contracts[0, contracts.size - 1] + contracts[-1].keys
|
68
|
-
|
69
|
-
@ret_contract = contracts[-1].values[0]
|
70
|
-
|
71
|
-
@args_validators = args_contracts.map do |contract|
|
72
|
-
Contract.make_validator(contract)
|
73
|
-
end
|
74
|
-
|
75
|
-
@args_contract_index = args_contracts.index do |contract|
|
76
|
-
contract.is_a? Contracts::Args
|
77
|
-
end
|
78
|
-
|
79
|
-
@ret_validator = Contract.make_validator(ret_contract)
|
80
|
-
|
81
|
-
@pattern_match = false
|
82
|
-
|
83
|
-
# == @has_proc_contract
|
84
|
-
last_contract = args_contracts.last
|
85
|
-
is_a_proc = last_contract.is_a?(Class) && (last_contract <= Proc || last_contract <= Method)
|
86
|
-
maybe_a_proc = last_contract.is_a?(Contracts::Maybe) && last_contract.include_proc?
|
87
|
-
|
88
|
-
@has_proc_contract = is_a_proc || maybe_a_proc || last_contract.is_a?(Contracts::Func)
|
89
|
-
|
90
|
-
# ====
|
91
|
-
|
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
|
-
@klass, @method = klass, method
|
103
|
-
end
|
104
|
-
|
105
|
-
def pretty_contract c
|
106
|
-
c.is_a?(Class) ? c.name : c.class.name
|
107
|
-
end
|
108
|
-
|
109
|
-
def to_s
|
110
|
-
args = args_contracts.map { |c| pretty_contract(c) }.join(", ")
|
111
|
-
ret = pretty_contract(ret_contract)
|
112
|
-
("#{args} => #{ret}").gsub("Contracts::Builtin::", "")
|
113
|
-
end
|
114
|
-
|
115
|
-
# Given a hash, prints out a failure message.
|
116
|
-
# This function is used by the default #failure_callback method
|
117
|
-
# and uses the hash passed into the failure_callback method.
|
118
|
-
def self.failure_msg(data)
|
119
|
-
Contracts::ErrorFormatters.failure_msg(data)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Callback for when a contract fails. By default it raises
|
123
|
-
# an error and prints detailed info about the contract that
|
124
|
-
# failed. You can also monkeypatch this callback to do whatever
|
125
|
-
# you want...log the error, send you an email, print an error
|
126
|
-
# message, etc.
|
127
|
-
#
|
128
|
-
# Example of monkeypatching:
|
129
|
-
#
|
130
|
-
# def Contract.failure_callback(data)
|
131
|
-
# puts "You had an error!"
|
132
|
-
# puts failure_msg(data)
|
133
|
-
# exit
|
134
|
-
# end
|
135
|
-
def self.failure_callback(data, use_pattern_matching = true)
|
136
|
-
if data[:contracts].pattern_match? && use_pattern_matching
|
137
|
-
return DEFAULT_FAILURE_CALLBACK.call(data)
|
138
|
-
end
|
139
|
-
|
140
|
-
fetch_failure_callback.call(data)
|
141
|
-
end
|
142
|
-
|
143
|
-
# Used to override failure_callback without monkeypatching.
|
144
|
-
#
|
145
|
-
# Takes: block parameter, that should accept one argument - data.
|
146
|
-
#
|
147
|
-
# Example usage:
|
148
|
-
#
|
149
|
-
# Contract.override_failure_callback do |data|
|
150
|
-
# puts "You had an error"
|
151
|
-
# puts failure_msg(data)
|
152
|
-
# exit
|
153
|
-
# end
|
154
|
-
def self.override_failure_callback(&blk)
|
155
|
-
@failure_callback = blk
|
156
|
-
end
|
157
|
-
|
158
|
-
# Used to restore default failure callback
|
159
|
-
def self.restore_failure_callback
|
160
|
-
@failure_callback = DEFAULT_FAILURE_CALLBACK
|
161
|
-
end
|
162
|
-
|
163
|
-
def self.fetch_failure_callback
|
164
|
-
@failure_callback ||= DEFAULT_FAILURE_CALLBACK
|
165
|
-
end
|
166
|
-
|
167
|
-
# Used to verify if an argument satisfies a contract.
|
168
|
-
#
|
169
|
-
# Takes: an argument and a contract.
|
170
|
-
#
|
171
|
-
# Returns: a tuple: [Boolean, metadata]. The boolean indicates
|
172
|
-
# whether the contract was valid or not. If it wasn't, metadata
|
173
|
-
# contains some useful information about the failure.
|
174
|
-
def self.valid?(arg, contract)
|
175
|
-
make_validator(contract)[arg]
|
176
|
-
end
|
177
|
-
|
178
|
-
def [](*args, &blk)
|
179
|
-
call(*args, &blk)
|
180
|
-
end
|
181
|
-
|
182
|
-
def call(*args, &blk)
|
183
|
-
call_with(nil, *args, &blk)
|
184
|
-
end
|
185
|
-
|
186
|
-
# if we specified a proc in the contract but didn't pass one in,
|
187
|
-
# it's possible we are going to pass in a block instead. So lets
|
188
|
-
# append a nil to the list of args just so it doesn't fail.
|
189
|
-
|
190
|
-
# a better way to handle this might be to take this into account
|
191
|
-
# before throwing a "mismatched # of args" error.
|
192
|
-
# returns true if it appended nil
|
193
|
-
def maybe_append_block! args, blk
|
194
|
-
return false unless @has_proc_contract && !blk &&
|
195
|
-
(@args_contract_index || args.size < args_contracts.size)
|
196
|
-
args << nil
|
197
|
-
true
|
198
|
-
end
|
199
|
-
|
200
|
-
# Same thing for when we have named params but didn't pass any in.
|
201
|
-
# returns true if it appended nil
|
202
|
-
def maybe_append_options! args, blk
|
203
|
-
return false unless @has_options_contract
|
204
|
-
if @has_proc_contract && (args_contracts[-2].is_a?(Hash) || args_contracts[-2].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-2].is_a?(Hash)
|
205
|
-
args.insert(-2, {})
|
206
|
-
elsif (args_contracts[-1].is_a?(Hash) || args_contracts[-1].is_a?(Contracts::Builtin::KeywordArgs)) && !args[-1].is_a?(Hash)
|
207
|
-
args << {}
|
208
|
-
end
|
209
|
-
true
|
210
|
-
end
|
211
|
-
|
212
|
-
# Used to determine type of failure exception this contract should raise in case of failure
|
213
|
-
def failure_exception
|
214
|
-
if pattern_match?
|
215
|
-
PatternMatchingError
|
216
|
-
else
|
217
|
-
ParamContractError
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# @private
|
222
|
-
# Used internally to mark contract as pattern matching contract
|
223
|
-
def pattern_match!
|
224
|
-
@pattern_match = true
|
225
|
-
end
|
226
|
-
|
227
|
-
# Used to determine if contract is a pattern matching contract
|
228
|
-
def pattern_match?
|
229
|
-
@pattern_match == true
|
230
|
-
end
|
231
|
-
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Contracts
|
2
|
+
class ArgsValidator
|
3
|
+
attr_accessor :splat_args_contract_index, :klass, :method, :contracts, :args_contracts, :args_validators
|
4
|
+
def initialize(opts)
|
5
|
+
@splat_args_contract_index = opts.fetch(:splat_args_contract_index)
|
6
|
+
@klass = opts.fetch(:klass)
|
7
|
+
@method = opts.fetch(:method)
|
8
|
+
@contracts = opts.fetch(:contracts)
|
9
|
+
@args_contracts = opts.fetch(:args_contracts)
|
10
|
+
@args_validators = opts.fetch(:args_validators)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Loop forward validating the arguments up to the splat (if there is one)
|
14
|
+
# may change the `args` param
|
15
|
+
def validate_args_before_splat!(args)
|
16
|
+
(splat_args_contract_index || args.size).times do |i|
|
17
|
+
validate!(args, i)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
## possibilities
|
22
|
+
# - splat is last argument, like: def hello(a, b, *args)
|
23
|
+
# - splat is not last argument, like: def hello(*args, n)
|
24
|
+
def validate_splat_args_and_after!(args)
|
25
|
+
return unless splat_args_contract_index
|
26
|
+
from, count = splat_range(args)
|
27
|
+
validate_splat(args, from, count)
|
28
|
+
|
29
|
+
splat_upper_bound = from + count
|
30
|
+
return if splat_upper_bound == args.size
|
31
|
+
|
32
|
+
validate_rest(args, from, splat_upper_bound)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate_splat(args, from, count)
|
38
|
+
args.slice(from, count).each_with_index do |_arg, index|
|
39
|
+
arg_index = from + index
|
40
|
+
contract = args_contracts[from]
|
41
|
+
validator = args_validators[from]
|
42
|
+
validate!(args, arg_index, contract, validator)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_rest(args, from, splat_upper_bound)
|
47
|
+
args[splat_upper_bound..-1].each_with_index do |_arg, index|
|
48
|
+
arg_index = splat_upper_bound + index
|
49
|
+
contract_index = from + index + 1
|
50
|
+
contract = args_contracts[contract_index]
|
51
|
+
validator = args_validators[contract_index]
|
52
|
+
validate!(args, arg_index, contract, validator)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# string, splat[integer], float
|
57
|
+
# - "aom", 2, 3, 4, 5, 0.1 >>> 1, 4
|
58
|
+
# - "aom", 2, 0.1 >>> 1, 1
|
59
|
+
# - "aom", 2, 3, 4, 5, 6, 7, 0.1 >>> 1, 6
|
60
|
+
|
61
|
+
# splat[integer]
|
62
|
+
# - 2, 3, 4, 5 >>> 0, 4
|
63
|
+
# - 2 >>> 0, 1
|
64
|
+
# - 2, 3, 4, 5, 6, 7 >>> 0, 6
|
65
|
+
def splat_range(args)
|
66
|
+
args_after_splat = args_contracts.size - (splat_args_contract_index + 1)
|
67
|
+
in_splat = args.size - args_after_splat - splat_args_contract_index
|
68
|
+
|
69
|
+
[splat_args_contract_index, in_splat]
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate!(args, index, contract = nil, validator = nil)
|
73
|
+
arg = args[index]
|
74
|
+
contract ||= args_contracts[index]
|
75
|
+
validator ||= args_validators[index]
|
76
|
+
fail_if_invalid(validator, arg, index + 1, args.size, contract)
|
77
|
+
|
78
|
+
return unless contract.is_a?(Contracts::Func)
|
79
|
+
args[index] = Contract.new(klass, arg, *contract.contracts)
|
80
|
+
end
|
81
|
+
|
82
|
+
def fail_if_invalid(validator, arg, arg_pos, args_size, contract)
|
83
|
+
return if validator && validator.call(arg)
|
84
|
+
throw :return, Contracts::CallWith::SILENT_FAILURE unless Contract.failure_callback(
|
85
|
+
:arg => arg,
|
86
|
+
:contract => contract,
|
87
|
+
:class => klass,
|
88
|
+
:method => method,
|
89
|
+
:contracts => contracts,
|
90
|
+
:arg_pos => arg_pos,
|
91
|
+
:total_args => args_size,
|
92
|
+
:return_value => false
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require "contracts/formatters"
|
2
|
-
require "set"
|
3
|
-
|
4
|
-
# rdoc
|
5
1
|
# This module contains all the builtin contracts.
|
6
2
|
# If you want to use them, first:
|
7
3
|
#
|
@@ -22,56 +18,56 @@ module Contracts
|
|
22
18
|
module Builtin
|
23
19
|
# Check that an argument is +Numeric+.
|
24
20
|
class Num
|
25
|
-
def self.valid?
|
21
|
+
def self.valid?(val)
|
26
22
|
val.is_a? Numeric
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
30
26
|
# Check that an argument is a positive number.
|
31
27
|
class Pos
|
32
|
-
def self.valid?
|
28
|
+
def self.valid?(val)
|
33
29
|
val && val.is_a?(Numeric) && val > 0
|
34
30
|
end
|
35
31
|
end
|
36
32
|
|
37
33
|
# Check that an argument is a negative number.
|
38
34
|
class Neg
|
39
|
-
def self.valid?
|
35
|
+
def self.valid?(val)
|
40
36
|
val && val.is_a?(Numeric) && val < 0
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
44
40
|
# Check that an argument is an +Integer+.
|
45
41
|
class Int
|
46
|
-
def self.valid?
|
42
|
+
def self.valid?(val)
|
47
43
|
val && val.is_a?(Integer)
|
48
44
|
end
|
49
45
|
end
|
50
46
|
|
51
47
|
# Check that an argument is a natural number (includes zero).
|
52
48
|
class Nat
|
53
|
-
def self.valid?
|
49
|
+
def self.valid?(val)
|
54
50
|
val && val.is_a?(Integer) && val >= 0
|
55
51
|
end
|
56
52
|
end
|
57
53
|
|
58
54
|
# Check that an argument is a positive natural number (excludes zero).
|
59
55
|
class NatPos
|
60
|
-
def self.valid?
|
56
|
+
def self.valid?(val)
|
61
57
|
val && val.is_a?(Integer) && val > 0
|
62
58
|
end
|
63
59
|
end
|
64
60
|
|
65
61
|
# Passes for any argument.
|
66
62
|
class Any
|
67
|
-
def self.valid?
|
63
|
+
def self.valid?(val)
|
68
64
|
true
|
69
65
|
end
|
70
66
|
end
|
71
67
|
|
72
68
|
# Fails for any argument.
|
73
69
|
class None
|
74
|
-
def self.valid?
|
70
|
+
def self.valid?(val)
|
75
71
|
false
|
76
72
|
end
|
77
73
|
end
|
@@ -91,6 +87,15 @@ module Contracts
|
|
91
87
|
end
|
92
88
|
end
|
93
89
|
|
90
|
+
class EnumInspector
|
91
|
+
include ::Contracts::Formatters
|
92
|
+
def self.inspect(vals, last_join_word)
|
93
|
+
vals[0, vals.size-1].map do |x|
|
94
|
+
InspectWrapper.create(x)
|
95
|
+
end.join(", ") + " #{last_join_word} " + InspectWrapper.create(vals[-1]).to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
94
99
|
# Takes a variable number of contracts.
|
95
100
|
# The contract passes if any of the contracts pass.
|
96
101
|
# Example: <tt>Or[Fixnum, Float]</tt>
|
@@ -101,15 +106,13 @@ module Contracts
|
|
101
106
|
|
102
107
|
def valid?(val)
|
103
108
|
@vals.any? do |contract|
|
104
|
-
res,
|
109
|
+
res, = Contract.valid?(val, contract)
|
105
110
|
res
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
109
114
|
def to_s
|
110
|
-
@vals
|
111
|
-
InspectWrapper.create(x)
|
112
|
-
end.join(", ") + " or " + InspectWrapper.create(@vals[-1]).to_s
|
115
|
+
EnumInspector.inspect(@vals, "or")
|
113
116
|
end
|
114
117
|
end
|
115
118
|
|
@@ -123,16 +126,14 @@ module Contracts
|
|
123
126
|
|
124
127
|
def valid?(val)
|
125
128
|
results = @vals.map do |contract|
|
126
|
-
res,
|
129
|
+
res, = Contract.valid?(val, contract)
|
127
130
|
res
|
128
131
|
end
|
129
132
|
results.count(true) == 1
|
130
133
|
end
|
131
134
|
|
132
135
|
def to_s
|
133
|
-
@vals
|
134
|
-
InspectWrapper.create(x)
|
135
|
-
end.join(", ") + " xor " + InspectWrapper.create(@vals[-1]).to_s
|
136
|
+
EnumInspector.inspect(@vals, "xor")
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
@@ -146,15 +147,13 @@ module Contracts
|
|
146
147
|
|
147
148
|
def valid?(val)
|
148
149
|
@vals.all? do |contract|
|
149
|
-
res,
|
150
|
+
res, = Contract.valid?(val, contract)
|
150
151
|
res
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
154
155
|
def to_s
|
155
|
-
@vals
|
156
|
-
InspectWrapper.create(x)
|
157
|
-
end.join(", ") + " and " + InspectWrapper.create(@vals[-1]).to_s
|
156
|
+
EnumInspector.inspect(@vals, "and")
|
158
157
|
end
|
159
158
|
end
|
160
159
|
|
@@ -257,7 +256,7 @@ module Contracts
|
|
257
256
|
|
258
257
|
def valid?(val)
|
259
258
|
@vals.all? do |contract|
|
260
|
-
res,
|
259
|
+
res, = Contract.valid?(val, contract)
|
261
260
|
!res
|
262
261
|
end
|
263
262
|
end
|
@@ -282,7 +281,7 @@ module Contracts
|
|
282
281
|
def valid?(vals)
|
283
282
|
return false unless vals.is_a?(@collection_class)
|
284
283
|
vals.all? do |val|
|
285
|
-
res,
|
284
|
+
res, = Contract.valid?(val, @contract)
|
286
285
|
res
|
287
286
|
end
|
288
287
|
end
|
@@ -302,7 +301,7 @@ module Contracts
|
|
302
301
|
CollectionOf.new(@collection_class, contract)
|
303
302
|
end
|
304
303
|
|
305
|
-
|
304
|
+
alias [] new
|
306
305
|
end
|
307
306
|
end
|
308
307
|
|
@@ -321,20 +320,28 @@ module Contracts
|
|
321
320
|
# Used for <tt>*args</tt> (variadic functions). Takes a contract
|
322
321
|
# and uses it to validate every element passed in
|
323
322
|
# through <tt>*args</tt>.
|
324
|
-
# Example: <tt>
|
325
|
-
class
|
323
|
+
# Example: <tt>SplatArgs[Or[String, Num]]</tt>
|
324
|
+
class SplatArgs < CallableClass
|
326
325
|
attr_reader :contract
|
327
326
|
def initialize(contract)
|
328
327
|
@contract = contract
|
329
328
|
end
|
330
329
|
|
331
330
|
def to_s
|
332
|
-
"
|
331
|
+
"SplatArgs[#{@contract}]"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# for compatibility
|
336
|
+
class Args < SplatArgs
|
337
|
+
def initialize(contract)
|
338
|
+
puts "DEPRECATION WARNING: \nContract::Args was renamed to Contract::SplatArgs, please update your before the next major version"
|
339
|
+
@contract = contract
|
333
340
|
end
|
334
341
|
end
|
335
342
|
|
336
343
|
class Bool
|
337
|
-
def self.valid?
|
344
|
+
def self.valid?(val)
|
338
345
|
val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
339
346
|
end
|
340
347
|
end
|
@@ -361,7 +368,7 @@ module Contracts
|
|
361
368
|
# one for hash keys and one for hash values.
|
362
369
|
# Example: <tt>HashOf[Symbol, String]</tt>
|
363
370
|
class HashOf < CallableClass
|
364
|
-
INVALID_KEY_VALUE_PAIR = "You should provide only one key-value pair to HashOf contract"
|
371
|
+
INVALID_KEY_VALUE_PAIR = "You should provide only one key-value pair to HashOf contract".freeze
|
365
372
|
|
366
373
|
def initialize(key, value = nil)
|
367
374
|
if value
|
@@ -389,7 +396,7 @@ module Contracts
|
|
389
396
|
private
|
390
397
|
|
391
398
|
def validate_hash(hash)
|
392
|
-
|
399
|
+
raise ArgumentError, INVALID_KEY_VALUE_PAIR unless hash.count == 1
|
393
400
|
end
|
394
401
|
end
|
395
402
|
|
@@ -468,7 +475,7 @@ module Contracts
|
|
468
475
|
# Example: <tt>Optional[Num]</tt>
|
469
476
|
class Optional < CallableClass
|
470
477
|
UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH =
|
471
|
-
"Unable to use Optional contract outside of KeywordArgs contract"
|
478
|
+
"Unable to use Optional contract outside of KeywordArgs contract".freeze
|
472
479
|
|
473
480
|
def self._valid?(hash, key, contract)
|
474
481
|
return Contract.valid?(hash[key], contract) unless contract.is_a?(Optional)
|
@@ -505,7 +512,7 @@ module Contracts
|
|
505
512
|
|
506
513
|
def ensure_within_opt_hash
|
507
514
|
return if within_opt_hash
|
508
|
-
|
515
|
+
raise ArgumentError, UNABLE_TO_USE_OUTSIDE_OF_OPT_HASH
|
509
516
|
end
|
510
517
|
|
511
518
|
def formatted_contract
|