feat: add source hyperlinks + remove thin from-source.md
Every source reference now links to elixir-lang/elixir at commit f4e1b34. 122 hyperlinks across 11 topic files. Added PATTERN_COMPLETE sentinels. Removed from-source.md (326 lines, shallow) — covered by existing files.
This commit is contained in:
+14
-12
@@ -6,7 +6,7 @@ Analysis of `lib/elixir/lib/gen_server.ex`, `lib/elixir/lib/agent.ex`, and relat
|
||||
|
||||
## Pattern 1: Client/Server API Separation
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:101-149` (documentation example)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L101](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L101) (documentation example)
|
||||
|
||||
**What it does:** Every GenServer module defines two distinct API layers — a **client API** (thin public functions that wrap `GenServer.call/cast`) and a **server API** (callback implementations). The client functions live in the same module but are clearly separated with comments.
|
||||
|
||||
@@ -120,7 +120,7 @@ end
|
||||
|
||||
## Pattern 2: `@impl true` Annotations on All Callbacks
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:41-60` (Stack example)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L41](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L41) (Stack example)
|
||||
|
||||
**What it does:** Every callback function is annotated with `@impl true`. This tells the compiler to verify that the function is a valid callback for the declared behaviour.
|
||||
|
||||
@@ -193,7 +193,7 @@ end
|
||||
|
||||
## Pattern 3: Guard-Protected `start_link`
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:101` and `lib/elixir/lib/agent.ex:28`
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L101](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L101) and [lib/elixir/lib/agent.ex#L28](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L28)
|
||||
|
||||
**What it does:** The `start_link` function uses guards to validate its arguments at the API boundary, failing fast with a clear error before any process spawning occurs.
|
||||
|
||||
@@ -261,7 +261,7 @@ end
|
||||
|
||||
## Pattern 4: `handle_continue` for Post-Init Work
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:520-528` (callback spec), `lib/elixir/lib/gen_server.ex:714-720` (handle_continue callback definition)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L520](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L520) (callback spec), [lib/elixir/lib/gen_server.ex#L714](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L714) (handle_continue callback definition)
|
||||
|
||||
**What it does:** `init/1` can return `{:ok, state, {:continue, continue_arg}}`, which causes `handle_continue/2` to be invoked immediately after init completes. This allows splitting initialization into a synchronous part (that unblocks the supervisor) and an asynchronous continuation.
|
||||
|
||||
@@ -352,7 +352,7 @@ end
|
||||
|
||||
## Pattern 5: Timeout-Based Idle Shutdown
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:335-380`
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L335](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L335)
|
||||
|
||||
**What it does:** Callbacks can return a timeout value (milliseconds) as the last element of the return tuple. If no message arrives within that time, `handle_info(:timeout, state)` is called. This enables idle process cleanup.
|
||||
|
||||
@@ -458,7 +458,7 @@ end
|
||||
|
||||
## Pattern 6: Periodic Work via `Process.send_after`
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:298-332` (Periodically example in docs)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L298](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L298) (Periodically example in docs)
|
||||
|
||||
**What it does:** A GenServer schedules periodic work by sending itself a message via `Process.send_after` in `init/1`, then rescheduling in `handle_info`. This creates a self-sustaining periodic loop.
|
||||
|
||||
@@ -573,7 +573,7 @@ end
|
||||
|
||||
## Pattern 7: Call vs Cast Decision (Synchronous vs Asynchronous)
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:83-90` (docs), `lib/elixir/lib/agent.ex:368-378` (Agent.update uses call, Agent.cast uses cast)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L83](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L83) (docs), [lib/elixir/lib/agent.ex#L368](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L368) (Agent.update uses call, Agent.cast uses cast)
|
||||
|
||||
**What it does:** The Elixir team uses `call` (synchronous) for operations where the client needs confirmation or a return value, and `cast` (fire-and-forget) only when the client genuinely doesn't care about the outcome.
|
||||
|
||||
@@ -640,7 +640,7 @@ end
|
||||
|
||||
## Pattern 8: Default Callback Implementations with Clear Error Messages
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:902-993` (`__using__` macro)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L902](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L902) (`__using__` macro)
|
||||
|
||||
**What it does:** `use GenServer` injects default implementations of `handle_call`, `handle_cast`, `handle_info`, `terminate`, and `code_change`. The defaults for `handle_call` and `handle_cast` raise with a descriptive error message including the process name. `handle_info` logs an error. All are `defoverridable`.
|
||||
|
||||
@@ -747,7 +747,7 @@ end
|
||||
|
||||
## Pattern 9: `child_spec/1` Generation and Customization via `use` Options
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:900-921`, `lib/elixir/lib/agent.ex:206-218`, `lib/elixir/lib/task.ex:282-292`
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L900](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L900), [lib/elixir/lib/agent.ex#L206](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L206), [lib/elixir/lib/task.ex#L282](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L282)
|
||||
|
||||
**What it does:** Each `use GenServer/Agent/Task` generates a `child_spec/1` function with sensible defaults that can be customized via options passed to `use`. The child spec is a map with `:id`, `:start`, `:restart`, `:shutdown`, and `:type`.
|
||||
|
||||
@@ -852,7 +852,7 @@ end
|
||||
|
||||
## Pattern 10: Agent as Minimal State Wrapper (GenServer Under the Hood)
|
||||
|
||||
**Source:** `lib/elixir/lib/agent.ex:1-60` (module docs), `lib/elixir/lib/agent.ex:246-250` (implementation)
|
||||
**Source:** [lib/elixir/lib/agent.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L1) (module docs), [lib/elixir/lib/agent.ex#L246](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L246) (implementation)
|
||||
|
||||
**What it does:** Agent is implemented entirely in terms of `GenServer.start_link(Agent.Server, fun, options)`. It's a thin abstraction that provides `get/update/get_and_update/cast` over GenServer's `call/cast`.
|
||||
|
||||
@@ -947,7 +947,7 @@ end
|
||||
|
||||
## Pattern 11: Name Registration via `:via` Tuple
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:1087-1107` (do_start implementation), `lib/elixir/lib/gen_server.ex:230-250` (documentation)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L1087](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L1087) (do_start implementation), [lib/elixir/lib/gen_server.ex#L230](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L230) (documentation)
|
||||
|
||||
**What it does:** GenServer supports four naming schemes: `nil` (anonymous), atom (local), `{:global, term}` (cluster-wide), and `{:via, module, term}` (pluggable registry). The implementation delegates to `:gen.start` with the appropriate format.
|
||||
|
||||
@@ -1032,7 +1032,7 @@ end
|
||||
|
||||
## Pattern 12: GenServer as Anti-Pattern — Don't Use Processes for Code Organization
|
||||
|
||||
**Source:** `lib/elixir/lib/gen_server.ex:381-415` ("When (not) to use a GenServer" section)
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L381](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L381) ("When (not) to use a GenServer" section)
|
||||
|
||||
**What it does:** The Elixir team explicitly documents that GenServer should NOT be used for code organization. A process must model a runtime property: mutable state, concurrency boundary, or failure isolation.
|
||||
|
||||
@@ -1108,3 +1108,5 @@ end
|
||||
**Better alternative:** If you need mutable state (counters, connections, caches), you need a process. The rule is "don't use processes for code organization" — not "never use processes." A rate limiter with shared counters is a legitimate use of GenServer (or ETS owned by a GenServer).
|
||||
|
||||
**Why:** The pattern cuts both ways. Over-using GenServer creates bottlenecks. Under-using it means reinventing state management poorly. The litmus test: does the state need to survive between function calls? Does access need serialization? If yes, you need a process.
|
||||
|
||||
<!-- PATTERN_COMPLETE -->
|
||||
|
||||
Reference in New Issue
Block a user