如何在包含的模块中获取调用者模块名称?

时间:2016-08-30 07:35:56

标签: elixir phoenix-framework ecto

如何在包含MyApp.MyUniversalModule中获取调用者模块并获取field_name参数?

defmodule MyApp.MyUniversalModule do
  def gen_and_check_unique(changeset, field_name) do
    token = random_string()
    # MyApp.MyQueryableModule |> Repo.get_by(field_name, token)
  end

  def random_string(length \\ 8) do
    :crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length)
  end
end

defmodule MyApp.MyQueryableModule do
  use MyApp.Web, :model
  import MyApp.MyUniversalModule

  schema "items" do
    field :name, :string
    field :token, :string
  end

  def changeset(model, params) do
    model
    |> cast(params, ~w(name), ~w())
    |> gen_and_check_unique(:token)
  end
end

2 个答案:

答案 0 :(得分:3)

编辑:虽然这个答案回答了问题的标题,但是你的代码在几个地方是不正确的,正如@ stephen_m的答案所指出的那样。

虽然你can在运行时获取调用堆栈并从中提取调用模块,但它效率低下,通常不推荐使用。 Elixir中的惯用方法是使用宏和__using__钩子。 Ecto uses the same method for Repo以及许多其他模块。

基本上,您可以在__using__宏的引号内定义要注入调用者模块的所有函数。在这种情况下,这看起来像(未经测试):

defmodule MyApp.MyUniversalModule do
  defmacro __using__(_opts) do
    quote do
      def gen_and_check_unique(changeset, field_name) do
        token = random_string()
        __MODULE__ |> Repo.get_by(field_name, token)
      end

      def random_string(length \\ 8) do
        :crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length)
      end
    end
  end
end

然后,在MyApp.MyQueryableModule中,更改:

import MyApp.MyUniversalModule

use MyApp.MyUniversalModule

您可能希望不会使用random_string污染调用模块,在这种情况下您可以执行(再次未经测试):

defmodule MyApp.MyUniversalModule do
  defmacro __using__(_opts) do
    quote do
      def gen_and_check_unique(changeset, field_name) do
        token = MyApp.MyUniversalModule.random_string()
        __MODULE__ |> Repo.get_by(field_name, token)
      end
    end
  end

  def random_string(length \\ 8) do
    :crypto.strong_rand_bytes(length) |> Base.url_encode64 |> binary_part(0, length)
  end
end

答案 1 :(得分:3)

首先,我认为这里有两个不同的问题:

1。 Repo.get_by(从一段数据中获取模式)

要获取作为架构的[1,[2],{"foo":"bar"},4],您可以使用 Repo.get_by / 3函数如下:

MyApp.MyQueryableModule

2。转换变更集

在此代码中,

alias MyApp.MyQueryableModule

defmodule MyApp.MyUniversalModule do
  def gen_and_check_unique(field_name) do
    Repo.get_by(MyQueryableModule, [{field_name, random_string()}])
  end
end

您似乎正在尝试返回 def changeset(model, params) do model |> cast(params, ~w(name), ~w(token)) |> gen_and_check_unique(:token) end ,通常情况下会Ecto.Schema.t。另外,我不确定,但你可能在这个函数中做了两件事(Ecto.Changeset.tapplying changes ??),我通常会保留我的变更集功能,仅用于验证更改和应用更改。最后,我们弃用get_by,转而使用Ecto.Changeset.cast/4请参阅here