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!