Writing custom Typespecs

I am trying to write custom type specs but some functions appear to be quiet complex since I am not very much familiar with Elixir yet.

iex> deliver_update_email_instructions(account, email, &Routes.account_update_email_url(conn, :edit, &1))
          {:ok, %{to: ..., body: ...}}

I am unable to find this code &Routes.account_update_email_url(conn, :edit, &1) in my project, hence not able to figure out how to represent it in the form of typespec. Any inputs how to write typespec for
&Routes.account_update_email_url(conn, :edit, &1) ??

You could use function()

A shape like &...&1... with no &2 is an anonymous function of arity 1. You could write the typespec as:
(any -> binary) or even narrow that initial any depending on what type your record IDs are (string / integer / UUID etc).

The reason for not finding this code is that the function is automatically generated by the routes helper from your router.ex configuration. [guide] [module]

Helpers are automatically generated based on the controller name. For example, the route:

get "/pages/:page", PageController, :show

will generate the following named helper:

MyAppWeb.Router.Helpers.page_path(conn_or_endpoint, :show, "hello")
"/pages/hello"

If you look at the signature for the function you’ll see something like

iex> h Routes.account_update_email_url

             def account_update_email_url(conn_or_endpoint, action)              

         def account_update_email_url(conn_or_endpoint, action, params)   
  • conn is a Plug.conn.t() type, but conn_or_endpoint means you can also provide an endpoint atom, such as MyAppWeb.Endpoint, so Plug.conn.t() | atom()
  • :edit is an atom()
  • &1 represents the query params, which can be keyword() | map()

Putting that together with @al2o3cr 's answer you can try (Plug.conn.t() | atom(), atom(), keyword() | map() -> binary()) for a fuller signature.

There may be a few nuances I’m missing but that should get you most of the way there.

1 Like