From 7c408c9644431573e2b1ce498fa135e3efb40657 Mon Sep 17 00:00:00 2001 From: John McGuin Date: Thu, 5 Sep 2024 16:24:55 -0600 Subject: [PATCH 1/2] feat: supports match, to: functional_plug follows pattern in `forward` macro to check if the target is an Elixir module or function. --- lib/plug/router.ex | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/plug/router.ex b/lib/plug/router.ex index 4c9c07f9..0c754b74 100644 --- a/lib/plug/router.ex +++ b/lib/plug/router.ex @@ -267,22 +267,31 @@ defmodule Plug.Router do init_mode = Module.get_attribute(env.module, :plug_builder_opts)[:init_mode] defs = - for {callback, {mod, opts}} <- router_to do - if init_mode == :runtime do - quote do - defp unquote(callback)(conn, _opts) do - unquote(mod).call(conn, unquote(mod).init(unquote(Macro.escape(opts)))) + for {callback, {target, opts}} <- router_to do + case {init_mode, Atom.to_string(target)} do + {:runtime, "Elixir." <> _} -> + quote do + defp unquote(callback)(conn, _opts) do + unquote(target).call(conn, unquote(target).init(unquote(Macro.escape(opts)))) + end + end + + {_, "Elixir." <> _} -> + opts = target.init(opts) + + quote do + defp unquote(callback)(conn, _opts) do + require unquote(target) + unquote(target).call(conn, unquote(Macro.escape(opts))) + end end - end - else - opts = mod.init(opts) - - quote do - defp unquote(callback)(conn, _opts) do - require unquote(mod) - unquote(mod).call(conn, unquote(Macro.escape(opts))) + + _ -> + quote do + defp unquote(callback)(conn, _opts) do + unquote(target)(conn, unquote(Macro.escape(opts))) + end end - end end end @@ -575,6 +584,7 @@ defmodule Plug.Router do router_to = Module.get_attribute(module, :plug_router_to) callback = :"plug_router_to_#{map_size(router_to)}" router_to = Map.put(router_to, callback, {to, init_opts}) + Module.put_attribute(module, :plug_router_to, router_to) {Macro.var(callback, nil), options} end From aed4519eafccf69326d4e99a8b04cb1e08dff38b Mon Sep 17 00:00:00 2001 From: John McGuin Date: Thu, 5 Sep 2024 16:26:50 -0600 Subject: [PATCH 2/2] test: match, to: functional_plug --- test/plug/router_test.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/plug/router_test.exs b/test/plug/router_test.exs index b6fed1e3..3886fba2 100644 --- a/test/plug/router_test.exs +++ b/test/plug/router_test.exs @@ -206,12 +206,18 @@ defmodule Plug.RouterTest do forward "/plug/init_opts", to: plug, init_opts: opts, private: %{baz: :qux} forward "/plug/forward_local", to: :forward_local + match "/plug/match_local", to: :match_local forward "/plug/forward_local_opts", to: :forward_local, init_opts: opts, private: %{baz: :qux} + match "/plug/match_local_opts", to: :match_local, init_opts: opts, private: %{baz: :qux} def forward_local(conn, opts) do resp(conn, 200, "#{inspect(opts)}") end + def match_local(conn, opts) do + resp(conn, 200, "#{inspect(opts)}") + end + match _ do resp(conn, 404, "oops") end @@ -586,12 +592,23 @@ defmodule Plug.RouterTest do assert conn.resp_body == "[]" end + test "matches to a function plug" do + conn = call(Sample, conn(:get, "/plug/match_local")) + assert conn.resp_body == "[]" + end + test "forwards to a function plug with options" do conn = call(Sample, conn(:get, "/plug/forward_local_opts")) assert conn.private[:baz] == :qux assert conn.resp_body == ":hello" end + test "matches to a function plug with options" do + conn = call(Sample, conn(:get, "/plug/match_local_opts")) + assert conn.private[:baz] == :qux + assert conn.resp_body == ":hello" + end + test "emit start and stop event when router dispatches" do start_router_id = {:start, :rand.uniform(100)} stop_router_id = {:stop, :rand.uniform(100)}