无法理解使用宏Elixir/Phoenix通过导入
我正在使用Phoenix框架并尝试创建一个用于身份验证的插件,但遇到了一个错误。下面是这个示例代码无法理解使用宏Elixir/Phoenix通过导入,elixir,phoenix-framework,Elixir,Phoenix Framework,我正在使用Phoenix框架并尝试创建一个用于身份验证的插件,但遇到了一个错误。下面是这个示例代码 defmodule ChhutiServer.GoogleAuthController do use ChhutiServer.Web, :controller use ChhutiServer.Plugs.GoogleAuth end # inside lib/plugs defmodule ChhutiServer.Plugs.GoogleAuth do import Plug
defmodule ChhutiServer.GoogleAuthController do
use ChhutiServer.Web, :controller
use ChhutiServer.Plugs.GoogleAuth
end
# inside lib/plugs
defmodule ChhutiServer.Plugs.GoogleAuth do
import Plug.Conn
defmodule ChhutiServer.Behaviour.GoogleAuth do
@callback callback(Plug.Conn.t, map) :: any
end
defmacro __using__(_) do
quote do
plug ChhutiServer.Plugs.GoogleAuth
end
end
end
上面的代码返回下面的错误
== Compilation error on file web/controllers/google_auth_controller.ex ==
** (UndefinedFunctionError) undefined function: ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth.init/1 (module ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth is not available)
ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth.init([])
(plug) lib/plug/builder.ex:198: Plug.Builder.init_module_plug/3
(plug) lib/plug/builder.ex:186: anonymous fn/4 in Plug.Builder.compile/3
(elixir) lib/enum.ex:1387: Enum."-reduce/3-lists^foldl/2-0-"/3
(plug) lib/plug/builder.ex:186: Plug.Builder.compile/3
(phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
web/controllers/google_auth_controller.ex:1: ChhutiServer.GoogleAuthController (module)
(elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8
我理解错误是由于elixir查找ChhutiServe.Plugs.GoogleAuth.ChhuttiServer.Plugs.GoogleAuth
而不是ChhuttiServe.Plugs.GoogleAuth
。我还可以通过使用Elixir名称空间预先设置模块来解决这个问题。ie使用下面的代码
defmodule ChhutiServer.Plugs.GoogleAuth do
import Plug.Conn
defmodule ChhutiServer.Behaviour.GoogleAuth do
@callback callback(Plug.Conn.t, map) :: any
end
defmacro __using__(_) do
quote do
plug Elixir.ChhutiServer.Plugs.GoogleAuth
end
end
end
但我不明白为什么它会这样。谁能帮帮我吗
更新
添加所有必需的代码 问题是因为我在
ChhutiServer.Plugs.GoogleAuth
内部声明了模块ChhutiServer.behavior.GoogleAuth
。但报告的错误是非常奇怪和误导的。将ChhutiServer.behavior.GoogleAuth
移动到ChhutiServer.behavior.GoogleAuth
外部解决了这个问题。我注意到的另一件奇怪的事情是,在\u下移动ChhutiServer.behavior.GoogleAuth
,使用
宏也会删除错误。由于错误报告看起来很奇怪,我提出了一个问题。以下是问题
更新
Jose Valim帮助我理解了这不是一个问题,它是由我们定义嵌套模块时创建的别名造成的。我将解释为什么我们会出现上述错误,以便其他面临类似问题的人能够理解这个概念
每当我们在elixir中创建嵌套模块时,我们都在为嵌套模块创建别名。下面是一个例子
defmodule A do
defmodule B do
end
end
上述代码相当于
defmodule A.B do
end
defmodule A do
alias A.B, as: B
end
因此嵌套模块创建别名,以便可以在父模块内部访问别名,而无需完全限定名,即我们可以使用B
而不是A.B
直接在A内部访问B
现在让我们来回答我的问题,检查发生了什么,并理解产生的错误
defmodule ChhutiServer.Plugs.GoogleAuth do
import Plug.Conn
defmodule ChhutiServer.Behaviour.GoogleAuth do
@callback callback(Plug.Conn.t, map) :: any
end
defmacro __using__(_) do
quote do
plug ChhutiServer.Plugs.GoogleAuth
end
end
end
在上面的代码模块中,ChhutiServer.behavior.GoogleAuth
嵌套在ChhutiServer.Plugs.GoogleAuth
中。所以ChhutiServer.behavior.GoogleAuth
的完全限定名是ChhutiServer.Plugs.GoogleAuth.ChhutiServer.behavior.GoogleAuth
。正如我们所看到的,嵌套模块创建别名,上面的代码与
defmodule ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Behaviour.GoogleAuth do
@callback callback(Plug.Conn.t, map) :: any
end
defmodule ChhutiServer.Plugs.GoogleAuth do
import Plug.Conn
alias ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Behaviour.GoogleAuth, as: ChhutiServer.Behaviour.GoogleAuth
defmacro __using__(_) do
quote do
plug ChhutiServer.Plugs.GoogleAuth
end
end
end
由于这个别名,现在ChhutiServer
指向ChhutiServer.Plugs.GoogleAuth.ChhutiServer
,这样我们就可以在模块内部使用ChhutiServer.behavior.GoogleAuth
ChhutiServer.Plugs.GoogleAuth
而不是使用全名ChhutiServer.Plugs.GoogleAuth.ChhutiServer.behavior.GoogleAuth
。那么当我们使用
plug ChhutiServer.Plugs.GoogleAuth
内部\uu使用
宏将其扩展为plug ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth
ChhutiServer
指向ChhutiServer.Plugs.GoogleAuth.ChhutiServer
。我们的错误来了,因为elixir无法找到模块
ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth
我还质疑地指出,当我们在\u下面使用
宏定义moduleChhutiServer.behavior.GoogleAuth
时,上面的代码运行时没有任何错误。
这是因为alias在elixir中是词汇范围。因此,如果我们使用宏将ChhutiServer.behavior.GoogleAuth
置于\u下方,则别名将在宏下方定义。
因此,plug ChhutiServer.Plugs.GoogleAuth
没有扩展为任何内容,而是保持原样。elixir能够找到ChhutiServer.Plugs.GoogleAuth
。用一个小例子来解释上面的事情
模块A.B.do
def b do
结束
结束
defado模块
定义一个do
#将返回错误“模块B不可用”,因为A.B尚未别名。
#如果我们想调用b
我们必须将其写成A.b.b
B.B
结束
别名A.B,as:B
结束
如果我们在使用B
之前使用别名,它将起作用
defado模块
别名A.B,as:B定义一个do B.B 结束 结束 希望这有助于其他人理解嵌套模块和别名。谢谢Jose Valim的解释 参考资料:
MyApp.Web的内容是什么?上面的代码应该可以正常运行。MyApp.Web是phoenix生成的默认模块(Web.ex文件)。它不包含我添加的任何行。我应该在这里发布完整的代码吗。文件有很多行代码,所以我避免将其粘贴到这里。但我想我错过了粘贴小车部件。我将在这里发布github repo的链接,而不是将所有内容都粘贴到这里谢谢你的报告。我试图解决你的问题,但运气不好。但是现在,在你的帖子和@josevalim回复你的问题之后,我对别名和嵌套模块的工作原理有了更好的理解。您也可以编辑您的帖子,并在这里从问题报告中进行解释。@DmitryByletsky我确信,一旦我弄清楚了几点,我会更新帖子。我仍然有点不清楚,并已就同一问题发布了其他问题。我会等到我完全明白。你能不能也查一下这期杂志上的问题。您可能已经得到了我想要的答案。@Dmitrybiletsky在答案中添加了一些细节,以详细解释这个问题