Documenting multiple `handle_event/3` functions in LiveView

What is the preferred way to document multiple handle_event/3 functions in a LiveView module?

I currently have different @doc strings associated with each function, since they each handle a different task. After upgrading to Elixir v1.11, I’m seeing “redefining @doc attribute previously set…” warnings.

Is it preferable to have all varieties of handle_event/3 documented in one large @doc string? I get that this makes sense for multiple functions that handles one task, but doesn’t make sense to me in this case.

1 Like

handle_event/3 is a single function no matter how you turn it. The beam basically converts all the function heads into one function and a big case statement. If you handle different events you might make sections in the documentation, but generally callbacks are discouraged to be documented anyways (e.g. @impl true defaults to @doc false). Their documentation is usually in their related behaviour modules.

I do generally have the modules documented rather than the callbacks. However, in some instances I’m matching in a non-obvious way, that some context helps.

For instance:

def handle_info(%Message{room_id: room_id} = message, %Phoenix.LiveView.Socket{assigns: %{room_id: room_id}} = socket)
def handle_info(%Message{room_id: room_id} = message, socket)

Code is condensed, so it’s a bit more obvious here, but the first function matches a message passed to an active room, where the second one matches messages passed to in-active rooms. In these cases I think it’s helpful to have a doc with the callback. Maybe it makes more sense to have a comment here rather than a doc string.

If the functionality isn’t that different between them, sure it makes sense. If they are different and you need to actually generate public documentation for them, then you will probably need to break out into separate functions you can document.

def handle_event("widget_fizzled", params, socket), do: handle_widget_fizzled(params, socket)
def handle_event("fizzerator_gringled", params, socket), do: handle_fizzerator_gringled(params, socket)

@doc """
Handles the case when the widget was fizzled.
"""
def handle_widget_fizzled(_params, socket) do
  # ...
  {:noreply, socket}
end

@doc """
Handles the case when the fizzerator was gringled.
"""
def handle_fizzerator_gringled(_params, socket)
  # ...
  {:noreply, socket}
end

I see this pattern a lot in Erlang libraries - that is the handle*/[*] functions do very little inside the function body and actually just call out another function in the module. Then documentation can be provided on those functions instead.

1 Like

Ah, gringle fizzle! Good call, I don’t hate this approach :slightly_smiling_face:

2 Likes