# Storing temporary data in ETS - for example, using ETS in a web wizard

I'm learning the Elixir language and the Phoenix web framework. And I had an idea. What if, in a web wizard (multi-step form) scenario, I stored the intermediate data in a ETS table instead of the relational database?

So I wrote this controller:

defmodule PlayPhoenixWeb.ThingsController do
use PlayPhoenixWeb, :controller
require Logger
@moduledoc false

@form_ets :things_forms

alias PlayPhoenixWeb.Router.Helpers, as: Routes
alias PlayPhoenixWeb.Endpoint

def step_1_display(conn, _params) do
render(conn, "form_1.html")
end

def step_1_process(conn, params) do
data = Map.take(params, ["name", "vcode"])
key = make_key()
write_data(key, data)
Logger.debug inspect(data)
redirect(conn, to: Routes.thing_form_2_path(Endpoint, :step_2_display, key))
end

def step_2_display(conn, %{"key" => key}) do
render(conn, "form_2.html", key: key)
end

def step_2_process(conn, %{"key" => key} = params) do
{:ok, data} = get_data(key)
full_data = Map.take(params, ["description", "value"]) |> Map.merge(data)
# In production, processing full data would happen here
Logger.debug inspect(full_data)
redirect(conn, to: Routes.page_path(Endpoint, :index))
end

# This method is called by the Application on startup
def init_ets(), do: :ets.new(@form_ets, [:public, :named_table])

defp make_key(), do: :crypto.hash(:md5, DateTime.utc_now() |> DateTime.to_iso8601) |> :base64.encode

defp get_data(key) do
case :ets.lookup(@form_ets, key) do
[{^key, value}] -> {:ok, value}
_ -> {:not_found}
end
end

defp write_data(key, data) do
:ets.insert(@form_ets, {key, data})
end

defp delete_data(key), do: :ets.delete(@form_ets, key)

end


(It's purely for the sake of playing/research, and currently doesn't store or process any data).

Is this the Elixir way/the Phoenix way?

Thank you very much.

• Hi, you should try to explain in a little more detail what your code is supposed to do :) – IEatBagels Feb 26 '19 at 18:30

Yes, using ETS for this sort of temporary data is totally fine, as long as you realize of course that all that intermediate state is lost whenever you restart your server. In fact, if you dig into Phoenix and Elixir a bit, you will see a lot of ETS usage - it's not so much a thing you turn to in exceptional circumstances, but more a tool in the extensive toolbox at the same level of, say, GenServer.
Can't say much on Elixir, but, make_key - why go through the trouble and use MD5 on a string format of the current time (at what resolution? seconds? so you'll get duplicates whenever this is called quickly enough in rapid succession) when you can just generate a bunch of random numbers of a fixed length instead? Additionally you might just be able to pass in the source of the random numbers too and then suddenly it's way more testable (because 1. you're not relying on the current time, 2. you're not on a global random number generator).
• :erlang.unique_integer/0 will do the job for unique keys that are VM loocal. – cdegroot Mar 7 '19 at 17:43