如何在包含的模块中获取调用方模块名称?
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 changes
和 get_by
??),我通常保留我的变更集函数,仅用于验证变更和应用变更。最后,Ecto.Changeset.cast/4
被弃用,取而代之的是 Ecto.Changeset.cast/3
请参阅 here。
如何在包含的 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 changes
和 get_by
??),我通常保留我的变更集函数,仅用于验证变更和应用变更。最后,Ecto.Changeset.cast/4
被弃用,取而代之的是 Ecto.Changeset.cast/3
请参阅 here。