defmodule Combo.Endpoint.SyncCodeReloadPlug do @moduledoc """ Wraps an endpoint, attempting to sync with `Combo.CodeReloader` if an exception is raised which indicates that we may be in the middle of a reload. We detect this by looking at the raised exception and seeing if it indicates that the endpoint is not defined. This indicates that the code reloader may be midway through a compile, and that we should attempt to retry the request after the compile has completed. This is also why this must be implemented in a separate module (one that is not recompiled in a typical code reload cycle), since otherwise it may be the case that the endpoint itself is not defined. """ @behaviour Plug def init({endpoint, opts}), do: {endpoint, endpoint.init(opts)} def call(conn, {endpoint, opts}), do: do_call(conn, endpoint, opts, true) defp do_call(conn, endpoint, opts, retry?) do try do endpoint.call(conn, opts) rescue exception in [UndefinedFunctionError] -> case exception do %UndefinedFunctionError{module: ^endpoint} when retry? -> # Sync with the code reloader and retry once Combo.CodeReloader.sync() do_call(conn, endpoint, opts, false) exception -> reraise(exception, __STACKTRACE__) end end end end