contracts 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,11 +7,11 @@ module Contracts
7
7
  #
8
8
  # [[1, 3], [1, 4], [2, 3], [2, 4]]
9
9
  def self.product(arrays)
10
- arrays.inject { |acc, x|
10
+ arrays.inject do |acc, x|
11
11
  acc.product(x)
12
- }.flatten(arrays.size - 2)
12
+ end.flatten(arrays.size - 2)
13
13
  end
14
-
14
+
15
15
  # Given a contract, tells if you it's testable
16
16
  def self.testable?(contract)
17
17
  if contract.respond_to?(:testable?)
@@ -30,10 +30,10 @@ module Contracts
30
30
  end
31
31
  end
32
32
 
33
- # TODO Should work on whatever class it was invoked on, no?
33
+ # TODO: Should work on whatever class it was invoked on, no?
34
34
  def self.check_all
35
35
  o = Object.new
36
- Object.decorated_methods.each do |name, contracts|
36
+ Object.decorated_methods.each do |name, _contracts|
37
37
  check(o.method(name))
38
38
  end
39
39
  end
@@ -64,6 +64,6 @@ module Contracts
64
64
  end
65
65
  puts "#{test_data.size} tests run."
66
66
  end
67
- end
67
+ end
68
68
  end
69
69
  end
@@ -1,3 +1,3 @@
1
1
  module Contracts
2
- VERSION = "0.7"
2
+ VERSION = "0.8"
3
3
  end
@@ -0,0 +1,5 @@
1
+ if RUBY_VERSION.to_f == 2.1
2
+ puts "running rubocop..."
3
+ puts `bundle exec rubocop #{ARGV.join(" ")} -D`
4
+ exit $?.exitstatus
5
+ end
@@ -22,6 +22,10 @@ RSpec.describe "Contracts:" do
22
22
  expect { @o.pos_test(1) }.to_not raise_error
23
23
  end
24
24
 
25
+ it "should fail for 0" do
26
+ expect { @o.pos_test(0) }.to raise_error(ContractError)
27
+ end
28
+
25
29
  it "should fail for negative numbers" do
26
30
  expect { @o.pos_test(-1) }.to raise_error(ContractError)
27
31
  end
@@ -32,11 +36,33 @@ RSpec.describe "Contracts:" do
32
36
  expect { @o.neg_test(-1) }.to_not raise_error
33
37
  end
34
38
 
39
+ it "should fail for 0" do
40
+ expect { @o.neg_test(0) }.to raise_error(ContractError)
41
+ end
42
+
35
43
  it "should fail for positive numbers" do
36
44
  expect { @o.neg_test(1) }.to raise_error(ContractError)
37
45
  end
38
46
  end
39
47
 
48
+ describe "Nat:" do
49
+ it "should pass for 0" do
50
+ expect { @o.nat_test(0) }.to_not raise_error
51
+ end
52
+
53
+ it "should pass for positive whole numbers" do
54
+ expect { @o.nat_test(1) }.to_not raise_error
55
+ end
56
+
57
+ it "should fail for positive non-whole numbers" do
58
+ expect { @o.nat_test(1.5) }.to raise_error(ContractError)
59
+ end
60
+
61
+ it "should fail for negative numbers" do
62
+ expect { @o.nat_test(-1) }.to raise_error(ContractError)
63
+ end
64
+ end
65
+
40
66
  describe "Any:" do
41
67
  it "should pass for numbers" do
42
68
  expect { @o.show(1) }.to_not raise_error
@@ -143,6 +169,28 @@ RSpec.describe "Contracts:" do
143
169
  end
144
170
  end
145
171
 
172
+ describe "Eq:" do
173
+ it "should pass for a class" do
174
+ expect { @o.eq_class_test(Foo) }
175
+ end
176
+
177
+ it "should pass for a module" do
178
+ expect { @o.eq_module_test(Bar) }
179
+ end
180
+
181
+ it "should pass for other values" do
182
+ expect { @o.eq_value_test(Baz) }
183
+ end
184
+
185
+ it "should fail when not equal" do
186
+ expect { @o.eq_class_test(Bar) }.to raise_error(ContractError)
187
+ end
188
+
189
+ it "should fail when given instance of class" do
190
+ expect { @o.eq_class_test(Foo.new) }.to raise_error(ContractError)
191
+ end
192
+ end
193
+
146
194
  describe "Not:" do
147
195
  it "should pass for an argument that isn't nil" do
