Dead routes detection?

A new developer discovering our codebase has stumbled upon a dead route in our router.

It took a bit of time to figure out, so I wonder if there are built-in ways to list all the dead routes (by dead route, I typically mean a valid controller, but an non-existing action specified).

An invalid controller will lead to a warning, but afaik not an non-existing action.

Are there existing tools for that? I wonder how hard it would be to add to Phoenix itself (presumably there are a number of edge cases to handle).

Thanks!

– Thibaut

2 Likes

The thing is, the module mentioned in the route is just a Plug, and the router calls call/2 on it. If the module is implemented using use Phoenix.Controller then it defines a call/2 implementation that pipes the request through the controller’s plug pipeline, and then calls action/2. The default implementation of action/2 then tries to call a function matching the name of the action set by the router, but it may be overridden.

So in an application that doesn’t do any customizations and sticks 100% to the Phoenix controller conventions it would be possible to flag router actions that point to a controller that does not appear to implement a function with the expected action name. But in a slightly more… adventurous application such a task would likely result in false positives. This is probably why Phoenix doesn’t try to warn out-of-the-box.

3 Likes

Thanks for the detailed information ; I wasn’t aware of the possibility to override action btw!

Indeed it can be more or less adventurous. I will probably use something more dirty (a bit of grep + module checks) to have a first idea.

Thanks!

I’ve used something like this in the past:

MyAppWeb.Router.__routes__()
|> Enum.reject(fn
  %{plug: controller, plug_opts: action} when is_atom(action) ->
    Code.ensure_loaded?(controller) && function_exported?(controller, action, 2)
  _ ->
    true
end)
|> Enum.each(fn
  %{path: path, plug: controller, plug_opts: action} ->
    IO.puts "Warning: #{inspect(controller)}.#{action}/2 not defined (#{path})"
end) 
14 Likes

Thanks, it worked perfectly well for most cases, and allowed me to detect an extra dead route. Thanks for sharing!

1 Like