Elixir basics · Lesson 30 (open contents)
Package the edge and leave clues
Keep CLI parsing thin, log useful context, and build the escript.
Run this first
Run it once, change one input, and compare the new result.
# lib/cleaner/cli.ex
defmodule Cleaner.CLI do
require Logger
def main([path]) do
with {:ok, text} <- File.read(path),
{:ok, records} <- parse_lines(String.split(text, "\n")),
{:ok, counts} <- Cleaner.run(records) do
Logger.info("cleaned records path=#{inspect(path)} count=#{length(records)}")
IO.inspect(counts, label: "levels")
:ok
else
{:error, reason} -> fail(reason)
end
end
def main(_args), do: {:error, :usage}
defp parse_lines(lines) do
Enum.reduce_while(lines, {:ok, []}, fn line, {:ok, records} ->
case Cleaner.parse_line(line) do
{:ok, record} -> {:cont, {:ok, [record | records]}}
:skip -> {:cont, {:ok, records}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
|> then(fn
{:ok, records} -> {:ok, Enum.reverse(records)}
error -> error
end)
end
defp fail(reason) do
Logger.error("clean_failed reason=#{inspect(reason)}")
{:error, reason}
end
end- The CLI reads one file, calls the project API, logs the path and accepted record count, and prints the level summary.
Read from the first line down
- Read
Logger.info/1: Write one useful operational message. - Read
main([path]): Accept exactly one file argument. - Read
mix escript.build: Package the command after tests pass.
Symbols are not secret signs
Logger.info/1Write one useful operational message.
main([path])Accept exactly one file argument.
mix escript.buildPackage the command after tests pass.
Match each name to its meaning
thin entry point
A CLI entry point parses arguments, calls the API, prints a result, and does little else.
structured context
Log fields such as path and count separately from the message.
privacy boundary
Operational clues should not expose full user data or secrets.
Why these forms are useful
Run the example first. Predict one result, then change one input and run it again.
A CLI entry point parses arguments, calls the API, prints a result, and does little else.
Log fields such as path and count separately from the message.
Close the answer and try
Return a usage error when the CLI receives no path.
def main([]), do: ____Target result: Return {:error, :usage}.
Stuck? Read one hint
Keep the error tagged.
After you run it, see one answer
def main([]), do: {:error, :usage}Think about it: Should a log line copy every source line?
No. Record enough context to diagnose the run without exposing full input.
Remember these three lines
- 1
Keep CLI code thin.
- 2
Log decisions and counts.
- 3
Run tests before packaging.