Prev Next

Organizing Code in Elixir

Exercise: File Handler Module

If you’d like some practice creating files and modules, put all the handle_file functions into their own module named FileHandler, for example:

Solution:

defmodule Servy.FileHandler do
  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
end

You’ll need to then import this new module in the existing Handler module:

Solution :

import Servy.FileHandler, only: [handle_file: 2]

Import Options

By default, when you use import it imports all the module’s functions and macros into the current namespace. As we did in the video, you can use the only option to explicitly import specific functions:

import Servy.Plugins, only: [rewrite_path: 1, log: 1, track: 1]
import Servy.Parser, only: [parse: 1]

Using only is optional, but it’s recommended to avoid importing all functions into the current namespace and potentially causing name collisions. Conversely, there’s a rarely used except option to import all functions except those that are specified.

For completeness, you might bump into two other variations worth noting:

  • import SomeModule, only: :functions
  • import SomeModule, only: :macros

Using the :functions atom imports only functions, whereas using the :macros atom only imports macros.

Alternate Way to Get Absolute Paths

In the previous video, we expanded the path to the pages directory relative to the directory of the current file (__DIR__) like so:

@pages_path Path.expand("../../pages", __DIR__)

Now that we’re running the application using iex -S mix, you can optionally expand the path using a slightly different approach:

@pages_path Path.expand("pages", File.cwd!)

We’ve removed the ../../ part of the first argument and called the File.cwd! function as the second argument. File.cwd! returns the current working directory.

Neat Trick: Mix always runs from the root project directory, which is the top-level servy directory in this case. So, calling File.cwd! always returns the top-level servy directory. The pages directory is just one level down relative to that.

About the ”!” in Function Names

This is the first time we’ve seen a function name ending with !. Generally speaking, this is a naming convention that conveys that the function will raise an exception if it fails. For instance, calling File.cwd! is the same as calling File.cwd, but it raises an exception if there’s a problem.

The Kernel Module

Functions and macros defined in Elixir’s Kernel module are automatically imported into every module for convenience. It’s worth scanning through these functions and macros.

Code So Far

The code for this section is in the organizing-code directory found within the video-code directory of the code bundle

Prev Next