contracts 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 GenericExample.a_class_method x
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 < Struct.new(:request)
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 === string && !!string.match(/hello/i)
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[decorated_request(status.request)]
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 < Struct.new(:day, :month)
400
+ class MyBirthday
286
401
  include Contracts
287
402
  include Contracts::Invariants
288
403
 
289
- Invariant(:day) { 1 <= day && day <= 31 }
290
- Invariant(:month) { 1 <= month && month <= 12 }
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
- Invariant(:day_rule) { 1 <= day && day <= 7 }
478
+ invariant(:day_rule) { 1 <= day && day <= 7 }
358
479
 
359
480
  Contract None => nil
360
481
  def next_day
@@ -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
@@ -3,7 +3,7 @@ module Mod
3
3
 
4
4
  Contract Num => Num
5
5
  def self.a_module_method a
6
- a + 1
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
@@ -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 = 'doc'
77
+ config.default_formatter = "doc"
71
78
  end
72
79
 
73
80
  # Print the 10 slowest examples and example groups at the
@@ -4,3 +4,7 @@ def with_enabled_no_contracts
4
4
  yield
5
5
  ENV["NO_CONTRACTS"] = no_contracts
6
6
  end
7
+
8
+ def ruby_version
9
+ RUBY_VERSION.match(/\d+\.\d+/)[0].to_f
10
+ end
@@ -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.7'
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-16 00:00:00.000000000 Z
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: