如何使用map参数动态生成Phoenix控制器函数?

时间:2018-01-24 00:59:40

标签: macros elixir phoenix-framework

所以我正在使用Phoenix 1.3,我创建了一个宏来生成一个函数并将其注入控制器。

根据我传入的数字,我希望它生成一个包含许多参数的地图,这些参数名为“id1”,“id2”等,一直到“id#{number}”。这张地图将与通常的凤凰城“conn”一起成为参数列表的一部分。

所以我想生成一个这样的方法来进行模式匹配,并且可以执行“some stuff”:

def index(conn, %{"id1" => id1, "id2" => id2}) do
  # some stuff
end

当我调用宏create_some_function_by_number("index", 2)时。

我的宏看起来像:

defmacro create_some_function_by_number(name, num) do
    params =
      for n <- 1..num, do: %{"id#{n}" => Macro.var(:"id#{n}", nil)}
      |> Map.new

    quote do
      def unquote(:"#{name}")(unquote(Macro.escape(params)) do
         # some code here for the index action
      end
    end
  end

1)如何将“conn”注入到功能头中,以便与模式匹配?

2)这是创建与图案匹配的地图的正确方法吗?

1 个答案:

答案 0 :(得分:2)

虽然你绝对可以以这种方式使用宏,但你可能不应该这样做。这是一个有评论的工作解决方案:

defmodule MyMacro do
  defmacro create_some_function_by_number(name, num, do: block) do
    params =
      for n <- 1..num do
        {"id#{n}", Macro.var(:"id#{n}", nil)}
      end

    # We can't call Macro.escape because it is for escaping values.
    # In this case, we have a mixture of values "id#{n}" and
    # expressions "Macro.var(...)", so we build the map AST by hand.
    pattern =
      {:%{}, [], params}

    conn =
      Macro.var(:conn, nil)

    quote do
      def unquote(:"#{name}")(unquote(conn), unquote(pattern)) do
        unquote(block)
      end
    end
  end
end

defmodule MyExample do
  import MyMacro

  create_some_function_by_number :index, 2 do
    {conn, id1 + id2}
  end
end

IO.inspect MyExample.index(:conn, %{"id1" => 1, "id2" => 2})

正如您所看到的,宏可以使代码更难理解。如果你可以在运行时解决它,那肯定是首选。