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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 065efb039a13c2917e10fc96dd77dd2fe858bc08
4
- data.tar.gz: 98f3253cc22a5da08f4d3414361db7218c438107
3
+ metadata.gz: 124deda42f022fb31b53dc3389bd51ff85860d30
4
+ data.tar.gz: 8ea3f78006952432311338880c563cdbdf327f33
5
5
  SHA512:
6
- metadata.gz: b8486b6e85a38517371403aa4cb7b3e6795ee01a2170e663eab8ff772507971a8e7bfa050b3b98dabb8fc60772153f544a14e9e419025a7c899ebda682b82338
7
- data.tar.gz: 44cfdb1101dbe48269e88c5758196e2bc11ce2238f70a791c0f62aa36d0754994dd00f49f4c8e0d98cca43a4b254a085cc83af3673964b6e31198ba66c14527e
6
+ metadata.gz: 644753384168c11234cd6a77b6d64384cc0f6fca93f3614e4d71ae3629ec7d05d85b02bfbd0150b3db21f94e04fe980f784d0e4decee0e5dcfd912f66526b2fb
7
+ data.tar.gz: 8a92fd259e98d4320fe5a4a52cc0d2d6cd01e7362b0fc6ed809001ef4e4618a7d1fab4513b2cb2db5946028fdf7a1c745e13491bf9d1e4e897f2dfe39446aafe
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gemspec
4
4
 
5
5
  group :test do
6
6
  gem "rspec"
7
+ gem "rubocop", "~> 0.29", :platform => [:ruby_20, :ruby_21]
7
8
  end
8
9
 
9
10
  group :development do
@@ -1,15 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contracts (0.6)
4
+ contracts (0.7)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
+ ast (2.0.0)
10
+ astrolabe (1.3.0)
11
+ parser (>= 2.2.0.pre.3, < 3.0)
9
12
  diff-lcs (1.2.5)
10
13
  hirb (0.7.2)
11
14
  method_profiler (2.0.1)
12
15
  hirb (>= 0.6.0)
16
+ parser (2.2.0.3)
17
+ ast (>= 1.1, < 3.0)
18
+ powerpack (0.1.0)
19
+ rainbow (2.0.0)
13
20
  rspec (3.1.0)
14
21
  rspec-core (~> 3.1.0)
15
22
  rspec-expectations (~> 3.1.0)
@@ -22,7 +29,14 @@ GEM
22
29
  rspec-mocks (3.1.3)
23
30
  rspec-support (~> 3.1.0)
24
31
  rspec-support (3.1.2)
32
+ rubocop (0.29.1)
33
+ astrolabe (~> 1.3)
34
+ parser (>= 2.2.0.1, < 3.0)
35
+ powerpack (~> 0.1)
36
+ rainbow (>= 1.99.1, < 3.0)
37
+ ruby-progressbar (~> 1.4)
25
38
  ruby-prof (0.15.2)
39
+ ruby-progressbar (1.7.5)
26
40
 
27
41
  PLATFORMS
28
42
  ruby
@@ -31,4 +45,5 @@ DEPENDENCIES
31
45
  contracts!
32
46
  method_profiler
33
47
  rspec
