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

How to get caller module name in included module?

如何在包含的 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

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


尽管您 can get the call stack at runtime and extract the calling module from that, it's going to be inefficient and is generally not recommended. The idiomatic way in Elixir would be to use macros and the __using__ hook. Ecto uses the same method for Repo 以及许多其他模块。

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

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。 Repo.get_by(从一段数据中获取模式)

要获取作为架构的 MyApp.MyQueryableModule,您可以使用 Repo.get_by/3函数如下:

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

2。投射变更集

在此代码中,

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

您似乎在尝试 return Ecto.Schema.t,而通常情况下 Ecto.Changeset.t 是预期的。另外,我不确定,但您可能在此函数中同时做了两件事(applying changesget_by ??),我通常保留我的变更集函数,仅用于验证变更和应用变更。最后,Ecto.Changeset.cast/4 被弃用,取而代之的是 Ecto.Changeset.cast/3 请参阅 here