diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3487c3268..db2fae5e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -276,8 +276,7 @@ Have you tried turning it off and on again? Example Solution ```elixir -initial_count = 0 -initial_count + high + ``` diff --git a/README.md b/README.md index 5047808ac..e61d2969e 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,4 @@ See [start.livemd](https://github.com/DockYard-Academy/curriculum/blob/main/star + diff --git a/exercises/capstone_entity_relationship_diagram.livemd b/exercises/capstone_entity_relationship_diagram.livemd index 79aace3a1..396ce8ae5 100644 --- a/exercises/capstone_entity_relationship_diagram.livemd +++ b/exercises/capstone_entity_relationship_diagram.livemd @@ -78,7 +78,7 @@ See https://mermaid.js.org/syntax/entityRelationshipDiagram.html for the syntax. ### Requirements * Document the entities and their fields. -* Document the associations (many-to-one, many-to-many, one-to-one) in your capstone project +* Document the associations (many-to-one, many-to-many, one-to-one). ## Commit Your Progress diff --git a/exercises/games_supervised_score_tracker.livemd b/exercises/games_supervised_score_tracker.livemd index 43e11c589..8d6949c0d 100644 --- a/exercises/games_supervised_score_tracker.livemd +++ b/exercises/games_supervised_score_tracker.livemd @@ -60,7 +60,7 @@ Increase the `ScoreTracker`'s score whenever a user wins the game. ```elixir -# Games.ScoreTracker Should Be A Named Process, So It Is Not Necessary To Send The Pid. +# Games.ScoreTracker should be a named process, so we don't need to provide the pid. {:ok, _pid} = Games.ScoreTracker.start_link() :ok = Games.Score.add_points(10) :ok = Games.Score.add_points(10) diff --git a/exercises/group_project_blog.livemd b/exercises/group_project_blog.livemd index 4bdf65afb..bd0d6b96c 100644 --- a/exercises/group_project_blog.livemd +++ b/exercises/group_project_blog.livemd @@ -124,7 +124,7 @@ $ git init Edit the `README.md` file initialized in your Phoenix project. -Include alteast the following information: +Include at least the following information: * Project Name * Project Summary diff --git a/exercises/stack.livemd b/exercises/stack.livemd index d65d99fc6..24ce5ab0a 100644 --- a/exercises/stack.livemd +++ b/exercises/stack.livemd @@ -58,7 +58,7 @@ In addition to the above, add documentation and at least one doctest for the `pu This will be a **pair testing** exercise Using TDD (Test Driven Development). We recommend using Visual Studio Code LiveShare to collaborate. -1. One student (**the tester**) will write a test +1. One student (**the tester**) will write a single test 2. The other student (**the implementer**) will implement **only** the code necessary to make the test pass. 3. **Swap roles after each test.** diff --git a/exercises/supervised_stack.livemd b/exercises/supervised_stack.livemd index d7c4520a9..3cf85f162 100644 --- a/exercises/supervised_stack.livemd +++ b/exercises/supervised_stack.livemd @@ -32,9 +32,13 @@ Mix.install([ ## Supervised Stack -Previously we created a [Stack](./stack_server.livemd) [GenServer](https://hexdocs.pm/elixir/GenServer.html) process. +You're going to create a stack project that starts a name [Stack](./stack_server.livemd) GenServer process under a supervisor. -We've made a slight modification to the `Stack` process by adding a `start_link/1` function so this named `Stack` can be started under a [Supervisor](https://hexdocs.pm/elixir/Supervisor.html). +``` +mix new stack +``` + +Here's an example Stack GenServer you can use. ```elixir defmodule Stack do @@ -63,30 +67,13 @@ defmodule Stack do end ``` -We're able to push and pop elements off of the stack. - -```elixir -{:ok, stack_pid} = GenServer.start_link(Stack, []) - -GenServer.call(stack_pid, {:push, 1}) |> IO.inspect(label: "push") -GenServer.call(stack_pid, {:push, 2}) |> IO.inspect(label: "push") -GenServer.call(stack_pid, {:push, 3}) |> IO.inspect(label: "push") - -GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop") -GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop") -GenServer.call(stack_pid, :pop) |> IO.inspect(label: "pop") - -# The Final State Is Empty. -:sys.get_state(stack_pid) -``` - -However, there's a bug. If we try to `pop/1` an item off of an empty stack, the process +We're able to push and pop elements off of the stack. However, there's a bug. If we try to `pop/1` an item off of an empty stack, the process will crash due to a function clause error because the `handle_call/2` function expects a list with one or more elements. Uncomment the following code to watch the `Stack` crash. ```elixir -# {:ok, Pid} = GenServer.start_link(Stack, []) +# {:ok, pid} = GenServer.start_link(Stack, []) # GenServer.call(stack_pid, :pop) ``` @@ -102,7 +89,7 @@ def handle_call(:pop, _from, []) do end ``` -Instead, you're going to start the Stack process under a supervisor so that it will be restarted when it crashes. In the Elixir cell below, start the `Stack` process under a supervisor so that it will restart with an empty stack when it crashes. +Instead, you're going to start the `Stack` process under a supervisor in your application so that it will be restarted when it crashes.
Example Solution @@ -117,18 +104,17 @@ children = [
-Keep in mind, if you have already started a supervisor with the `Stack` process, your livebook may crash. You can resolve this issue by simply re-running the cell below to start the supervisor again. + -```elixir +### Crash The Stack -``` - -You should be able to send a `:pop` message to the `Stack` process and the [Supervisor](https://hexdocs.pm/elixir/Supervisor.html) will restart the `Stack` process. +Open the [IEx](https://hexdocs.pm/iex/IEx.html) shell and send the `Stack` a `:pop` message to cause it to crash and restart. -Uncomment and evaluate the code below to test your supervisor. + ```elixir -# GenServer.call(Stack, :pop) +$ iex -S mix +iex> GenServer.call(Stack, :pop) ``` ## Commit Your Progress diff --git a/exercises/task_drills.livemd b/exercises/task_drills.livemd index 47bde0633..2704e6e07 100644 --- a/exercises/task_drills.livemd +++ b/exercises/task_drills.livemd @@ -40,18 +40,51 @@ This set of drills is for [Tasks](../reading/task.livemd). Follow the instructio Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that returns any response. +
+Example Solution + +```elixir +task = Task.async(fn -> :ok end) + +Task.await(task) +``` + +
+ ```elixir ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that sleeps for six seconds. It should cause a timeout error. +
+Example Solution + +```elixir +task = Task.async(fn -> Process.sleep(6000) end) + +Task.await(task) +``` + +
+ ```elixir ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that sleeps for six seconds. Provide a timeout value to [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) so that it properly awaits the response. +
+Example Solution + +```elixir +task = Task.async(fn -> Process.sleep(6000) end) + +Task.await(task, 7000) +``` + +
+ ```elixir ``` @@ -60,67 +93,75 @@ Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/ Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that returns any response. - - -### Slow Operation +
+Example Solution ```elixir -# 1000 Operations -{time, _result} = :timer.tc(fn -> Enum.map(1..10, fn int -> Process.sleep(1000) end) end) -time +task = Task.async(fn -> :ok end) + +Task.yield(task) ``` +
+ ```elixir -{time, _result} = - :timer.tc(fn -> - # 1000 tasks - # 1000 operations -> parallel - tasks = - Enum.map(1..10, fn int -> - Task.async(fn -> - IO.puts("starting task #{int}") - Process.sleep(1000) - end) - end) - Task.await_many(tasks) - end) +``` + +Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Notice that the yield is `nil`. + +
+Example Solution -10_009_225 -1_001_494 +```elixir +task = Task.async(fn -> Process.sleep(6000) end) + +Task.yield(task) ``` -### Fast Operation +
```elixir -# 1000 Operations -{time, _result} = :timer.tc(fn -> Enum.map(1..10, fn int -> int * 2 end) end) -time + ``` +Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Provide a timeout value to [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) so that it properly returns the response. + +
+Example Solution + ```elixir -{time, _result} = - :timer.tc(fn -> - # 1000 tasks - # 1000 operations -> parallel - tasks = Enum.map(1..10, fn int -> Task.async(fn -> int * 2 end) end) - Task.await_many(tasks) - end) +task = Task.async(fn -> Process.sleep(6000) end) + +Task.yield(task, 7000) ``` -Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Notice that the yield is `nil`. +
```elixir ``` -Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Provide a timeout value to [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) so that it properly returns the response. +Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2), and [Task.shutdown/2](https://hexdocs.pm/elixir/Task.html#shutdown/2) to spawn a task process that sleeps for six seconds. Shutdown the task process if it does not yield any value after five seconds. + +Use `IO.puts/2` to prove that the task was shutdown. + +
+Example Solution ```elixir +task = + Task.async(fn -> + Process.sleep(6000) + IO.puts("NOT RAN") + end) +if !Task.yield(task) do + Task.shutdown(task) +end ``` -Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2), and [Task.shutdown/2](https://hexdocs.pm/elixir/Task.html#shutdown/2) to spawn a task process that sleeps for six seconds. Shutdown the task process if it does not yield any value. +
```elixir @@ -130,17 +171,34 @@ Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [Task.yield/2]( Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn two task processes that both return a random number from `1..10`. +
+Example Solution + +```elixir +task1 = Task.async(fn -> Enum.random(1..10) end) +task2 = Task.async(fn -> Enum.random(1..10) end) + +Task.await_many([task1, task2]) +``` + +
+ ```elixir ``` Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn one hundred task processes that all return a random integer from `1..100`. +
+Example Solution + ```elixir +tasks = Enum.map(1..100, fn _ -> Task.async(fn -> Enum.random(1..100) end) end) +Task.await_many(tasks) ``` -Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn one hundred task processes that return a list doubled numbers from one to one hundred. i.e. `[2, 4, 6, 8, ..., 198, 200]`. +
```elixir @@ -148,13 +206,29 @@ Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](http Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [String.upcase/2](https://hexdocs.pm/elixir/String.html#upcase/2), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to concurrently capitalize every string in the list `["apple", "pear", "peaches"]` +
+Example Solution + +```elixir +list = ["apple", "pear", "peaches"] + +tasks = Enum.map(list, fn word -> Task.async(fn -> String.upcase(word) end) end) + +Task.await_many(tasks) +``` + +
+ ```elixir ``` ## Task.Supervisor -Start a [Supervisor](https://hexdocs.pm/elixir/Supervisor.html) with a child `Task.Supervisor` process named `MyTaskSupervisor`. You will use `MyTaskSupervisor` in all of the examples below that need a task supervisor. +Start a [Supervisor](https://hexdocs.pm/elixir/Supervisor.html) with a child `Task.Supervisor` process named `MyTaskSupervisor`. Use a `:one_for_one` strategy. You will use `MyTaskSupervisor` in all of the examples below that need a task supervisor. + +
+Example Solution ```elixir children = [ @@ -164,13 +238,46 @@ children = [ Supervisor.start_link(children, strategy: :one_for_one) ``` +
+ +```elixir + +``` + Use `Task.Supervisor.async/3` and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to start a supervised task process that returns a random number after sleeping for two seconds. +
+Example Solution + ```elixir +task = Task.Supervisor.async(MyTaskSupervisor, fn -> + Process.sleep(2000) + Enum.random(1..10) +end) +Task.await(task) ``` -Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), `Task.Supervisor.async/3` and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to start one hundred supervised task processes that returns a list of doubled numbers from one `1..100`. i.e. `[2, 4, 6, 8, ..., 198, 200]`. +
+ +```elixir + +``` + +Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), `Task.Supervisor.async/3` and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to start one hundred supervised task processes that returns a random number. + +
+Example Solution + +```elixir +tasks = Enum.map(1..100, fn _ -> + Task.Supervisor.async(MyTaskSupervisor, fn -> Enum.random(1..10) end) +end) + +Task.await_many(tasks) +``` + +
```elixir @@ -180,13 +287,47 @@ Use `Task.Supervisor.start_child/3` to start a `:temporary` task that crashes af Use [Supervisor.which_children/1](https://hexdocs.pm/elixir/Supervisor.html#which_children/1) after six seconds to demonstrate that a `:temporary` process is not restarted. +
+Example Solution + +```elixir +task = + Task.Supervisor.start_child(MyTaskSupervisor, fn -> + Process.sleep(5000) + raise "error" + end) + +Process.sleep(6000) +Supervisor.which_children(MyTaskSupervisor) +``` + +
+ ```elixir ``` -Use `Task.Supervisor.start_child/3` with the `restart: :transient` option to start a `:transient` task that crashes after five seconds. +Use `Task.Supervisor.start_child/3` with the `restart: :transient` option to start a `:transient` task that crashes after one second. + +Use `IO.inspect/2` and `self/0` to demonstrate that the process is restarted, and that the `pid` has changed. + +
+Example Solution + +```elixir +task = + Task.Supervisor.start_child( + MyTaskSupervisor, + fn -> + IO.inspect(self(), label: "Process restarted") + Process.sleep(1000) + raise "error" + end, + restart: :transient + ) +``` -Use [Supervisor.which_children/1](https://hexdocs.pm/elixir/Supervisor.html#which_children/1) after six seconds to demonstrate that the `:transient` process is restarted. +
```elixir diff --git a/start.livemd b/start.livemd index 307c5e52f..6776c8573 100644 --- a/start.livemd +++ b/start.livemd @@ -479,7 +479,7 @@ For new students, we recommend that you [start here](reading/start_here.livemd). * Instruction * [PicChat: Messages](reading/pic_chat_messages.livemd) -!-- livebook:{"break_markdown":true} --> + ### LiveView Authentication diff --git a/utils/test/test_helper.exs b/utils/test/test_helper.exs index e4ba82d65..869559e70 100644 --- a/utils/test/test_helper.exs +++ b/utils/test/test_helper.exs @@ -1,3 +1 @@ ExUnit.start() -# avoids issues with module attributes not being loaded. -System.cmd("mix", ["compile", "--force"], env: [{"MIX_ENV", "test"}])