-
-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* start * Add exercise and concept * More work * Update concept text * Update config * Format spec file * Update based on feedback
- Loading branch information
1 parent
37a9758
commit d7c3b0a
Showing
14 changed files
with
742 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"blurb": "Crystal has errors which can be raised and rescued. It is also possible to deffine your own exceptions.", | ||
"authors": ["meatball133"], | ||
"contributors": ["ryanplusplus"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{% $errors %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# Exception | ||
|
||
In the ideal world, everything works perfectly. | ||
But in the real world, things can go wrong, and how we handle these situations matters to ensure that our software is robust and reliable. | ||
Exceptions are a crucial concept in programming that allows us to handle errors and unexpected situations gracefully. | ||
|
||
Raising an error, if not handled, halts the program and throws an error message. | ||
In most cases, you don't want your program to halt when an error occurs, instead you want to handle the error and continue running the program. | ||
|
||
## Raising an exception | ||
|
||
In Crystal, exceptions are raised using the `raise` keyword and can either be given a `String` or an `Exception` object. | ||
If unhandled, the program will halt and print the error message. | ||
|
||
```crystal | ||
raise "This is an error" | ||
``` | ||
|
||
There are several built-in exceptions in Crystal, like `ArgumentError`, `IndexError`, `KeyError`, `IOError`, `SystemCallError`, `TypeError`, `ZeroDivisionError` and many more. | ||
These require you to pass a message to the exception. | ||
|
||
```crystal | ||
raise ArgumentError.new("This is an argument error") | ||
``` | ||
|
||
## [Handling exceptions][[exception-handling]] | ||
|
||
We wouldn't want our program to crash when an exception is raised. | ||
Therefore, when we know a piece of code is error prone, we can wrap it in a `begin` block and rescue the exception with a `rescue` block. | ||
The `begin` block marks the beginning of the code that might raise an exception, and the `rescue` block handles the exception. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a variable to get the exception object. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue ex | ||
puts "An error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a specific exception type only to catch that exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
end | ||
# or | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ex : ArgumentError | ||
puts "An argument error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
Multiple `rescue` blocks can be used to handle different types of exceptions. | ||
In the example below, the first `rescue` block will catch an `ArgumentError`, and the second `rescue` block will catch any other exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `begin` block can also have an `else` block, which is executed if no exception is raised. | ||
|
||
```crystal | ||
begin | ||
puts "No error occurred" | ||
rescue | ||
puts "An error occurred!" | ||
else | ||
puts "No error occurred" | ||
end | ||
``` | ||
|
||
Lastly, there is an `ensure` block that is always executed, regardless of whether an exception was raised. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
ensure | ||
puts "This is always executed" | ||
end | ||
``` | ||
|
||
## Method convention | ||
|
||
Some methods have two versions, one with `!` and the other without. | ||
This can mean two different things. | ||
One is that the method mutates the object, and the other is that the method can raise an exception. | ||
|
||
But there is also another convention around ending a method with `?` mentioned in the boolean concept. | ||
Some methods raise an exception by default but also have a version ending with `?` which returns `nil` instead of raising an exception. | ||
|
||
This is ideal when you want to avoid an error being raised. | ||
This can benefit performance since it doesn't have to create a stack trace and, if set up correctly, could make the code safer. | ||
|
||
## Custom exceptions | ||
|
||
You can also create your own exceptions by inheriting from the [`Exception`][exception] class. | ||
In doing so, you can optionally override the `initialize` method to set the exception message. | ||
This can be done by assigning an instance variable named `@message` with the message. | ||
|
||
```crystal | ||
class MyException < Exception | ||
def initialize | ||
@message = "This is my exception" | ||
end | ||
end | ||
raise MyException.new | ||
``` | ||
|
||
[exception-handling]: https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html | ||
[exception]: https://crystal-lang.org/api/Exception.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# Exception | ||
|
||
In the ideal world, everything works perfectly. | ||
But in the real world, things can go wrong, and how we handle these situations matters to ensure that our software is robust and reliable. | ||
Exceptions are a crucial concept in programming that allows us to handle errors and unexpected situations gracefully. | ||
|
||
Raising an error, if not handled, halts the program and throws an error message. | ||
In most cases, you don't want your program to halt when an error occurs, instead you want to handle the error and continue running the program. | ||
|
||
## Raising an exception | ||
|
||
In Crystal, exceptions are raised using the `raise` keyword and can either be given a `String` or an `Exception` object. | ||
If unhandled, the program will halt and print the error message. | ||
|
||
```crystal | ||
raise "This is an error" | ||
``` | ||
|
||
There are several built-in exceptions in Crystal, like `ArgumentError`, `IndexError`, `KeyError`, `IOError`, `SystemCallError`, `TypeError`, `ZeroDivisionError` and many more. | ||
These require you to pass a message to the exception. | ||
|
||
```crystal | ||
raise ArgumentError.new("This is an argument error") | ||
``` | ||
|
||
## [Handling exceptions][[exception-handling]] | ||
|
||
We wouldn't want our program to crash when an exception is raised. | ||
Therefore, when we know a piece of code is error prone, we can wrap it in a `begin` block and rescue the exception with a `rescue` block. | ||
The `begin` block marks the beginning of the code that might raise an exception, and the `rescue` block handles the exception. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a variable to get the exception object. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue ex | ||
puts "An error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a specific exception type only to catch that exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
end | ||
# or | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ex : ArgumentError | ||
puts "An argument error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
Multiple `rescue` blocks can be used to handle different types of exceptions. | ||
In the example below, the first `rescue` block will catch an `ArgumentError`, and the second `rescue` block will catch any other exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `begin` block can also have an `else` block, which is executed if no exception is raised. | ||
|
||
```crystal | ||
begin | ||
puts "No error occurred" | ||
rescue | ||
puts "An error occurred!" | ||
else | ||
puts "No error occurred" | ||
end | ||
``` | ||
|
||
Lastly, there is an `ensure` block that is always executed, regardless of whether an exception was raised. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
ensure | ||
puts "This is always executed" | ||
end | ||
``` | ||
|
||
## Method convention | ||
|
||
Some methods have two versions, one with `!` and the other without. | ||
This can mean two different things. | ||
One is that the method mutates the object, and the other is that the method can raise an exception. | ||
|
||
But there is also another convention around ending a method with `?` mentioned in the boolean concept. | ||
Some methods raise an exception by default but also have a version ending with `?` which returns `nil` instead of raising an exception. | ||
|
||
This is ideal when you want to avoid an error being raised. | ||
This can benefit performance since it doesn't have to create a stack trace and, if set up correctly, could make the code safer. | ||
|
||
## Custom exceptions | ||
|
||
You can also create your own exceptions by inheriting from the [`Exception`][exception] class. | ||
In doing so, you can optionally override the `initialize` method to set the exception message. | ||
This can be done by assigning an instance variable named `@message` with the message. | ||
|
||
```crystal | ||
class MyException < Exception | ||
def initialize | ||
@message = "This is my exception" | ||
end | ||
end | ||
raise MyException.new | ||
``` | ||
|
||
[exception-handling]: https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html | ||
[exception]: https://crystal-lang.org/api/Exception.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[ | ||
{ | ||
"url": "https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html", | ||
"description": "Crystal docs: exception-handling" | ||
}, | ||
{ | ||
"url": "https://crystal-lang.org/api/Exception.html", | ||
"description": "Crystal API: exception" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Hints | ||
|
||
## 1. Divide the food evenly | ||
|
||
- Start by writing the function signature of `DivideFood`. | ||
It should accept 2 parameters of type `FodderCalculator` and `int` and return two values of types `float64` and `error`. | ||
Revisit the [functions concept][concept-functions] if you need more information on how to define functions. | ||
- In the function body, call the `FodderAmount` [method][concept-methods] on `FodderCalculator` to fetch the default total amount of fodder for the cows. | ||
It will return the actual result and an error. | ||
Handle the error via an if-statement as it was explained in the introduction. | ||
- After that, call the `FatteningFactor` method and handle the error return value as before. | ||
- Now that you have the fodder amount and the factor, you can calculate the final result. | ||
You need to divide the fodder by the number of cows (revisit [numbers] for hints on type conversion) and multiply with the factor. Check the introduction for what to return as the error value in case of success. | ||
|
||
## 2. Check the number of cows | ||
|
||
- `ValidateInputAndDivideFood` has the same function signature as `DivideFood`. | ||
- Since you want to return early in case of an error in Go, you first check whether the number of cows is less or equal than 0 with an if-statement. | ||
- If it is, you return an error that you created with `errors.New`. | ||
Make sure the message matches the instructions. | ||
- If the number of cows is valid, you can proceed to call the existing `DivideFood` function from task 1. | ||
|
||
## 3. Improve the error handling | ||
|
||
- Start by creating the `InvalidCowsError` [struct][concept-structs] with two unexported fields that hold the number of cows and the message. | ||
- Next, define the `Error` method on that struct (with a pointer receiver). Revisit the exercise introduction for help on how to do this. | ||
- Now you can work on the `ValidateNumberOfCows` function. | ||
Depending on the number of cows ([if-statement][concept-conditionals]), it should create and return a new instance of the `InvalidCowsError` and set the correct message while doing so. | ||
If the number of cows was valid, `nil` should be returned. | ||
|
||
[concept-methods]: /tracks/go/concepts/methods | ||
[concept-functions]: /tracks/go/concepts/functions | ||
[concept-numbers]: /tracks/go/concepts/numbers | ||
[concept-structs]: /tracks/go/concepts/structs | ||
[concept-conditionals]: /tracks/go/concepts/conditionals-if |
Oops, something went wrong.