contracts 0.7 → 0.8
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/Gemfile +1 -0
- data/Gemfile.lock +16 -1
- data/README.md +12 -4
- data/TUTORIAL.md +67 -18
- data/benchmarks/bench.rb +20 -20
- data/benchmarks/invariants.rb +28 -18
- data/benchmarks/io.rb +62 -0
- data/benchmarks/wrap_test.rb +11 -13
- data/contracts.gemspec +1 -1
- data/lib/contracts.rb +167 -109
- data/lib/contracts/builtin_contracts.rb +132 -84
- data/lib/contracts/decorators.rb +41 -53
- data/lib/contracts/eigenclass.rb +3 -7
- data/lib/contracts/errors.rb +1 -1
- data/lib/contracts/formatters.rb +134 -0
- data/lib/contracts/invariants.rb +7 -8
- data/lib/contracts/method_reference.rb +33 -9
- data/lib/contracts/support.rb +9 -3
- data/lib/contracts/testable.rb +6 -6
- data/lib/contracts/version.rb +1 -1
- data/script/rubocop.rb +5 -0
- data/spec/builtin_contracts_spec.rb +57 -9
- data/spec/contracts_spec.rb +182 -53
- data/spec/fixtures/fixtures.rb +130 -9
- data/spec/invariants_spec.rb +0 -2
- data/spec/module_spec.rb +2 -2
- data/spec/ruby_version_specific/contracts_spec_1.9.rb +18 -0
- data/spec/ruby_version_specific/contracts_spec_2.0.rb +22 -0
- data/spec/ruby_version_specific/contracts_spec_2.1.rb +55 -0
- data/spec/spec_helper.rb +9 -2
- data/spec/support.rb +4 -0
- data/spec/support_spec.rb +21 -0
- metadata +11 -3
- data/lib/contracts/core_ext.rb +0 -15
data/spec/fixtures/fixtures.rb
CHANGED
@@ -45,16 +45,23 @@ class C
|
|
45
45
|
def good
|
46
46
|
false
|
47
47
|
end
|
48
|
+
|
48
49
|
def bad
|
49
50
|
true
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
54
|
+
class EmptyCont
|
55
|
+
def self.to_s
|
56
|
+
""
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
53
60
|
class GenericExample
|
54
61
|
include Contracts
|
55
62
|
|
56
63
|
Contract Num => Num
|
57
|
-
def
|
64
|
+
def self.a_class_method x
|
58
65
|
x + 1
|
59
66
|
end
|
60
67
|
|
@@ -68,6 +75,11 @@ class GenericExample
|
|
68
75
|
x * 2
|
69
76
|
end
|
70
77
|
|
78
|
+
Contract 123, nil => nil
|
79
|
+
def constanty(num, nul)
|
80
|
+
0
|
81
|
+
end
|
82
|
+
|
71
83
|
Contract String => nil
|
72
84
|
def hello(name)
|
73
85
|
end
|
@@ -84,10 +96,29 @@ class GenericExample
|
|
84
96
|
end
|
85
97
|
end
|
86
98
|
|
87
|
-
Contract ({:name => String, :age => Fixnum}) => nil
|
99
|
+
Contract ({ :name => String, :age => Fixnum }) => nil
|
88
100
|
def person(data)
|
89
101
|
end
|
90
102
|
|
103
|
+
Contract ({ :rigged => Or[TrueClass, FalseClass] }) => nil
|
104
|
+
def hash_complex_contracts(data)
|
105
|
+
end
|
106
|
+
|
107
|
+
Contract ({ :rigged => Bool,
|
108
|
+
:contents => { :kind => Or[String, Symbol],
|
109
|
+
:total => Num }
|
110
|
+
}) => nil
|
111
|
+
def nested_hash_complex_contracts(data)
|
112
|
+
end
|
113
|
+
|
114
|
+
Contract [Or[TrueClass, FalseClass]] => nil
|
115
|
+
def array_complex_contracts(data)
|
116
|
+
end
|
117
|
+
|
118
|
+
Contract [Bool, [Or[String, Symbol]]] => nil
|
119
|
+
def nested_array_complex_contracts(data)
|
120
|
+
end
|
121
|
+
|
91
122
|
Contract Proc => Any
|
92
123
|
def do_call(&blk)
|
93
124
|
blk.call
|
@@ -118,6 +149,12 @@ class GenericExample
|
|
118
149
|
blk[sum]
|
119
150
|
end
|
120
151
|
|
152
|
+
# Important to use different arg types or it falsely passes
|
153
|
+
Contract Num, Args[String] => ArrayOf[String]
|
154
|
+
def arg_then_splat(n, *vals)
|
155
|
+
vals.map { |v| v * n }
|
156
|
+
end
|
157
|
+
|
121
158
|
Contract Num, Proc => nil
|
122
159
|
def double_with_proc(x, &blk)
|
123
160
|
blk.call(x * 2)
|
@@ -132,6 +169,10 @@ class GenericExample
|
|
132
169
|
def neg_test(x)
|
133
170
|
end
|
134
171
|
|
172
|
+
Contract Nat => nil
|
173
|
+
def nat_test(x)
|
174
|
+
end
|
175
|
+
|
135
176
|
Contract Any => nil
|
136
177
|
def show(x)
|
137
178
|
end
|
@@ -189,6 +230,24 @@ class GenericExample
|
|
189
230
|
ret
|
190
231
|
end
|
191
232
|
|
233
|
+
Contract ArrayOf[Any], Proc => ArrayOf[Any]
|
234
|
+
def tutorial_map(arr, func)
|
235
|
+
ret = []
|
236
|
+
arr.each do |x|
|
237
|
+
ret << func[x]
|
238
|
+
end
|
239
|
+
ret
|
240
|
+
end
|
241
|
+
|
242
|
+
# Need to test Func with weak contracts for other args
|
243
|
+
# and changing type from input to output otherwise it falsely passes!
|
244
|
+
Contract Array, Func[String => Num] => Array
|
245
|
+
def map_plain(arr, func)
|
246
|
+
arr.map do |x|
|
247
|
+
func[x]
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
192
251
|
Contract Num => Num
|
193
252
|
def default_args(x = 1)
|
194
253
|
2
|
@@ -208,12 +267,23 @@ class GenericExample
|
|
208
267
|
hash.values.max
|
209
268
|
end
|
210
269
|
|
270
|
+
Contract EmptyCont => Any
|
271
|
+
def using_empty_contract(a)
|
272
|
+
a
|
273
|
+
end
|
274
|
+
|
211
275
|
Contract nil => String
|
212
276
|
def a_private_method
|
213
277
|
"works"
|
214
278
|
end
|
215
279
|
private :a_private_method
|
216
280
|
|
281
|
+
Contract nil => String
|
282
|
+
def a_protected_method
|
283
|
+
"works"
|
284
|
+
end
|
285
|
+
protected :a_protected_method
|
286
|
+
|
217
287
|
private
|
218
288
|
|
219
289
|
Contract nil => String
|
@@ -221,6 +291,12 @@ class GenericExample
|
|
221
291
|
"works for sure"
|
222
292
|
end
|
223
293
|
|
294
|
+
protected
|
295
|
+
|
296
|
+
Contract nil => String
|
297
|
+
def a_really_protected_method
|
298
|
+
"works for sure"
|
299
|
+
end
|
224
300
|
end
|
225
301
|
|
226
302
|
# for testing inheritance
|
@@ -247,11 +323,40 @@ class GenericExample
|
|
247
323
|
end
|
248
324
|
end
|
249
325
|
|
326
|
+
# for testing equality
|
327
|
+
class Foo
|
328
|
+
end
|
329
|
+
module Bar
|
330
|
+
end
|
331
|
+
Baz = 1
|
332
|
+
|
333
|
+
class GenericExample
|
334
|
+
Contract Eq[Foo] => Any
|
335
|
+
def eq_class_test(x)
|
336
|
+
end
|
337
|
+
|
338
|
+
Contract Eq[Bar] => Any
|
339
|
+
def eq_module_test(x)
|
340
|
+
end
|
341
|
+
|
342
|
+
Contract Eq[Baz] => Any
|
343
|
+
def eq_value_test(x)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
250
347
|
# pattern matching example with possible deep contract violation
|
251
348
|
class PatternMatchingExample
|
252
349
|
include Contracts
|
253
350
|
|
254
|
-
class Success
|
351
|
+
class Success
|
352
|
+
attr_accessor :request
|
353
|
+
def initialize request
|
354
|
+
@request = request
|
355
|
+
end
|
356
|
+
|
357
|
+
def ==(other)
|
358
|
+
request == other.request
|
359
|
+
end
|
255
360
|
end
|
256
361
|
|
257
362
|
class Failure
|
@@ -261,13 +366,13 @@ class PatternMatchingExample
|
|
261
366
|
|
262
367
|
class StringWithHello
|
263
368
|
def self.valid?(string)
|
264
|
-
String
|
369
|
+
string.is_a?(String) && !!string.match(/hello/i)
|
265
370
|
end
|
266
371
|
end
|
267
372
|
|
268
373
|
Contract Success => Response
|
269
374
|
def process_request(status)
|
270
|
-
Success
|
375
|
+
Success.new(decorated_request(status.request))
|
271
376
|
end
|
272
377
|
|
273
378
|
Contract Failure => Response
|
@@ -279,15 +384,31 @@ class PatternMatchingExample
|
|
279
384
|
def decorated_request(request)
|
280
385
|
request + "!"
|
281
386
|
end
|
387
|
+
|
388
|
+
Contract Num, String => String
|
389
|
+
def do_stuff(number, string)
|
390
|
+
"foo"
|
391
|
+
end
|
392
|
+
|
393
|
+
Contract Num, String, Num => String
|
394
|
+
def do_stuff(number, string, other_number)
|
395
|
+
"bar"
|
396
|
+
end
|
282
397
|
end
|
283
398
|
|
284
399
|
# invariant example (silliest implementation ever)
|
285
|
-
class MyBirthday
|
400
|
+
class MyBirthday
|
286
401
|
include Contracts
|
287
402
|
include Contracts::Invariants
|
288
403
|
|
289
|
-
|
290
|
-
|
404
|
+
invariant(:day) { 1 <= day && day <= 31 }
|
405
|
+
invariant(:month) { 1 <= month && month <= 12 }
|
406
|
+
|
407
|
+
attr_accessor :day, :month
|
408
|
+
def initialize(day, month)
|
409
|
+
@day = day
|
410
|
+
@month = month
|
411
|
+
end
|
291
412
|
|
292
413
|
Contract None => Fixnum
|
293
414
|
def silly_next_day!
|
@@ -354,7 +475,7 @@ with_enabled_no_contracts do
|
|
354
475
|
|
355
476
|
attr_accessor :day
|
356
477
|
|
357
|
-
|
478
|
+
invariant(:day_rule) { 1 <= day && day <= 7 }
|
358
479
|
|
359
480
|
Contract None => nil
|
360
481
|
def next_day
|
data/spec/invariants_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Contracts
|
2
2
|
RSpec.describe Invariants do
|
3
|
-
|
4
3
|
def new_subject
|
5
4
|
MyBirthday.new(31, 12)
|
6
5
|
end
|
@@ -14,6 +13,5 @@ module Contracts
|
|
14
13
|
expect { new_subject.silly_next_day! }.to raise_error(InvariantError, /day condition to be true/)
|
15
14
|
expect { new_subject.silly_next_month! }.to raise_error(InvariantError, /month condition to be true/)
|
16
15
|
end
|
17
|
-
|
18
16
|
end
|
19
17
|
end
|
data/spec/module_spec.rb
CHANGED
@@ -3,7 +3,7 @@ module Mod
|
|
3
3
|
|
4
4
|
Contract Num => Num
|
5
5
|
def self.a_module_method a
|
6
|
-
|
6
|
+
a + 1
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -15,4 +15,4 @@ RSpec.describe "module methods" do
|
|
15
15
|
it "should fail for incorrect input" do
|
16
16
|
expect { Mod.a_module_method("bad") }.to raise_error(ContractError)
|
17
17
|
end
|
18
|
-
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class GenericExample
|
2
|
+
Contract Args[String], Num => ArrayOf[String]
|
3
|
+
def splat_then_arg(*vals, n)
|
4
|
+
vals.map { |v| v * n }
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.describe "Contracts:" do
|
9
|
+
before :all do
|
10
|
+
@o = GenericExample.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Splat not last (or penultimate to block)" do
|
14
|
+
it "should work with arg after splat" do
|
15
|
+
expect { @o.splat_then_arg("hello", "world", 3) }.to_not raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class GenericExample
|
2
|
+
Contract Args[String], { repeat: Maybe[Num] } => ArrayOf[String]
|
3
|
+
def splat_then_optional_named(*vals, repeat: 2)
|
4
|
+
vals.map { |v| v * repeat }
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.describe "Contracts:" do
|
9
|
+
before :all do
|
10
|
+
@o = GenericExample.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Optional named arguments" do
|
14
|
+
it "should work with optional named argument unfilled after splat" do
|
15
|
+
expect { @o.splat_then_optional_named("hello", "world") }.to_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should work with optional named argument filled after splat" do
|
19
|
+
expect { @o.splat_then_optional_named("hello", "world", repeat: 3) }.to_not raise_error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class GenericExample
|
2
|
+
Contract String, Bool, Args[Symbol], Float, { e: Range, f: Maybe[Num] }, Proc =>
|
3
|
+
[Proc, Hash, Num, Range, Float, ArrayOf[Symbol], Bool, String]
|
4
|
+
def complicated(a, b = true, *c, d, e:, f:2, **g, &h)
|
5
|
+
h.call [h, g, f, e, d, c, b, a]
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec.describe "Contracts:" do
|
10
|
+
before :all do
|
11
|
+
@o = GenericExample.new
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "Required named arguments" do
|
15
|
+
describe "really complicated method signature" do
|
16
|
+
it "should work with default named args used" do
|
17
|
+
expect do
|
18
|
+
@o.complicated("a", false, :b, 2.0, e: (1..5)) { |x| x }
|
19
|
+
end.to_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should work with all args filled manually, with extra splat and hash" do
|
23
|
+
expect do
|
24
|
+
@o.complicated("a", true, :b, :c, 2.0, e: (1..5), f: 8.3, g: :d) do |x|
|
25
|
+
x
|
26
|
+
end
|
27
|
+
end.to_not raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fail when the return is invalid" do
|
31
|
+
expect do
|
32
|
+
@o.complicated("a", true, :b, 2.0, e: (1..5)) { |_x| "bad" }
|
33
|
+
end.to raise_error(ContractError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should fail when args are invalid" do
|
37
|
+
expect do
|
38
|
+
@o.complicated("a", "bad", :b, 2.0, e: (1..5)) { |x| x }
|
39
|
+
end.to raise_error(ContractError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should fail when splat is invalid" do
|
43
|
+
expect do
|
44
|
+
@o.complicated("a", true, "bad", 2.0, e: (1..5)) { |x| x }
|
45
|
+
end.to raise_error(ContractError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should fail when named argument is invalid" do
|
49
|
+
expect do
|
50
|
+
@o.complicated("a", true, :b, 2.0, e: "bad") { |x| x }
|
51
|
+
end.to raise_error(ContractError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,6 +19,13 @@ require File.expand_path(File.join(__FILE__, "../fixtures/fixtures"))
|
|
19
19
|
#
|
20
20
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
21
21
|
RSpec.configure do |config|
|
22
|
+
config.pattern = "*.rb"
|
23
|
+
|
24
|
+
# Only load tests who's syntax is valid in the current Ruby
|
25
|
+
[1.9, 2.0, 2.1].each do |ver|
|
26
|
+
config.pattern << ",ruby_version_specific/*#{ver}.rb" if ruby_version >= ver
|
27
|
+
end
|
28
|
+
|
22
29
|
# rspec-expectations config goes here. You can use an alternate
|
23
30
|
# assertion/expectation library such as wrong or the stdlib/minitest
|
24
31
|
# assertions if you prefer.
|
@@ -58,7 +65,7 @@ RSpec.configure do |config|
|
|
58
65
|
|
59
66
|
# This setting enables warnings. It's recommended, but in some cases may
|
60
67
|
# be too noisy due to issues in dependencies.
|
61
|
-
#config.warnings = true
|
68
|
+
# config.warnings = true
|
62
69
|
|
63
70
|
# Many RSpec users commonly either run the entire suite or an individual
|
64
71
|
# file, and it's useful to allow more verbose output when running an
|
@@ -67,7 +74,7 @@ RSpec.configure do |config|
|
|
67
74
|
# Use the documentation formatter for detailed output,
|
68
75
|
# unless a formatter has already been configured
|
69
76
|
# (e.g. via a command-line flag).
|
70
|
-
config.default_formatter =
|
77
|
+
config.default_formatter = "doc"
|
71
78
|
end
|
72
79
|
|
73
80
|
# Print the 10 slowest examples and example groups at the
|
data/spec/support.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Contracts
|
2
|
+
RSpec.describe Support do
|
3
|
+
describe "eigenclass?" do
|
4
|
+
it "is falsey for non-singleton classes" do
|
5
|
+
expect(Contracts::Support.eigenclass? String).to be_falsey
|
6
|
+
end
|
7
|
+
|
8
|
+
it "is truthy for singleton classes" do
|
9
|
+
singleton_class = String.instance_exec { class << self; self; end }
|
10
|
+
expect(Contracts::Support.eigenclass? singleton_class).to be_truthy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "eigenclass_of" do
|
15
|
+
it "returns the eigenclass of a given object" do
|
16
|
+
singleton_class = String.instance_exec { class << self; self; end }
|
17
|
+
expect(Contracts::Support.eigenclass_of String).to eq singleton_class
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
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.8'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aditya Bhargava
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02
|
11
|
+
date: 2015-04-02 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
|
@@ -20,6 +20,7 @@ extra_rdoc_files: []
|
|
20
20
|
files:
|
21
21
|
- ".gitignore"
|
22
22
|
- ".rspec"
|
23
|
+
- ".rubocop.yml"
|
23
24
|
- ".travis.yml"
|
24
25
|
- Gemfile
|
25
26
|
- Gemfile.lock
|
@@ -28,27 +29,33 @@ files:
|
|
28
29
|
- TUTORIAL.md
|
29
30
|
- benchmarks/bench.rb
|
30
31
|
- benchmarks/invariants.rb
|
32
|
+
- benchmarks/io.rb
|
31
33
|
- benchmarks/wrap_test.rb
|
32
34
|
- contracts.gemspec
|
33
35
|
- lib/contracts.rb
|
34
36
|
- lib/contracts/builtin_contracts.rb
|
35
|
-
- lib/contracts/core_ext.rb
|
36
37
|
- lib/contracts/decorators.rb
|
37
38
|
- lib/contracts/eigenclass.rb
|
38
39
|
- lib/contracts/errors.rb
|
40
|
+
- lib/contracts/formatters.rb
|
39
41
|
- lib/contracts/invariants.rb
|
40
42
|
- lib/contracts/method_reference.rb
|
41
43
|
- lib/contracts/modules.rb
|
42
44
|
- lib/contracts/support.rb
|
43
45
|
- lib/contracts/testable.rb
|
44
46
|
- lib/contracts/version.rb
|
47
|
+
- script/rubocop.rb
|
45
48
|
- spec/builtin_contracts_spec.rb
|
46
49
|
- spec/contracts_spec.rb
|
47
50
|
- spec/fixtures/fixtures.rb
|
48
51
|
- spec/invariants_spec.rb
|
49
52
|
- spec/module_spec.rb
|
53
|
+
- spec/ruby_version_specific/contracts_spec_1.9.rb
|
54
|
+
- spec/ruby_version_specific/contracts_spec_2.0.rb
|
55
|
+
- spec/ruby_version_specific/contracts_spec_2.1.rb
|
50
56
|
- spec/spec_helper.rb
|
51
57
|
- spec/support.rb
|
58
|
+
- spec/support_spec.rb
|
52
59
|
homepage: http://github.com/egonSchiele/contracts.ruby
|
53
60
|
licenses: []
|
54
61
|
metadata: {}
|
@@ -73,3 +80,4 @@ signing_key:
|
|
73
80
|
specification_version: 4
|
74
81
|
summary: Contracts for Ruby.
|
75
82
|
test_files: []
|
83
|
+
has_rdoc:
|