148
196
  expect { @o.not_nil(1) }.to_not raise_error
@@ -192,22 +240,22 @@ RSpec.describe "Contracts:" do
192
240
  end
193
241
  end
194
242
 
195
- describe 'HashOf:' do
196
- context 'given a fulfilled contract' do
243
+ describe "HashOf:" do
244
+ context "given a fulfilled contract" do
197
245
  it { expect(@o.gives_max_value(:panda => 1, :bamboo => 2)).to eq(2) }
198
246
  end
199
247
 
200
- context 'given an unfulfilled contract' do
201
- it { expect { @o.gives_max_value(:panda => '1', :bamboo => '2') }.to raise_error(ContractError) }
248
+ context "given an unfulfilled contract" do
249
+ it { expect { @o.gives_max_value(:panda => "1", :bamboo => "2") }.to raise_error(ContractError) }
202
250
  end
203
251
 
204
- describe '#to_s' do
205
- context 'given Symbol => String' do
206
- it { expect(Contracts::HashOf[Symbol, String].to_s).to eq('Hash<Symbol, String>') }
252
+ describe "#to_s" do
253
+ context "given Symbol => String" do
254
+ it { expect(Contracts::HashOf[Symbol, String].to_s).to eq("Hash<Symbol, String>") }
207
255
  end
208
256
 
209
- context 'given String => Num' do
210
- it { expect(Contracts::HashOf[String, Contracts::Num].to_s).to eq('Hash<String, Contracts::Num>') }
257
+ context "given String => Num" do
258
+ it { expect(Contracts::HashOf[String, Contracts::Num].to_s).to eq("Hash<String, Contracts::Num>") }
211
259
  end
212
260
  end
213
261
  end
@@ -5,9 +5,9 @@ RSpec.describe "Contracts:" do
5
5
 
6
6
  describe "basic" do
7
7
  it "should fail for insufficient arguments" do
8
- expect {
8
+ expect do
9
9
  @o.hello
10
- }.to raise_error
10
+ end.to raise_error
11
11
  end
12
12
 
13
13
  it "should fail for insufficient contracts" do
@@ -23,8 +23,8 @@ RSpec.describe "Contracts:" do
23
23
 
24
24
  it "should work as expected when there is no contract violation" do
25
25
  expect(
26
- subject.process_request(PatternMatchingExample::Success[string_with_hello])
27
- ).to eq(PatternMatchingExample::Success[expected_decorated_string])
26
+ subject.process_request(PatternMatchingExample::Success.new(string_with_hello))
27
+ ).to eq(PatternMatchingExample::Success.new(expected_decorated_string))
28
28
 
29
29
  expect(
30
30
  subject.process_request(PatternMatchingExample::Failure.new)
@@ -33,28 +33,38 @@ RSpec.describe "Contracts:" do
33
33
 
34
34
  it "should not fall through to next pattern when there is a deep contract violation" do
35
35
  expect(PatternMatchingExample::Failure).not_to receive(:is_a?)
36
- expect {
37
- subject.process_request(PatternMatchingExample::Success[string_without_hello])
38
- }.to raise_error(ContractError)
36
+ expect do
37
+ subject.process_request(PatternMatchingExample::Success.new(string_without_hello))
38
+ end.to raise_error(ContractError)
39
39
  end
40
40
 
41
41
  it "should fail when the pattern-matched method's contract fails" do
42
- expect {
42
+ expect do
43
43
  subject.process_request("bad input")
44
- }.to raise_error(ContractError)
44
+ end.to raise_error(ContractError)
45
+ end
46
+
47
+ it "should work for differing arities" do
48
+ expect(
49
+ subject.do_stuff(1, "abc", 2)
50
+ ).to eq("bar")
51
+
52
+ expect(
53
+ subject.do_stuff(3, "def")
54
+ ).to eq("foo")
45
55
  end
46
56
 
47
57
  context "when failure_callback was overriden" do
48
58
  before do
49
59
  ::Contract.override_failure_callback do |_data|
50
- raise RuntimeError, "contract violation"
60
+ fail "contract violation"
51
61
  end
52
62
  end
53
63
 
54
64
  it "calls a method when first pattern matches" do
55
65
  expect(
56
- subject.process_request(PatternMatchingExample::Success[string_with_hello])
57
- ).to eq(PatternMatchingExample::Success[expected_decorated_string])
66
+ subject.process_request(PatternMatchingExample::Success.new(string_with_hello))
67
+ ).to eq(PatternMatchingExample::Success.new(expected_decorated_string))
58
68
  end