48
+ rubocop (~> 0.29)
34
49
  ruby-prof
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- # contracts.ruby
2
-
3
- [![Build Status](https://travis-ci.org/egonSchiele/contracts.ruby.png?branch=master)](https://travis-ci.org/egonSchiele/contracts.ruby)
1
+ # contracts.ruby [![Build Status](https://travis-ci.org/egonSchiele/contracts.ruby.png?branch=master)](https://travis-ci.org/egonSchiele/contracts.ruby) [![Join the chat at https://gitter.im/egonSchiele/contracts.ruby](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/egonSchiele/contracts.ruby)
4
2
 
5
3
  Contracts let you clearly – even beautifully – express how your code behaves, and free you from writing tons of boilerplate, defensive code.
6
4
 
@@ -65,11 +63,21 @@ Using contracts.ruby results in very little slowdown. Check out [this blog post]
65
63
 
66
64
  If you're using the library, please [let me know](https://github.com/egonSchiele) what project you're using it on :)
67
65
 
66
+ ## Testimonials
67
+
68
+ > Contracts literally saves us hours of pain at Snowplow every day
69
+
70
+ Alexander Dean, creator of [Snowplow](https://github.com/snowplow/snowplow)
71
+
72
+ > Contracts caught a bug that saved us several hundred dollars. It took less than 30 seconds to add the contract.
73
+
74
+ Michael Tomer
75
+
68
76
  ## Credits
69
77
 
70
78
  Inspired by [contracts.coffee](http://disnetdev.com/contracts.coffee/).
71
79
 
72
- Copyright 2012 [Aditya Bhargava](http://adit.io).
80
+ Copyright 2012-2015 [Aditya Bhargava](http://adit.io).
73
81
  Major improvements by [Alexey Fedorov](https://github.com/waterlink).
74
82
 
75
83
  BSD Licensed.
@@ -59,17 +59,33 @@ You can also see the contract for a function with the `functype` method:
59
59
  functype(:add)
60
60
  => "add :: Num, Num => Num"
61
61
 
62
- This can be useful if you're in a repl and want to figure out how a function should be used.
63
-
64
- ## Builtin Contracts
65
-
66
- `Num` is one of the builtin contracts that contracts.ruby comes with. The builtin contracts are in the `Contracts` namespace. The easiest way to use them is to include the `Contracts` module in your class/module.
67
-
68
- contracts.ruby comes with a lot of builtin contracts, including:
69
-
70
- Num, Pos, Neg, Any, None, Or, Xor, And, Not, RespondTo, Send, Exactly, ArrayOf, HashOf, Bool, Maybe
71
-
72
- To see all the builtin contracts and what they do, check out the [rdoc](http://rubydoc.info/gems/contracts/Contracts).
62
+ This can be useful if you're in a REPL and want to figure out how a function should be used.
63
+
64
+ ## Built-in Contracts
65
+
66
+ `Num` is one of the built-in contracts that contracts.ruby comes with. The built-in contracts are in the `Contracts` namespace. The easiest way to use them is to include the `Contracts` module in your class/module.
67
+
68
+ contracts.ruby comes with a lot of built-in contracts, including the following:
69
+
70
+ * [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) checks that the argument is `Numeric`
71
+ * [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
72
+ * [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
73
+ * [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Nat) – checks that the argument is a natural number
74
+ * [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Bool) – checks that the argument is `true` or `false`
75
+ * [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Any) – Passes for any argument. Use when the argument has no constraints.
76
+ * [`None`](http://www.rubydoc.info/gems/contracts/Contracts/None) – Fails for any argument. Use when the method takes no arguments.
77
+ * [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
78
+ * [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
79
+ * [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Fixnum, Float]`
80
+ * [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
81
+ * [`ArrayOf`](http://www.rubydoc.info/gems/contracts/Contracts/ArrayOf) – checks that the argument is an array, and all elements pass the given contract, e.g. `ArrayOf[Num]`
82
+ * [`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]`
83
+ * [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – passes if the argument is `nil`, or if the given contract passes
84
+ * [`RespondTo`](http://www.rubydoc.info/gems/contracts/Contracts/RespondTo) – checks that the argument responds to all of the given methods, e.g. `RespondTo[:password, :credit_card]`
85
+ * [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return true, e.g. `Send[:valid?]`
86
+ * [`Exactly`](http://www.rubydoc.info/gems/contracts/Contracts/Exactly) – checks that the argument has the given type, not accepting sub-classes, e.g. `Exactly[Numeric]`.
87
+
88
+ To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
73
89
 
74
90
  ## More Examples
75
91
 
@@ -106,7 +122,7 @@ This introduces some new syntax. One of the valid values for a contract is an in
106
122
  Contract Or.new(Fixnum, Float) => Or.new(Fixnum, Float)
107
123
  ```
108
124
 
109
- All the builtin contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
125
+ All the built-in contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
110
126
 
111
127
  ```ruby
112
128
  Contract Or[Fixnum, Float] => Or[Fixnum, Float]
@@ -224,8 +240,26 @@ give_largest_value("a" => 1, 2 => 2, c: 3)
224
240
 
225
241
  ### Contracts On Functions
226
242
 
227
- If you're writing higher-order functions (functions that take functions as parameters) and want to write a contract for the passed-in function, you can!
228
- Use the `Func` contract. `Func` takes a contract as it's argument, and uses that contract on the function that you pass in.
243
+ Lets say you are writing a simple map function:
244
+
245
+ ```ruby
246
+ def map(arr, func)
247
+ ```
248
+
249
+ `map` takes an array, and a function. Suppose you want to add a contract to this function. You could try this:
250
+
251
+ ```ruby
252
+ Contract ArrayOf[Any], Proc => ArrayOf[Any]
253
+ def map(arr, func)
254
+ ```
255
+
256
+ This says that the second argument should be a `Proc`. You can call the function like so:
257
+
258
+ ```ruby
259
+ p map([1, 2, 3], lambda { |x| x + 1 }) # works
260
+ ```
261
+
262
+ But suppose you want to have a contract on the Proc too! Suppose you want to make sure that the Proc returns a number. Use the `Func` contract. `Func` takes a contract as it's argument, and uses that contract on the function that you pass in.
229
263
 
230
264
  Here's a `map` function that requires an array of numbers, and a function that takes a number and returns a number:
231
265
 
@@ -240,13 +274,24 @@ def map(arr, func)
240
274
  end
241
275
  ```
242
276
 
243
- This will add the contract `Num => Num` on `func`. Try it with these two examples:
277
+ Earlier, we used `Proc`, which just says "make sure the second variable is a Proc". Now we are using `Func[Num => Num]`, which says "make sure the second variable is a Proc that takes a number and returns a number". Better!
278
+
279
+ Try this map function with these two examples:
244
280
 
245
281
  ```ruby
246
282
  p map([1, 2, 3], lambda { |x| x + 1 }) # works
247
283
  p map([1, 2, 3], lambda { |x| "oops" }) # fails, the lambda returns a string.
248
284
  ```
249
285
 
286
+ NOTE: This is not valid:
287
+
288
+ ```ruby
289
+ Contract ArrayOf[Num], Func => ArrayOf[Num]
290
+ def map(arr, &func)
291
+ ```
292
+
293
+ Here I am using `Func` without specifying a contract, like `Func[Num => Num]`. That's not a legal contract. If you just want to validate that the second argument is a proc, use `Proc`.
294
+
250
295
  ### Returning Multiple Values
251
296
  Treat the return value as an array. For example, here's a function that returns two numbers:
252
297
 
@@ -491,8 +536,8 @@ class MyBirthday < Struct.new(:day, :month)
491
536
  include Contracts
492
537
  include Contracts::Invariants
493
538
 
494
- Invariant(:day) { 1 <= day && day <= 31 }
495
- Invariant(:month) { 1 <= month && month <= 12 }
539
+ invariant(:day) { 1 <= day && day <= 31 }
540
+ invariant(:month) { 1 <= month && month <= 12 }
496
541
 
497
542
  Contract None => Fixnum
498
543
  def silly_next_day!
@@ -514,7 +559,11 @@ If you run it, last line will generate invariant violation:
514
559
  At: main.rb:9
515
560
  ```
516
561
 
517
- Which means, that after `#silly_next_day!` all checks specified in `Invariant` statement will be verified, and if at least one fail, then Invariant violation error will be raised.
562
+ Which means, that after `#silly_next_day!` all checks specified in `invariant` statement will be verified, and if at least one fail, then invariant violation error will be raised.
563
+
564
+ ## Auto-generate documentation using contracts
565
+
566
+ If you are generating documentation for your code with [YARD](http://yardoc.org/), check out [yard-contracts](https://github.com/sfcgeorge/yard-contracts). It will automatically annotate your functions with contracts information. Instead of documenting each parameter for a function yourself, you can just add a contract and yard-contracts will generate the documentation for you!
518
567
 
519
568
  ## Misc
520
569
 
@@ -1,8 +1,8 @@
1
- require './lib/contracts'
2
- require 'benchmark'
3
- require 'rubygems'
4
- require 'method_profiler'
5
- require 'ruby-prof'
1
+ require "./lib/contracts"
2
+ require "benchmark"
3
+ require "rubygems"
4
+ require "method_profiler"
5
+ require "ruby-prof"
6
6
 
7
7
  include Contracts
8
8
 
@@ -16,22 +16,22 @@ def contracts_add a, b
16
16
  end
17
17
 
18
18
  def explicit_add a, b
19
- raise unless a.is_a?(Numeric)
20
- raise unless b.is_a?(Numeric)
19
+ fail unless a.is_a?(Numeric)
20
+ fail unless b.is_a?(Numeric)
21
21
  c = a + b
22
- raise unless c.is_a?(Numeric)
22
+ fail unless c.is_a?(Numeric)
23
23
  c
24
24
  end
25
25
 
26
26
  def benchmark
27
27
  Benchmark.bm 30 do |x|
28
- x.report 'testing add' do
29
- 1000000.times do |_|
28
+ x.report "testing add" do
29
+ 1_000_000.times do |_|
30
30
  add(rand(1000), rand(1000))
31
31
  end
32
32
  end
33
- x.report 'testing contracts add' do
34
- 1000000.times do |_|
33
+ x.report "testing contracts add" do
34
+ 1_000_000.times do |_|
35
35
  contracts_add(rand(1000), rand(1000))
36
36
  end
37
37
  end
@@ -46,20 +46,20 @@ def profile
46
46
  profilers << MethodProfiler.observe(Contracts::Decorator)
47
47
  profilers << MethodProfiler.observe(Contracts::Support)
48
48
  profilers << MethodProfiler.observe(UnboundMethod)
49
- 10000.times do |_|
49
+ 10_000.times do |_|
50
50
  contracts_add(rand(1000), rand(1000))
51
51
  end
52
52
  profilers.each { |p| puts p.report }
53
53
  end
54
54
 
55
55
  def ruby_prof
56
- RubyProf.start
57
- 100000.times do |_|
58
- contracts_add(rand(1000), rand(1000))
59
- end
60
- result = RubyProf.stop
61
- printer = RubyProf::FlatPrinter.new(result)
62
- printer.print(STDOUT)
56
+ RubyProf.start
57
+ 100_000.times do |_|
58
+ contracts_add(rand(1000), rand(1000))
59
+ end
60
+ result = RubyProf.stop
61
+ printer = RubyProf::FlatPrinter.new(result)
62
+ printer.print(STDOUT)
63
63
  end
64
64
 
65
65
  benchmark
@@ -1,24 +1,34 @@
1
- require './lib/contracts'
2
- require 'benchmark'
3
- require 'rubygems'
4
- require 'method_profiler'
5
- require 'ruby-prof'
1
+ require "./lib/contracts"
2
+ require "benchmark"
3
+ require "rubygems"
4
+ require "method_profiler"
5
+ require "ruby-prof"
6
6
 
7
- class Obj < Struct.new(:value)
7
+ class Obj
8
8
  include Contracts
9
9
 
10
+ attr_accessor :value
11
+ def initialize value
12
+ @value = value
13
+ end
14
+
10
15
  Contract Num, Num => Num
11
16
  def contracts_add a, b
12
17
  a + b
13
18
  end
14
19
  end
15
20
 
16
- class ObjWithInvariants < Struct.new(:value)
21
+ class ObjWithInvariants
17
22
  include Contracts
18
23
  include Contracts::Invariants
19
24
 
20
- Invariant(:value_not_nil) { value != nil }
21
- Invariant(:value_not_string) { !value.is_a?(String) }
25
+ invariant(:value_not_nil) { value != nil }
26
+ invariant(:value_not_string) { !value.is_a?(String) }
27
+
28
+ attr_accessor :value
29
+ def initialize value
30
+ @value = value
31
+ end
22
32
 
23
33
  Contract Num, Num => Num
24
34
  def contracts_add a, b
@@ -31,16 +41,16 @@ def benchmark
31
41
  obj_with_invariants = ObjWithInvariants.new(3)
32
42
 
33
43
  Benchmark.bm 30 do |x|
34
- x.report 'testing contracts add' do
35
- 1000000.times do |_|
44
+ x.report "testing contracts add" do
45
+ 1_000_000.times do |_|
36
46
  obj.contracts_add(rand(1000), rand(1000))
37
47
  end
38
48
  end
39
- x.report 'testing contracts add with invariants' do
40
- 1000000.times do |_|
49
+ x.report "testing contracts add with invariants" do
50
+ 1_000_000.times do |_|
41
51
  obj_with_invariants.contracts_add(rand(1000), rand(1000))
42
52
  end
43
- end
53
+ end
44
54
  end
45
55
  end
46
56
 
@@ -55,19 +65,19 @@ def profile
55
65
  profilers << MethodProfiler.observe(Contracts::Invariants::InvariantExtension)
56
66
  profilers << MethodProfiler.observe(UnboundMethod)
57
67
 
58
- 10000.times do |_|
68
+ 10_000.times do |_|
59
69
  obj_with_invariants.contracts_add(rand(1000), rand(1000))
60
- end
70
+ end
61
71
 
62
72
  profilers.each { |p| puts p.report }
63
73
  end
64
74
 
65
75
  def ruby_prof
66
- RubyProf.start
76
+ RubyProf.start
67
77
 
68
78
  obj_with_invariants = ObjWithInvariants.new(3)
69
79
 
70
- 100000.times do |_|
80
+ 100_000.times do |_|
71
81
  obj_with_invariants.contracts_add(rand(1000), rand(1000))
72
82
  end
73
83
 
@@ -0,0 +1,62 @@
1
+ require "./lib/contracts"
2
+ require "benchmark"
3
+ require "rubygems"
4
+ require "method_profiler"
5
+ require "ruby-prof"
6
+ require "open-uri"
7
+
8
+ include Contracts
9
+
10
+ def download url
11
+ open("https://www.#{url}/").read
12
+ end
13
+
14
+ Contract String => String
15
+ def contracts_download url
16
+ open("https://www.#{url}").read
17
+ end
18
+
19
+ @urls = %w{facebook.com google.com bing.com}
20
+
21
+ def benchmark
22
+ Benchmark.bm 30 do |x|
23
+ x.report "testing download" do
24
+ 100.times do |_|
25
+ download(@urls.sample)
26
+ end
27
+ end
28
+ x.report "testing contracts download" do
29
+ 100.times do |_|
30
+ contracts_download(@urls.sample)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def profile
37
+ profilers = []
38
+ profilers << MethodProfiler.observe(Contract)
39
+ profilers << MethodProfiler.observe(Object)
40
+ profilers << MethodProfiler.observe(Contracts::MethodDecorators)
41
+ profilers << MethodProfiler.observe(Contracts::Decorator)
42
+ profilers << MethodProfiler.observe(Contracts::Support)
43
+ profilers << MethodProfiler.observe(UnboundMethod)
44
+ 10.times do |_|
45
+ contracts_download(@urls.sample)
46
+ end
47
+ profilers.each { |p| puts p.report }
48
+ end
49
+
50
+ def ruby_prof
51
+ RubyProf.start
52
+ 10.times do |_|
53
+ contracts_download(@urls.sample)
54
+ end
55
+ result = RubyProf.stop
56
+ printer = RubyProf::FlatPrinter.new(result)
57
+ printer.print(STDOUT)
58
+ end
59
+
60
+ benchmark
61
+ profile
62
+ ruby_prof if ENV["FULL_BENCH"] # takes some time