contracts 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +9 -1
- data/TUTORIAL.md +51 -34
- data/lib/contracts.rb +10 -38
- data/lib/contracts/builtin_contracts.rb +16 -0
- data/lib/contracts/call_with.rb +3 -2
- data/lib/contracts/core.rb +45 -0
- data/lib/contracts/validators.rb +6 -0
- data/lib/contracts/version.rb +1 -1
- data/spec/builtin_contracts_spec.rb +36 -4
- data/spec/contracts_spec.rb +12 -2
- data/spec/fixtures/fixtures.rb +103 -84
- data/spec/module_spec.rb +2 -2
- data/spec/override_validators_spec.rb +4 -4
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +2 -2
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +2 -2
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +4 -4
- data/spec/validators_spec.rb +25 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 588796ed6d26a1394810a86debd6f0b285dfb7e1
|
4
|
+
data.tar.gz: bff5d90964ae5bb9911b66236c5898ad741c159b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 678e7911e3dee12d273a8839712583bd0cc1272bde6da8aa8bdc27685751a66c9c9a25c39b1182c6056ff1d8bce191b8982783a18abadeff2526501040f61133
|
7
|
+
data.tar.gz: 5c8b7cd54ebff1b8d9d75393a2c1f0d45df096c295c3387eefd54a1a01695afe8fe1ef4975bc09705d1a6d081761fe42ffb8512d926df6cc0104e10f764844c6
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## v0.11.0
|
2
|
+
- Enhancement: add `include Contracts::Core` that doesn't pollute the namespace as much as `include Contracts` - [Oleksii Federov](https://github.com/waterlink) [#185](https://github.com/egonSchiele/contracts.ruby/pull/185)
|
3
|
+
- Bugfix: fail if a non-hash is provided to a `HashOf` contract - [Abe Voelker](https://github.com/abevoelker) [#190](https://github.com/egonSchiele/contracts.ruby/pull/190)
|
4
|
+
- Bugfix: bugfix for using varargs and `Maybe[Proc]` together - [Adit Bhargava](https://github.com/egonSchiele) [#188](https://github.com/egonSchiele/contracts.ruby/pull/188)
|
5
|
+
- Bugfix: make KeywordArgs fail if unexpected keys are passed in - [Abe Voelker](https://github.com/abevoelker) [#187](https://github.com/egonSchiele/contracts.ruby/pull/187)
|
6
|
+
- Feature: range contract added - [Oleksii Fedorov](https://github.com/waterlink) [#184](https://github.com/egonSchiele/contracts.ruby/pull/184)
|
7
|
+
- Feature: enum contract added - [Dennis Günnewig](https://github.com/dg-ratiodata) [#181](https://github.com/egonSchiele/contracts.ruby/pull/181)
|
8
|
+
|
1
9
|
## v0.10.1
|
2
10
|
|
3
11
|
- Enhancement: make `@pattern_match` instance variable not render ruby warning. Required to use new aruba versions in rspec tests - [Dennis Günnewig](https://github.com/dg-ratiodata) [#179](https://github.com/egonSchiele/contracts.ruby/pull/179)
|
@@ -18,7 +26,7 @@
|
|
18
26
|
- MAJOR fix in pattern-matching: If the return contract for a pattern-matched function fails, it should NOT try the next pattern-match function. Pattern-matching is only for params, not return values.
|
19
27
|
- raise an error if multiple defns have the same contract for pattern matching.
|
20
28
|
|
21
|
-
- New syntax for functions with no input params (the old style still works)
|
29
|
+
- New syntax for functions with no input params (the old style still works)
|
22
30
|
Old way:
|
23
31
|
```ruby
|
24
32
|
Contract nil => 1
|
data/TUTORIAL.md
CHANGED
@@ -17,7 +17,7 @@ contracts.ruby brings code contracts to the Ruby language. Code contracts allow
|
|
17
17
|
A simple example:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
Contract Num, Num => Num
|
20
|
+
Contract Contracts::Num, Contracts::Num => Contracts::Num
|
21
21
|
def add(a, b)
|
22
22
|
a + b
|
23
23
|
end
|
@@ -31,9 +31,9 @@ Copy this code into a file and run it:
|
|
31
31
|
require 'contracts'
|
32
32
|
|
33
33
|
class Math
|
34
|
-
include Contracts
|
34
|
+
include Contracts::Core
|
35
35
|
|
36
|
-
Contract Num, Num => Num
|
36
|
+
Contract Contracts::Num, Contracts::Num => Contracts::Num
|
37
37
|
def self.add(a, b)
|
38
38
|
a + b
|
39
39
|
end
|
@@ -88,6 +88,7 @@ contracts.ruby comes with a lot of built-in contracts, including the following:
|
|
88
88
|
* [`SetOf`](http://www.rubydoc.info/gems/contracts/Contracts/SetOf) – checks that the argument is a set, and all elements pass the given contract, e.g. `SetOf[Num]`
|
89
89
|
* [`HashOf`](http://www.rubydoc.info/gems/contracts/Contracts/HashOf) – checks that the argument is a hash, and all keys and values pass the given contract, e.g. `HashOf[Symbol => String]` or `HashOf[Symbol,String]`
|
90
90
|
* [`RangeOf`](http://www.rubydoc.info/gems/contracts/Contracts/RangeOf) – checks that the argument is a range whose elements (#first and #last) pass the given contract, e.g. `RangeOf[Date]`
|
91
|
+
* [`Enum`](http://www.rubydoc.info/gems/contracts/Contracts/Enum) – checks that the argument is part of a given collection of objects, e.g. `Enum[:a, :b, :c]`
|
91
92
|
|
92
93
|
* Keyword arguments
|
93
94
|
* [`KeywordArgs`](http://www.rubydoc.info/gems/contracts/Contracts/KeywordArgs) – checks that the argument is an options hash, and all required keyword arguments are present, and all values pass their respective contracts, e.g. `KeywordArgs[:number => Num, :description => Optional[String]]`
|
@@ -104,6 +105,21 @@ contracts.ruby comes with a lot of built-in contracts, including the following:
|
|
104
105
|
|
105
106
|
To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
|
106
107
|
|
108
|
+
It is recommended to use shortcut for referring builtin contracts:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# define shortcut somewhere at the top level of your codebase:
|
112
|
+
C = Contracts
|
113
|
+
|
114
|
+
# and use it:
|
115
|
+
Contract C::Maybe[C::Num], String => C::Num
|
116
|
+
```
|
117
|
+
|
118
|
+
Shortcut name should not be necessary `C`, can be anything that you are comfort
|
119
|
+
with while typing and anything that does not conflict with libraries you use.
|
120
|
+
|
121
|
+
All examples after this point assume you have chosen a shortcut as `C::`.
|
122
|
+
|
107
123
|
## More Examples
|
108
124
|
|
109
125
|
### Hello, World
|
@@ -126,7 +142,7 @@ You always need to specify a contract for the return value. In this example, `he
|
|
126
142
|
### A Double Function
|
127
143
|
|
128
144
|
```ruby
|
129
|
-
Contract Or[Fixnum, Float] => Or[Fixnum, Float]
|
145
|
+
Contract C::Or[Fixnum, Float] => C::Or[Fixnum, Float]
|
130
146
|
def double(x)
|
131
147
|
2 * x
|
132
148
|
end
|
@@ -136,19 +152,19 @@ Sometimes you want to be able to choose between a few contracts. `Or` takes a va
|
|
136
152
|
This introduces some new syntax. One of the valid values for a contract is an instance of a class that responds to the `valid?` method. This is what `Or[Fixnum, Float]` is. The longer way to write it would have been:
|
137
153
|
|
138
154
|
```ruby
|
139
|
-
Contract Or.new(Fixnum, Float) => Or.new(Fixnum, Float)
|
155
|
+
Contract C::Or.new(Fixnum, Float) => C::Or.new(Fixnum, Float)
|
140
156
|
```
|
141
157
|
|
142
158
|
All the built-in contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
|
143
159
|
|
144
160
|
```ruby
|
145
|
-
Contract Or[Fixnum, Float] => Or[Fixnum, Float]
|
161
|
+
Contract C::Or[Fixnum, Float] => C::Or[Fixnum, Float]
|
146
162
|
```
|
147
163
|
|
148
164
|
or
|
149
165
|
|
150
166
|
```ruby
|
151
|
-
Contract Or.new(Fixnum, Float) => Or.new(Fixnum, Float)
|
167
|
+
Contract C::Or.new(Fixnum, Float) => C::Or.new(Fixnum, Float)
|
152
168
|
```
|
153
169
|
|
154
170
|
whichever you prefer. They both mean the same thing here: make a new instance of `Or` with `Fixnum` and `Float`. Use that instance to validate the argument.
|
@@ -156,7 +172,7 @@ whichever you prefer. They both mean the same thing here: make a new instance of
|
|
156
172
|
### A Product Function
|
157
173
|
|
158
174
|
```ruby
|
159
|
-
Contract ArrayOf[Num] => Num
|
175
|
+
Contract C::ArrayOf[C::Num] => C::Num
|
160
176
|
def product(vals)
|
161
177
|
total = 1
|
162
178
|
vals.each do |val|
|
@@ -179,7 +195,7 @@ product([1, 2, 3, "foo"])
|
|
179
195
|
### Another Product Function
|
180
196
|
|
181
197
|
```ruby
|
182
|
-
Contract Args[Num] => Num
|
198
|
+
Contract C::Args[C::Num] => C::Num
|
183
199
|
def product(*vals)
|
184
200
|
total = 1
|
185
201
|
vals.each do |val|
|
@@ -203,7 +219,7 @@ If an array is one of the arguments and you know how many elements it's going to
|
|
203
219
|
|
204
220
|
```ruby
|
205
221
|
# a function that takes an array of two elements...a person's age and a person's name.
|
206
|
-
Contract [Num, String] => nil
|
222
|
+
Contract [C::Num, String] => nil
|
207
223
|
def person(data)
|
208
224
|
p data
|
209
225
|
end
|
@@ -217,7 +233,7 @@ Here's a contract that requires a Hash. We can put contracts on each of the keys
|
|
217
233
|
|
218
234
|
```ruby
|
219
235
|
# note the parentheses around the hash; without those you would get a syntax error
|
220
|
-
Contract ({ :age => Num, :name => String }) => nil
|
236
|
+
Contract ({ :age => C::Num, :name => String }) => nil
|
221
237
|
def person(data)
|
222
238
|
p data
|
223
239
|
end
|
@@ -241,7 +257,7 @@ even though we don't specify a type for `:foo`.
|
|
241
257
|
Peruse this contract on the keys and values of a Hash.
|
242
258
|
|
243
259
|
```ruby
|
244
|
-
Contract HashOf[Symbol, Num] => Num
|
260
|
+
Contract C::HashOf[Symbol, C::Num] => C::Num
|
245
261
|
def give_largest_value(hsh)
|
246
262
|
hsh.values.max
|
247
263
|
end
|
@@ -269,14 +285,14 @@ def connect(host, port:, user:, password:)
|
|
269
285
|
You can of course put `Hash` contract on it:
|
270
286
|
|
271
287
|
```ruby
|
272
|
-
Contract String, { :port => Num, :user => String, :password => String } => Connection
|
288
|
+
Contract String, { :port => C::Num, :user => String, :password => String } => Connection
|
273
289
|
def connect(host, port:, user:, password:)
|
274
290
|
```
|
275
291
|
|
276
292
|
But this will not quite work if you want to have a default values:
|
277
293
|
|
278
294
|
```ruby
|
279
|
-
Contract String, { :port => Num, :user => String, :password => String } => Connection
|
295
|
+
Contract String, { :port => C::Num, :user => String, :password => String } => Connection
|
280
296
|
def connect(host, port: 5000, user:, password:)
|
281
297
|
# ...
|
282
298
|
end
|
@@ -296,13 +312,13 @@ ContractError: Contract violation for argument 2 of 2:
|
|
296
312
|
At: (irb):12
|
297
313
|
```
|
298
314
|
|
299
|
-
This can be fixed with contract `{ :port => Maybe[Num], ... }`, but that will
|
315
|
+
This can be fixed with contract `{ :port => C::Maybe[C::Num], ... }`, but that will
|
300
316
|
allow `nil` to be passed in, which is not the original intent.
|
301
317
|
|
302
318
|
So that is where `KeywordArgs` and `Optional` contracts jump in:
|
303
319
|
|
304
320
|
```ruby
|
305
|
-
Contract String, KeywordArgs[ :port => Optional[Num], :user => String, :password => String ] => Connection
|
321
|
+
Contract String, C::KeywordArgs[ :port => C::Optional[C::Num], :user => String, :password => String ] => Connection
|
306
322
|
def connect(host, port: 5000, user:, password:)
|
307
323
|
```
|
308
324
|
|
@@ -319,7 +335,7 @@ def map(arr, func)
|
|
319
335
|
`map` takes an array, and a function. Suppose you want to add a contract to this function. You could try this:
|
320
336
|
|
321
337
|
```ruby
|
322
|
-
Contract ArrayOf[Any], Proc => ArrayOf[Any]
|
338
|
+
Contract C::ArrayOf[C::Any], Proc => C::ArrayOf[C::Any]
|
323
339
|
def map(arr, func)
|
324
340
|
```
|
325
341
|
|
@@ -334,7 +350,7 @@ But suppose you want to have a contract on the Proc too! Suppose you want to mak
|
|
334
350
|
Here's a `map` function that requires an array of numbers, and a function that takes a number and returns a number:
|
335
351
|
|
336
352
|
```ruby
|
337
|
-
Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]
|
353
|
+
Contract C::ArrayOf[C::Num], C::Func[C::Num => C::Num] => C::ArrayOf[C::Num]
|
338
354
|
def map(arr, func)
|
339
355
|
ret = []
|
340
356
|
arr.each do |x|
|
@@ -362,7 +378,7 @@ def map(arr, &block)
|
|
362
378
|
NOTE: This is not valid:
|
363
379
|
|
364
380
|
```ruby
|
365
|
-
Contract ArrayOf[Num], Func => ArrayOf[Num]
|
381
|
+
Contract C::ArrayOf[C::Num], C::Func => C::ArrayOf[C::Num]
|
366
382
|
def map(arr, &func)
|
367
383
|
```
|
368
384
|
|
@@ -372,7 +388,7 @@ Here I am using `Func` without specifying a contract, like `Func[Num => Num]`. T
|
|
372
388
|
Treat the return value as an array. For example, here's a function that returns two numbers:
|
373
389
|
|
374
390
|
```ruby
|
375
|
-
Contract Num => [Num, Num]
|
391
|
+
Contract C::Num => [C::Num, C::Num]
|
376
392
|
def mult(x)
|
377
393
|
return x, x+1
|
378
394
|
end
|
@@ -383,7 +399,7 @@ end
|
|
383
399
|
If you use a contract a lot, it's a good idea to give it a meaningful synonym that tells the reader more about what your code returns. For example, suppose you have many functions that return a `Hash` or `nil`. If a `Hash` is returned, it contains information about a person. Your contact might look like this:
|
384
400
|
|
385
401
|
```ruby
|
386
|
-
Contract String => Or[Hash, nil]
|
402
|
+
Contract String => C::Or[Hash, nil]
|
387
403
|
def some_func(str)
|
388
404
|
```
|
389
405
|
|
@@ -415,7 +431,7 @@ The first two don't need any extra work to define: you can just use any constant
|
|
415
431
|
### A Proc
|
416
432
|
|
417
433
|
```ruby
|
418
|
-
Contract lambda { |x| x.is_a? Numeric } => Num
|
434
|
+
Contract lambda { |x| x.is_a? Numeric } => C::Num
|
419
435
|
def double(x)
|
420
436
|
```
|
421
437
|
|
@@ -460,7 +476,7 @@ The `Or` contract takes a sequence of contracts, and passes if any of them pass.
|
|
460
476
|
This class inherits from `CallableClass`, which allows us to use `[]` when using the class:
|
461
477
|
|
462
478
|
```ruby
|
463
|
-
Contract Or[Fixnum, Float] => Num
|
479
|
+
Contract C::Or[Fixnum, Float] => C::Num
|
464
480
|
def double(x)
|
465
481
|
2 * x
|
466
482
|
end
|
@@ -469,7 +485,7 @@ end
|
|
469
485
|
Without `CallableClass`, we would have to use `.new` instead:
|
470
486
|
|
471
487
|
```ruby
|
472
|
-
Contract Or.new(Fixnum, Float) => Num
|
488
|
+
Contract C::Or.new(Fixnum, Float) => C::Num
|
473
489
|
def double(x)
|
474
490
|
# etc
|
475
491
|
```
|
@@ -543,10 +559,11 @@ Possible validator overrides:
|
|
543
559
|
|
544
560
|
- `override_validator(MyCustomContract)` - allows to add some special behaviour for custom contracts,
|
545
561
|
- `override_validator(Proc)` - e.g. `lambda { true }`,
|
546
|
-
- `override_validator(Array)` - e.g. `[Num, String]`,
|
547
|
-
- `override_validator(Hash)` - e.g. `{ :a => Num, :b => String }`,
|
548
|
-
- `override_validator(
|
549
|
-
- `override_validator(Contracts::
|
562
|
+
- `override_validator(Array)` - e.g. `[C::Num, String]`,
|
563
|
+
- `override_validator(Hash)` - e.g. `{ :a => C::Num, :b => String }`,
|
564
|
+
- `override_validator(Range)` - e.g. `(1..10)`,
|
565
|
+
- `override_validator(Contracts::Args)` - e.g. `C::Args[C::Num]`,
|
566
|
+
- `override_validator(Contracts::Func)` - e.g. `C::Func[C::Num => C::Num]`,
|
550
567
|
- `override_validator(:valid)` - allows to override how contracts that respond to `:valid?` are handled,
|
551
568
|
- `override_validator(:class)` - allows to override how class/module contract constants are handled,
|
552
569
|
- `override_validator(:default)` - otherwise, raw value contracts.
|
@@ -564,7 +581,7 @@ You can use contracts for method overloading! This is commonly called "pattern m
|
|
564
581
|
For example, here's a factorial function without method overloading:
|
565
582
|
|
566
583
|
```ruby
|
567
|
-
Contract Num => Num
|
584
|
+
Contract C::Num => C::Num
|
568
585
|
def fact x
|
569
586
|
if x == 1
|
570
587
|
x
|
@@ -582,7 +599,7 @@ def fact x
|
|
582
599
|
x
|
583
600
|
end
|
584
601
|
|
585
|
-
Contract Num => Num
|
602
|
+
Contract C::Num => C::Num
|
586
603
|
def fact x
|
587
604
|
x * fact(x - 1)
|
588
605
|
end
|
@@ -608,7 +625,7 @@ end
|
|
608
625
|
Note that the second `get_ticket` contract above could have been simplified to:
|
609
626
|
|
610
627
|
```ruby
|
611
|
-
Contract Num => Ticket
|
628
|
+
Contract C::Num => Ticket
|
612
629
|
```
|
613
630
|
|
614
631
|
This is because the first contract eliminated the possibility of `age` being less than 12. However, the simpler contract is less explicit; you may want to "spell out" the age condition for clarity, especially if the method is overloaded with many contracts.
|
@@ -619,7 +636,7 @@ Usage is the same as contracts in classes:
|
|
619
636
|
|
620
637
|
```ruby
|
621
638
|
module M
|
622
|
-
include Contracts
|
639
|
+
include Contracts::Core
|
623
640
|
|
624
641
|
Contract String => String
|
625
642
|
def self.parse
|
@@ -638,13 +655,13 @@ A simple example:
|
|
638
655
|
|
639
656
|
```ruby
|
640
657
|
class MyBirthday < Struct.new(:day, :month)
|
641
|
-
include Contracts
|
658
|
+
include Contracts::Core
|
642
659
|
include Contracts::Invariants
|
643
660
|
|
644
661
|
invariant(:day) { 1 <= day && day <= 31 }
|
645
662
|
invariant(:month) { 1 <= month && month <= 12 }
|
646
663
|
|
647
|
-
Contract None => Fixnum
|
664
|
+
Contract C::None => Fixnum
|
648
665
|
def silly_next_day!
|
649
666
|
self.day += 1
|
650
667
|
end
|
data/lib/contracts.rb
CHANGED
@@ -9,48 +9,15 @@ require "contracts/engine"
|
|
9
9
|
require "contracts/method_handler"
|
10
10
|
require "contracts/validators"
|
11
11
|
require "contracts/call_with"
|
12
|
+
require "contracts/core"
|
12
13
|
|
13
14
|
module Contracts
|
14
15
|
def self.included(base)
|
15
|
-
|
16
|
+
base.send(:include, Core)
|
16
17
|
end
|
17
18
|
|
18
19
|
def self.extended(base)
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.common(base)
|
23
|
-
return if base.respond_to?(:Contract)
|
24
|
-
|
25
|
-
base.extend(MethodDecorators)
|
26
|
-
|
27
|
-
base.instance_eval do
|
28
|
-
def functype(funcname)
|
29
|
-
contracts = Engine.fetch_from(self).decorated_methods_for(:class_methods, funcname)
|
30
|
-
if contracts.nil?
|
31
|
-
"No contract for #{self}.#{funcname}"
|
32
|
-
else
|
33
|
-
"#{funcname} :: #{contracts[0]}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
base.class_eval do
|
39
|
-
# TODO: deprecate
|
40
|
-
# Required when contracts are included in global scope
|
41
|
-
def Contract(*args)
|
42
|
-
self.class.Contract(*args)
|
43
|
-
end
|
44
|
-
|
45
|
-
def functype(funcname)
|
46
|
-
contracts = Engine.fetch_from(self.class).decorated_methods_for(:instance_methods, funcname)
|
47
|
-
if contracts.nil?
|
48
|
-
"No contract for #{self.class}.#{funcname}"
|
49
|
-
else
|
50
|
-
"#{funcname} :: #{contracts[0]}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
20
|
+
base.send(:extend, Core)
|
54
21
|
end
|
55
22
|
end
|
56
23
|
|
@@ -118,6 +85,7 @@ class Contract < Contracts::Decorator
|
|
118
85
|
maybe_a_proc = last_contract.is_a?(Contracts::Maybe) && last_contract.include_proc?
|
119
86
|
|
120
87
|
@has_proc_contract = is_a_proc || maybe_a_proc || last_contract.is_a?(Contracts::Func)
|
88
|
+
|
121
89
|
# ====
|
122
90
|
|
123
91
|
# == @has_options_contract
|
@@ -235,20 +203,24 @@ class Contract < Contracts::Decorator
|
|
235
203
|
|
236
204
|
# a better way to handle this might be to take this into account
|
237
205
|
# before throwing a "mismatched # of args" error.
|
206
|
+
# returns true if it appended nil
|
238
207
|
def maybe_append_block! args, blk
|
239
|
-
return unless @has_proc_contract && !blk &&
|
208
|
+
return false unless @has_proc_contract && !blk &&
|
240
209
|
(@args_contract_index || args.size < args_contracts.size)
|
241
210
|
args << nil
|
211
|
+
true
|
242
212
|
end
|
243
213
|
|
244
214
|
# Same thing for when we have named params but didn't pass any in.
|
215
|
+
# returns true if it appended nil
|
245
216
|
def maybe_append_options! args, blk
|
246
|
-
return unless @has_options_contract
|
217
|
+
return false unless @has_options_contract
|
247
218
|
if @has_proc_contract && args_contracts[-2].is_a?(Hash) && !args[-2].is_a?(Hash)
|
248
219
|
args.insert(-2, {})
|
249
220
|
elsif args_contracts[-1].is_a?(Hash) && !args[-1].is_a?(Hash)
|
250
221
|
args << {}
|
251
222
|
end
|
223
|
+
true
|
252
224
|
end
|
253
225
|
|
254
226
|
# Used to determine type of failure exception this contract should raise in case of failure
|
@@ -201,6 +201,20 @@ module Contracts
|
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
+
# Takes a list of values, e.g. +[:a, :b, :c]+. If argument is included in
|
205
|
+
# the list, the contract passes.
|
206
|
+
#
|
207
|
+
# Example: <tt>Enum[:a, :b, :c]</tt>?
|
208
|
+
class Enum < CallableClass
|
209
|
+
def initialize(*vals)
|
210
|
+
@vals = vals
|
211
|
+
end
|
212
|
+
|
213
|
+
def valid?(val)
|
214
|
+
@vals.include? val
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
204
218
|
# Takes a value +v+. If the argument is +.equal+ to +v+, the contract passes,
|
205
219
|
# otherwise the contract fails.
|
206
220
|
# Example: <tt>Eq[Class]</tt>
|
@@ -346,6 +360,7 @@ module Contracts
|
|
346
360
|
end
|
347
361
|
|
348
362
|
def valid?(hash)
|
363
|
+
return false unless hash.is_a?(Hash)
|
349
364
|
keys_match = hash.keys.map { |k| Contract.valid?(k, @key) }.all?
|
350
365
|
vals_match = hash.values.map { |v| Contract.valid?(v, @value) }.all?
|
351
366
|
|
@@ -371,6 +386,7 @@ module Contracts
|
|
371
386
|
end
|
372
387
|
|
373
388
|
def valid?(hash)
|
389
|
+
return false unless hash.keys - options.keys == []
|
374
390
|
options.all? do |key, contract|
|
375
391
|
Optional._valid?(hash, key, contract)
|
376
392
|
end
|
data/lib/contracts/call_with.rb
CHANGED
@@ -4,7 +4,7 @@ module Contracts
|
|
4
4
|
args << blk if blk
|
5
5
|
|
6
6
|
# Explicitly append blk=nil if nil != Proc contract violation anticipated
|
7
|
-
maybe_append_block!(args, blk)
|
7
|
+
nil_block_appended = maybe_append_block!(args, blk)
|
8
8
|
|
9
9
|
# Explicitly append options={} if Hash contract is present
|
10
10
|
maybe_append_options!(args, blk)
|
@@ -66,7 +66,8 @@ module Contracts
|
|
66
66
|
end
|
67
67
|
|
68
68
|
# If we put the block into args for validating, restore the args
|
69
|
-
|
69
|
+
# OR if we added a fake nil at the end because a block wasn't passed in.
|
70
|
+
args.slice!(-1) if blk || nil_block_appended
|
70
71
|
result = if method.respond_to?(:call)
|
71
72
|
# proc, block, lambda, etc
|
72
73
|
method.call(*args, &blk)
|