59
69
 
60
70
  it "falls through to 2nd pattern when first pattern does not match" do
@@ -64,9 +74,9 @@ RSpec.describe "Contracts:" do
64
74
  end
65
75
 
66
76
  it "uses overriden failure_callback when pattern matching fails" do
67
- expect {
77
+ expect do
68
78
  subject.process_request("hello")
69
- }.to raise_error(RuntimeError, /contract violation/)
79
+ end.to raise_error(RuntimeError, /contract violation/)
70
80
  end
71
81
  end
72
82
  end
@@ -77,13 +87,13 @@ RSpec.describe "Contracts:" do
77
87
  end
78
88
 
79
89
  it "should fail with proper error when there is contract violation" do
80
- expect {
90
+ expect do
81
91
  SingletonClassExample.hoge(3)
82
- }.to raise_error(ContractError, /Expected: String/)
92
+ end.to raise_error(ContractError, /Expected: String/)
83
93
  end
84
94
 
85
95
  context "when owner class does not include Contracts" do
86
- let(:error) {
96
+ let(:error) do
87
97
  # NOTE Unable to support this user-friendly error for ruby
88
98
  # 1.8.7 and jruby 1.8, 1.9 it has much less support for
89
99
  # singleton inheritance hierarchy
@@ -92,10 +102,10 @@ RSpec.describe "Contracts:" do
92
102
  else
93
103
  [NoMethodError, /undefined method `Contract'/]
94
104
  end
95
- }
105
+ end
96
106
 
97
107
  it "fails with descriptive error" do
98
- expect {
108
+ expect do
99
109
  Class.new(GenericExample) do
100
110
  class << self
101
111
  Contract String => String
@@ -104,15 +114,15 @@ RSpec.describe "Contracts:" do
104
114
  end
105
115
  end
106
116
  end
107
- }.to raise_error(*error)
117
+ end.to raise_error(*error)
108
118
  end
109
119
  end
110
120
 
111
121
  describe "builtin contracts usage" do
112
122
  it "allows to use builtin contracts without namespacing and redundant Contracts inclusion" do
113
- expect {
123
+ expect do
114
124
  SingletonClassExample.add("55", 5.6)
115
- }.to raise_error(ContractError, /Expected: Contracts::Num/)
125
+ end.to raise_error(ContractError, /Expected: Num/)
116
126
  end
117
127
  end
118
128
  end
@@ -236,20 +246,20 @@ RSpec.describe "Contracts:" do
236
246
  it "should allow two classes to have the same method with different contracts" do
237
247
  a = A.new
238
248
  b = B.new
239
- expect {
249
+ expect do
240
250
  a.triple(5)
241
251
  b.triple("a string")
242
- }.to_not raise_error
252
+ end.to_not raise_error
243
253
  end
244
254
  end
245
255
 
246
256
  describe "instance and class methods" do
247
257
  it "should allow a class to have an instance method and a class method with the same name" do
248
258
  a = A.new
249
- expect {
259
+ expect do
250
260
  a.instance_and_class_method(5)
251
261
  A.instance_and_class_method("a string")
252
- }.to_not raise_error
262
+ end.to_not raise_error
253
263
  end
254
264
  end
255
265
 
@@ -313,77 +323,103 @@ RSpec.describe "Contracts:" do
313
323
 
314
324
  describe "Hashes" do
315
325
  it "should pass for exact correct input" do
316
- expect { @o.person({:name => "calvin", :age => 10}) }.to_not raise_error
326
+ expect { @o.person(:name => "calvin", :age => 10) }.to_not raise_error
317
327
  end
318
328
 
319
329
  it "should pass even if some keys don't have contracts" do
320
- expect { @o.person({:name => "calvin", :age => 10, :foo => "bar"}) }.to_not raise_error
330
+ expect { @o.person(:name => "calvin", :age => 10, :foo => "bar") }.to_not raise_error
321
331
  end
322
332
 
323
333
  it "should fail if a key with a contract on it isn't provided" do
324
- expect { @o.person({:name => "calvin"}) }.to raise_error(ContractError)
334
+ expect { @o.person(:name => "calvin") }.to raise_error(ContractError)
325
335
  end
326
336
 
327
337
  it "should fail for incorrect input" do
328
- expect { @o.person({:name => 50, :age => 10}) }.to raise_error(ContractError)
338
+ expect { @o.person(:name => 50, :age => 10) }.to raise_error(ContractError)
329
339
  end
330
340
  end
331
341
 
332
342
  describe "blocks" do
333
343
  it "should pass for correct input" do
334
- expect { @o.do_call {
335
- 2 + 2
336
- }}.to_not raise_error
344
+ expect do
345
+ @o.do_call do
346
+ 2 + 2
347
+ end
348
+ end.to_not raise_error
337
349
  end
338
350
 
339
351
  it "should fail for incorrect input" do
340
- expect { @o.do_call(nil) }.to raise_error(ContractError)
352
+ expect do
353
+ @o.do_call(nil)
354
+ end.to raise_error(ContractError)
341
355
  end
342
356
 
343
357
  it "should handle properly lack of block when there are other arguments" do
344
- expect { @o.double_with_proc(4) }.to raise_error(ContractError, /Actual: nil/)
358
+ expect do
359
+ @o.double_with_proc(4)
360
+ end.to raise_error(ContractError, /Actual: nil/)
345
361
  end
346
362
  end
347
363
 
348
364
  describe "varargs" do
349
365
  it "should pass for correct input" do
350
- expect { @o.sum(1, 2, 3) }.to_not raise_error
366
+ expect do
367
+ @o.sum(1, 2, 3)
368
+ end.to_not raise_error
351
369
  end
352
370
 
353
371
  it "should fail for incorrect input" do
354
- expect { @o.sum(1, 2, "bad") }.to raise_error(ContractError)
372
+ expect do
373
+ @o.sum(1, 2, "bad")
374
+ end.to raise_error(ContractError)
375
+ end
376
+
377
+ it "should work with arg before splat" do
378
+ expect do
379
+ @o.arg_then_splat(3, "hello", "world")
380
+ end.to_not raise_error
355
381
  end
356
382
  end
357
383
 
358
384
  describe "varargs with block" do
359
385
  it "should pass for correct input" do
360
- expect { @o.with_partial_sums(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error
361
- expect { @o.with_partial_sums_contracted(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error
386
+ expect do
387
+ @o.with_partial_sums(1, 2, 3) do |partial_sum|
388
+ 2 * partial_sum + 1
389
+ end
390
+ end.not_to raise_error
391
+ expect do
392
+ @o.with_partial_sums_contracted(1, 2, 3) do |partial_sum|
393
+ 2 * partial_sum + 1
394
+ end
395
+ end.not_to raise_error
362
396
  end
363
397
 
364
398
  it "should fail for incorrect input" do
365
- expect {
366
- @o.with_partial_sums(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 }
367
- }.to raise_error(ContractError, /Actual: "bad"/)
399
+ expect do
400
+ @o.with_partial_sums(1, 2, "bad") do |partial_sum|
401
+ 2 * partial_sum + 1
402
+ end
403
+ end.to raise_error(ContractError, /Actual: "bad"/)
368
404
 
369
- expect {
405
+ expect do
370
406
  @o.with_partial_sums(1, 2, 3)
371
- }.to raise_error(ContractError, /Actual: nil/)
407
+ end.to raise_error(ContractError, /Actual: nil/)
372
408
 
373
- expect {
409
+ expect do
374
410
  @o.with_partial_sums(1, 2, 3, lambda { |x| x })
375
- }.to raise_error(ContractError, /Actual: #<Proc/)
411
+ end.to raise_error(ContractError, /Actual: nil/)
376
412
  end
377
413
 
378
414
  context "when block has Func contract" do
379
415
  it "should fail for incorrect input" do
380
- expect {
416
+ expect do
381
417
  @o.with_partial_sums_contracted(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 }
382
- }.to raise_error(ContractError, /Actual: "bad"/)
418
+ end.to raise_error(ContractError, /Actual: "bad"/)
383
419
 
384
- expect {
420
+ expect do
385
421
  @o.with_partial_sums_contracted(1, 2, 3)
386
- }.to raise_error(ContractError, /Actual: nil/)
422
+ end.to raise_error(ContractError, /Actual: nil/)
387
423
  end
388
424
  end
389
425
  end
@@ -393,8 +429,20 @@ RSpec.describe "Contracts:" do
393
429
  expect { @o.map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error
394
430
  end
395
431
 
432
+ it "should pass for a function that passes the contract as in tutorial" do
433
+ expect { @o.tutorial_map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error
434
+ end
435
+
396
436
  it "should fail for a function that doesn't pass the contract" do
397
- expect { @o.map([1, 2, 3], lambda { |x| "bad return value" }) }.to raise_error(ContractError)
437
+ expect { @o.map([1, 2, 3], lambda { |_| "bad return value" }) }.to raise_error(ContractError)
438
+ end
439
+
440
+ it "should pass for a function that passes the contract with weak other args" do
441
+ expect { @o.map_plain(["hello", "joe"], lambda { |x| x.size }) }.to_not raise_error
442
+ end
443
+
444
+ it "should fail for a function that doesn't pass the contract with weak other args" do
445
+ expect { @o.map_plain(["hello", "joe"], lambda { |_| nil }) }.to raise_error(ContractError)
398
446
  end
399
447
  end
400
448
 
@@ -443,6 +491,77 @@ RSpec.describe "Contracts:" do
443
491
  end
444
492
  end
445
493
 
494
+ describe "Contracts to_s formatting in expected" do
495
+ def not_s(match)
496
+ Regexp.new "[^\"\']#{match}[^\"\']"
497
+ end
498
+
499
+ def delim(match)
500
+ "(#{match})"
501
+ end
502
+
503
+ it "should not stringify native types" do
504
+ expect do
505
+ @o.constanty("bad", nil)
506
+ end.to raise_error(ContractError, not_s(123))
507
+
508
+ expect do
509
+ @o.constanty(123, "bad")
510
+ end.to raise_error(ContractError, not_s(nil))
511
+ end
512
+
513
+ it "should contain to_s representation within a Hash contract" do
514
+ expect do
515
+ @o.hash_complex_contracts(:rigged => "bad")
516
+ end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
517
+ end
518
+
519
+ it "should contain to_s representation within a nested Hash contract" do
520
+ expect do
521
+ @o.nested_hash_complex_contracts(:rigged => true,
522
+ :contents => {
523
+ :kind => 0,
524
+ :total => 42 })
525
+ end.to raise_error(ContractError, not_s(delim "String or Symbol"))
526
+ end
527
+
528
+ it "should contain to_s representation within an Array contract" do
529
+ expect do
530
+ @o.array_complex_contracts(["bad"])
531
+ end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass"))
532
+ end
533
+
534
+ it "should contain to_s representation within a nested Array contract" do
535
+ expect do
536
+ @o.nested_array_complex_contracts([true, [0]])
537
+ end.to raise_error(ContractError, not_s(delim "String or Symbol"))
538
+ end
539
+
540
+ it "should not contain Contracts:: module prefix" do
541
+ expect do
542
+ @o.double("bad")
543
+ end.to raise_error(ContractError, /Expected: Num/)
544
+ end
545
+
546
+ it "should still show nils, not just blank space" do
547
+ expect do
548
+ @o.no_args("bad")
549
+ end.to raise_error(ContractError, /Expected: nil/)
550
+ end
551
+
552
+ it 'should show empty quotes as ""' do
553
+ expect do
554
+ @o.no_args("")
555
+ end.to raise_error(ContractError, /Actual: ""/)
556
+ end
557
+
558
+ it "should not use custom to_s if empty string" do
559
+ expect do
560
+ @o.using_empty_contract("bad")
561
+ end.to raise_error(ContractError, /Expected: EmptyCont/)
562
+ end
563
+ end
564
+
446
565
  describe "functype" do
447
566
  it "should correctly print out a instance method's type" do
448
567
  expect(@o.functype(:double)).not_to eq("")
@@ -455,11 +574,21 @@ RSpec.describe "Contracts:" do
455
574
 
456
575
  describe "private methods" do
457
576
  it "should raise an error if you try to access a private method" do
458
- expect { @o.a_private_method }.to raise_error
577
+ expect { @o.a_private_method }.to raise_error(NoMethodError, /private/)
459
578
  end
460
579
 
461
580
  it "should raise an error if you try to access a private method" do
462
- expect { @o.a_really_private_method }.to raise_error
581
+ expect { @o.a_really_private_method }.to raise_error(NoMethodError, /private/)
582
+ end
583
+ end
584
+
585
+ describe "protected methods" do
586
+ it "should raise an error if you try to access a protected method" do
587
+ expect { @o.a_protected_method }.to raise_error(NoMethodError, /protected/)
588
+ end
589
+
590
+ it "should raise an error if you try to access a protected method" do
591
+ expect { @o.a_really_protected_method }.to raise_error(NoMethodError, /protected/)
463
592
  end
464
593
  end
465
594