TUTORIAL.md in contracts-0.7 vs TUTORIAL.md in contracts-0.8
- old
+ new
@@ -57,21 +57,37 @@
You can also see the contract for a function with the `functype` method:
functype(:add)
=> "add :: Num, Num => Num"
-This can be useful if you're in a repl and want to figure out how a function should be used.
+This can be useful if you're in a REPL and want to figure out how a function should be used.
-## Builtin Contracts
+## Built-in Contracts
-`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.
+`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.
-contracts.ruby comes with a lot of builtin contracts, including:
+contracts.ruby comes with a lot of built-in contracts, including the following:
- Num, Pos, Neg, Any, None, Or, Xor, And, Not, RespondTo, Send, Exactly, ArrayOf, HashOf, Bool, Maybe
+* [`Num`](http://www.rubydoc.info/gems/contracts/Contracts/Num) – checks that the argument is `Numeric`
+* [`Pos`](http://www.rubydoc.info/gems/contracts/Contracts/Pos) – checks that the argument is a positive number
+* [`Neg`](http://www.rubydoc.info/gems/contracts/Contracts/Neg) – checks that the argument is a negative number
+* [`Nat`](http://www.rubydoc.info/gems/contracts/Contracts/Nat) – checks that the argument is a natural number
+* [`Bool`](http://www.rubydoc.info/gems/contracts/Contracts/Bool) – checks that the argument is `true` or `false`
+* [`Any`](http://www.rubydoc.info/gems/contracts/Contracts/Any) – Passes for any argument. Use when the argument has no constraints.
+* [`None`](http://www.rubydoc.info/gems/contracts/Contracts/None) – Fails for any argument. Use when the method takes no arguments.
+* [`Or`](http://www.rubydoc.info/gems/contracts/Contracts/Or) – passes if any of the given contracts pass, e.g. `Or[Fixnum, Float]`
+* [`Xor`](http://www.rubydoc.info/gems/contracts/Contracts/Xor) – passes if exactly one of the given contracts pass, e.g. `Xor[Fixnum, Float]`
+* [`And`](http://www.rubydoc.info/gems/contracts/Contracts/And) – passes if all contracts pass, e.g. `And[Fixnum, Float]`
+* [`Not`](http://www.rubydoc.info/gems/contracts/Contracts/Not) – passes if all contracts fail for the given argument, e.g. `Not[nil]`
+* [`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]`
+* [`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]`
+* [`Maybe`](http://www.rubydoc.info/gems/contracts/Contracts/Maybe) – passes if the argument is `nil`, or if the given contract passes
+* [`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]`
+* [`Send`](http://www.rubydoc.info/gems/contracts/Contracts/Send) – checks that all named methods return true, e.g. `Send[:valid?]`
+* [`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]`.
-To see all the builtin contracts and what they do, check out the [rdoc](http://rubydoc.info/gems/contracts/Contracts).
+To see all the built-in contracts and their full descriptions, check out the [RDoc](http://rubydoc.info/gems/contracts/Contracts).
## More Examples
### Hello, World
@@ -104,11 +120,11 @@
```ruby
Contract Or.new(Fixnum, Float) => Or.new(Fixnum, Float)
```
-All the builtin contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
+All the built-in contracts have overridden the square brackets (`[]`) to give the same functionality. So you could write
```ruby
Contract Or[Fixnum, Float] => Or[Fixnum, Float]
```
@@ -222,13 +238,31 @@
give_largest_value("a" => 1, 2 => 2, c: 3)
```
### Contracts On Functions
-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!
-Use the `Func` contract. `Func` takes a contract as it's argument, and uses that contract on the function that you pass in.
+Lets say you are writing a simple map function:
+```ruby
+def map(arr, func)
+```
+
+`map` takes an array, and a function. Suppose you want to add a contract to this function. You could try this:
+
+```ruby
+Contract ArrayOf[Any], Proc => ArrayOf[Any]
+def map(arr, func)
+```
+
+This says that the second argument should be a `Proc`. You can call the function like so:
+
+```ruby
+p map([1, 2, 3], lambda { |x| x + 1 }) # works
+```
+
+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.
+
Here's a `map` function that requires an array of numbers, and a function that takes a number and returns a number:
```ruby
Contract ArrayOf[Num], Func[Num => Num] => ArrayOf[Num]
def map(arr, func)
@@ -238,17 +272,28 @@
end
ret
end
```
-This will add the contract `Num => Num` on `func`. Try it with these two examples:
+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!
+Try this map function with these two examples:
+
```ruby
p map([1, 2, 3], lambda { |x| x + 1 }) # works
p map([1, 2, 3], lambda { |x| "oops" }) # fails, the lambda returns a string.
```
+NOTE: This is not valid:
+
+```ruby
+Contract ArrayOf[Num], Func => ArrayOf[Num]
+def map(arr, &func)
+```
+
+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`.
+
### Returning Multiple Values
Treat the return value as an array. For example, here's a function that returns two numbers:
```ruby
Contract Num => [Num, Num]
@@ -489,12 +534,12 @@
```ruby
class MyBirthday < Struct.new(:day, :month)
include Contracts
include Contracts::Invariants
- Invariant(:day) { 1 <= day && day <= 31 }
- Invariant(:month) { 1 <= month && month <= 12 }
+ invariant(:day) { 1 <= day && day <= 31 }
+ invariant(:month) { 1 <= month && month <= 12 }
Contract None => Fixnum
def silly_next_day!
self.day += 1
end
@@ -512,10 +557,14 @@
Actual: false
Value guarded in: MyBirthday::silly_next_day!
At: main.rb:9
```
-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.
+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.
+
+## Auto-generate documentation using contracts
+
+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!
## Misc
Please submit any bugs [here](https://github.com/egonSchiele/contracts.ruby/issues) and I'll try to get them resolved ASAP!