Elixir: My global .iex.exs file

Before an iex shell starts, it looks like a file .iex.exs. It starts looking at the current directory and if it doesn’t find one, it looks at a global one at $HOME/.iex.exs (usually ~/.iex.exs). All the code inside the file is loaded and added to the IEx shell’s context, so all the modules imported, aliases, required, and bindings from the file are accessible inside the shell.

In my .iex.exs, I do the following:

  • Enable ansi for better characters support.
  • Add message_queue to the prompt. I like playing with multiple iex nodes and it’s handy to monitor inter-node communication.
  • Improve history_limit and inspect_limit.
  • Make eval results bright.
  • Add Plug support.
  • Add Phoenix support and detect phoenix apps.
  • Add Ecto support and detect ecto app.
  • Alias the main ecto Repo module.

NOTE: As I do more elixir, I keep improving my .iex.exs

Here is my global iex.exs file.

# Will be using `ANSI`
Application.put_env(:elixir, :ansi_enabled, true)

# Letting people know what iex.exs they are using
IO.puts IO.ANSI.magenta_background() <> IO.ANSI.bright() <> IO.ANSI.underline()
        # <> IO.ANSI.blink_slow()
        <> "Using global .iex.exs (located in ~/.iex.exs)" <> IO.ANSI.reset()

# Get queue length for the IEx process
# This is fun to see while playing with nodes
queue_length = fn ->
  self()
  |> Process.info()
  |> Keyword.get(:message_queue_len)
end

prefix = IO.ANSI.light_black_background() <> IO.ANSI.green() <> "%prefix"
         <> IO.ANSI.reset()
counter = IO.ANSI.light_black_background() <> IO.ANSI.green() <> "-%node-(%counter)"
          <> IO.ANSI.reset()
info = IO.ANSI.light_blue() <> "✉ #{queue_length.()}" <> IO.ANSI.reset()
last = IO.ANSI.yellow() <> "➤➤➤" <> IO.ANSI.reset()
alive = IO.ANSI.bright() <> IO.ANSI.yellow() <> IO.ANSI.blink_rapid() <> "⚡"
        <> IO.ANSI.reset()

default_prompt = prefix <> counter <> " | " <> info <> " | " <> last
alive_prompt = prefix <> counter <> " | " <> info <> " | " <> alive <> last

inspect_limit = 5_000
history_size = 100

eval_result = [:green, :bright]
eval_error = [:red, :bright]
eval_info = [:blue, :bright]

# Configuring IEx
IEx.configure [
  inspect: [limit: inspect_limit],
  history_size: history_size,
  colors: [
    eval_result: eval_result,
    eval_error: eval_error,
    eval_info: eval_info,
  ],
  default_prompt: default_prompt,
  alive_prompt: alive_prompt
]

# Phoenix Support
import_if_available Plug.Conn
import_if_available Phoenix.HTML

phoenix_app = :application.info()
  |> Keyword.get(:running)
  |> Enum.reject(fn {_x, y} ->
    y == :undefined
  end)
  |> Enum.find(fn {x, _y} ->
    x |> Atom.to_string() |> String.match?(~r{_web})
  end)

  # Check if phoenix app is found
case phoenix_app do
  nil -> IO.puts "No Phoenix App found"
  {app, _pid} ->
    IO.puts "Phoenix app found: #{app}"
    ecto_app = app
      |> Atom.to_string()
      |> (&Regex.split(~r{_web}, &1)).()
      |> Enum.at(0)
      |> String.to_atom()

    exists = :application.info()
      |> Keyword.get(:running)
      |> Enum.reject(fn {_x, y} ->
        y == :undefined
      end)
      |> Enum.map(fn {x, _y} -> x end)
      |> Enum.member?(ecto_app)

    # Check if Ecto app exists or running
    case exists do
      false -> IO.puts "Ecto app #{ecto_app} doesn't exist or isn't running"
      true ->
        IO.puts "Ecto app found: #{ecto_app}"

        # Ecto Support
        import_if_available Ecto.Query
        import_if_available Ecto.Changeset

        # Alias Repo
        repo = ecto_app |> Application.get_env(:ecto_repos) |> Enum.at(0)
        quote do
          alias unquote(repo), as: Repo
        end
    end
end

Let me know if you have ways in which this can be further improved