Ookma-Kyi
I am currently enrolled in the Pragmatic Studio Elixir & OTP course and have just completed the section on module attributes. I have the following code:
defmodule Servy.Handler do
@moduledoc "Handles HTTP requests"
@pages_path Path.expand("../../pages", __DIR__)
@doc "Transforms the request into a response."
def handle(request) do
request
|> parse
|> rewrite_path
|> log
|> route
|> track
|> format_response
end
def emojify(%{status: 200} = conv) do
body = ":) " <> conv.resp_body <> " :)"
%{conv | resp_body: body}
end
def emojify(conv), do: conv
def track(%{status: 404, path: path} = conv) do
IO.puts("Warning: #{path} is on the loose!")
conv
end
@doc "Logs 404 requests"
def track(conv), do: conv
def rewrite_path(%{path: "/wildlife"} = conv) do
%{conv | path: "/wildthings"}
end
def rewrite_path(%{path: "/bears?id=" <> id} = conv) do
%{conv | path: "/bears/#{id}"}
end
def rewrite_path(conv), do: conv
def log(conv), do: IO.inspect(conv)
def parse(request) do
# TODO Parse the request string into a map
[method, path, _] =
request
|> String.split("\n")
|> List.first()
|> String.split(" ")
%{method: method, path: path, resp_body: "", status: nil}
end
def route(%{method: "GET", path: "/wildthings"} = conv) do
%{conv | status: 200, resp_body: "Bears, Lions, Tigers"}
end
def route(%{method: "GET", path: "/bears"} = conv) do
%{conv | status: 200, resp_body: "Teddy, Smokey, Paddington"}
end
def route(%{method: "GET", path: "/bears/new"} = conv) do
@pages_path
|> Path.join("form.html")
|> File.read()
|> handle_file(conv)
end
def route(%{method: "GET", path: "/bears/" <> id} = conv) do
%{conv | status: 200, resp_body: "Bear #{id}"}
end
def route(%{method: "GET", path: "/pages/" <> file} = conv) do
Path.expand("../../pages", __DIR__)
|> Path.join(file <> ".html")
|> File.read()
|> handle_file(conv)
end
def route(%{method: "GET", path: "/about"} = conv) do
Path.expand("../../pages", __DIR__)
|> Path.join("about.html")
|> File.read()
|> handle_file(conv)
end
def route(%{method: "DELETE", path: "/bears/" <> _id} = conv) do
%{conv | status: 403, resp_body: "Deleting a bear is forbidden!"}
end
def route(%{path: path} = conv) do
%{conv | status: 404, resp_body: "No #{path} here"}
end
def handle_file({:ok, content}, conv) do
%{conv | status: 200, resp_body: content}
end
def handle_file({:error, :enoent}, conv) do
%{conv | status: 404, resp_body: "File not found"}
end
def handle_file({:error, reason}, conv) do
%{conv | status: 500, resp_body: "File Error: #{reason}"}
end
def format_response(conv) do
# TODO: Use values in the map to create an HTTP response string:
"""
HTTP/1.1 #{conv.status} #{status_reason(conv.status)}
Content-Type: text/html
Content-Length: #{byte_size(conv.resp_body)}
#{conv.resp_body}
"""
end
defp status_reason(code) do
%{
200 => "OK",
201 => "Created",
401 => "Unauthorized",
403 => "Forbidden",
404 => "Not Found",
500 => "Internal Server Error"
}[code]
end
end
request = """
GET /wildthings HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /bears HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /bigfoot HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /bears/1 HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /wildlife HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /bears?id=1 HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /about HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
request = """
GET /bears/new HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/1.0
Accept: */*
"""
response = Servy.Handler.handle(request)
IO.puts(response)
However, when I try to see the generated documentation as an exercise, I get an error:
Any ideas?
Trending in Questions
Hi all,
I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
I’m very new to Elixir, so sorry if this is a silly question:
I am wondering what happens to the mailbox of a process when the machine i...
New
How do I tell Hologram not to escape HTML when substituting into templates?
{Textile.render(@product.long_description)}
escapes the HTM...
New
Background
When trying to execute mix release on a Windows 11 machine for a Phoenix project I get the following error:
* assembling mark...
New
I’d like to create a file with 600 permissions (rw-------), but there’s a race condition if I create it and then change the permissions. ...
New
Is there a way to import record definitions in the iex shell for pretty printing (similar to the rr command in the erl shell)? This woul...
New
When I use Mix.install with a git dependency, using git@github.com:... how do I change the SSH identity used?
I’ve got two Github accoun...
New
Other Trending Topics
Hey there,
I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first.
...
New
Hi everyone :waving_hand:
I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly)
AI dream has many faces, but general...
New
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #code-sync
- #discussion
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #ai
- #umbrella
- #elixirconf-us
- #full-time-contract
- #podcasts-by-brainlid
- #blog-post
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #elixirconf-eu
- #processes
- #security
- #api
- #forms
- #performance











Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
garrison
Someone else can probably give you a more detailed answer but I think the problem here is that you are running a script (
.exsfile). Scripts as I understand are only compiled into memory so there’s no beam file on disk to read the docs from.Ookma-Kyi
Actually, it’s an .ex file (executable).
sodapopcan
To be clear,
.exis not an executable, it’s just an elixir file..beamare the bytecode files.In short, modules in iex are loaded into memory so not compiled with docs. I’m not sure if there is a way to do so or not. If you have a mix project, though, and load it in with
iex -S mixthen you’ll be able to useh.garrison
How are you compiling it? If you are doing
c("handler.ex")in iex it still defaults to in-memory regardless of whether it’s a.exsfile.garrison
Looks like
c/2accepts apathas the second argument.sodapopcan
Oh neat! They’ve thought of everything