【问题标题】:How to dynamically generate a Phoenix controller function with a map argument?如何使用 map 参数动态生成 Phoenix 控制器函数?
【发布时间】:2018-01-24 00:59:39
【问题描述】:

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

根据我传入的数字,我希望它生成一个映射,其中包含许多名为“id1”、“id2”等的参数,一直到“id#{number}”。此映射将与通常的 Phoenix "conn" 一起成为参数列表的一部分。

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

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) 这是创建要进行模式匹配的地图的正确方法吗?

【问题讨论】:

  • 使用宏听起来像是过早的优化。你能通过迭代 params 映射中的元组来解决你的问题吗?
  • @J.RandomCoder - 您是否有链接或 sn-p 或有关“迭代参数映射中的元组”的进一步详细信息?
  • 另外,我还想了解更多关于元编程的知识,所以我想知道这个问题的答案?符合这一点。
  • 我的意思是你可以在运行时动态地对映射中的“项目”(元组)的数量做出反应,而不是在编译时为任何有意义的参数数量定义函数。无论第一个参数中有多少元组,以下 sn-p 都将起作用:Enum.map(%{"id1": 23, "id2": 42}, fn {key,value} -&gt; IO.puts("#{key}: #{value}") end).
  • 我希望在函数头上进行精确的模式匹配,因为这似乎也可以让我更轻松地利用 action_fallback 进行错误处理。所以如果不匹配def some_dynamic_fund(conn, %{"id1" =&gt; id1, "id2" =&gt; id2} = params), do: "hello" ,那么自然会回退到错误中。

标签: macros elixir phoenix-framework


【解决方案1】:

虽然您绝对可以通过这种方式使用宏,但您可能不应该这样做。这是一个使用 cmets 的有效解决方案:

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})

如您所见,宏会使代码更难理解。如果能在运行时解决,那绝对是首选。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多