Skip to content

Commit ab8fef5

Browse files
committed
improvements
1 parent aaa7352 commit ab8fef5

File tree

2 files changed

+49
-49
lines changed

2 files changed

+49
-49
lines changed

getting-started/mix-otp/genserver.markdown

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ redirect_from: /getting_started/mix_otp/3.html
88

99
{% include toc.html %}
1010

11-
In the previous chapter we used agents to represent our buckets. In the first chapter, we specified we would like to name each bucket so we can do the following:
11+
In the [previous chapter](/getting-started/mix-otp/agent.html) we used agents to represent our buckets. In the first chapter, we specified we would like to name each bucket so we can do the following:
1212

1313
```elixir
1414
CREATE shopping
@@ -149,20 +149,20 @@ Our test should pass right out of the box!
149149
To shutdown the registry, we are simply sending a `:shutdown` signal to its process when our test finishes. While this solution is ok for tests, if there is a need to stop a `GenServer` as part of the application logic, it is best to define a `stop/1` function that sends a `call` message causing the server to stop:
150150

151151
```elixir
152-
## Client API
152+
## Client API
153153

154-
@doc """
155-
Stops the registry.
156-
"""
157-
def stop(server) do
158-
GenServer.call(server, :stop)
159-
end
154+
@doc """
155+
Stops the registry.
156+
"""
157+
def stop(server) do
158+
GenServer.call(server, :stop)
159+
end
160160

161-
## Server Callbacks
161+
## Server Callbacks
162162

163-
def handle_call(:stop, _from, state) do
164-
{:stop, :normal, :ok, state}
165-
end
163+
def handle_call(:stop, _from, state) do
164+
{:stop, :normal, :ok, state}
165+
end
166166
```
167167

168168
In the example above, the new `handle_call/3` clause is returning the atom `:stop`, along side the reason the server is being stopped (`:normal`), the reply `:ok` and the server state.
@@ -172,12 +172,12 @@ In the example above, the new `handle_call/3` clause is returning the atom `:sto
172172
Our registry is almost complete. The only remaining issue is that the registry may become stale if a bucket stops or crashes. Let's add a test to `KV.RegistryTest` that exposes this bug:
173173

174174
```elixir
175-
test "removes buckets on exit", %{registry: registry} do
176-
KV.Registry.create(registry, "shopping")
177-
{:ok, bucket} = KV.Registry.lookup(registry, "shopping")
178-
Agent.stop(bucket)
179-
assert KV.Registry.lookup(registry, "shopping") == :error
180-
end
175+
test "removes buckets on exit", %{registry: registry} do
176+
KV.Registry.create(registry, "shopping")
177+
{:ok, bucket} = KV.Registry.lookup(registry, "shopping")
178+
Agent.stop(bucket)
179+
assert KV.Registry.lookup(registry, "shopping") == :error
180+
end
181181
```
182182

183183
The test above will fail on the last assertion as the bucket name remains in the registry even after we stop the bucket process.
@@ -202,43 +202,43 @@ Note `Process.monitor(pid)` returns a unique reference that allows us to match u
202202
Let's reimplement the server callbacks to fix the bug and make the test pass. First, we will modify the GenServer state to two dictionaries: one that contains `name -> pid` and another that holds `ref -> name`. Then we need to monitor the buckets on `handle_cast/2` as well as implement a `handle_info/2` callback to handle the monitoring messages. The full server callbacks implementation is shown below:
203203

204204
```elixir
205-
## Server callbacks
205+
## Server callbacks
206206

207-
def init(:ok) do
208-
names = HashDict.new
209-
refs = HashDict.new
210-
{:ok, {names, refs}}
211-
end
207+
def init(:ok) do
208+
names = HashDict.new
209+
refs = HashDict.new
210+
{:ok, {names, refs}}
211+
end
212212

213-
def handle_call({:lookup, name}, _from, {names, _} = state) do
214-
{:reply, HashDict.fetch(names, name), state}
215-
end
213+
def handle_call({:lookup, name}, _from, {names, _} = state) do
214+
{:reply, HashDict.fetch(names, name), state}
215+
end
216216

217-
def handle_call(:stop, _from, state) do
218-
{:stop, :normal, :ok, state}
219-
end
217+
def handle_call(:stop, _from, state) do
218+
{:stop, :normal, :ok, state}
219+
end
220220

221-
def handle_cast({:create, name}, {names, refs}) do
222-
if HashDict.has_key?(names, name) do
223-
{:noreply, {names, refs}}
224-
else
225-
{:ok, pid} = KV.Bucket.start_link()
226-
ref = Process.monitor(pid)
227-
refs = HashDict.put(refs, ref, name)
228-
names = HashDict.put(names, name, pid)
229-
{:noreply, {names, refs}}
221+
def handle_cast({:create, name}, {names, refs}) do
222+
if HashDict.has_key?(names, name) do
223+
{:noreply, {names, refs}}
224+
else
225+
{:ok, pid} = KV.Bucket.start_link()
226+
ref = Process.monitor(pid)
227+
refs = HashDict.put(refs, ref, name)
228+
names = HashDict.put(names, name, pid)
229+
{:noreply, {names, refs}}
230+
end
230231
end
231-
end
232232

233-
def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
234-
{name, refs} = HashDict.pop(refs, ref)
235-
names = HashDict.delete(names, name)
236-
{:noreply, {names, refs}}
237-
end
233+
def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
234+
{name, refs} = HashDict.pop(refs, ref)
235+
names = HashDict.delete(names, name)
236+
{:noreply, {names, refs}}
237+
end
238238

239-
def handle_info(_msg, state) do
240-
{:noreply, state}
241-
end
239+
def handle_info(_msg, state) do
240+
{:noreply, state}
241+
end
242242
```
243243

244244
Observe that we were able to considerably change the server implementation without changing any of the client API. That's one of the benefits of explicitly segregating the server and the client.

getting-started/processes.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ iex> receive do
6161
"world"
6262
```
6363

64-
When a message is sent to a process, the message is stored in the process mailbox. The `receive/1` block goes through the current process mailbox searching for a message that matches any of the given patterns. `receive/1` supports many clauses, like `case/2`, as well as guards in the clauses.
64+
When a message is sent to a process, the message is stored in the process mailbox. The `receive/1` block goes through the current process mailbox searching for a message that matches any of the given patterns. `receive/1` supports guards and many clauses, such as `case/2`.
6565

6666
If there is no message in the mailbox matching any of the patterns, the current process will wait until a matching message arrives. A timeout can also be specified:
6767

0 commit comments

Comments
 (0)