Roh*_*ari 4 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.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
Run Code Online (Sandbox Code Playgroud)
上面的代码返回下面的错误。
== 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
Run Code Online (Sandbox Code Playgroud)
我知道这个错误是由于 Elixir 寻找ChhutiServe.Plugs.GoogleAuth.ChhuttiServer.Plugs.GoogleAuth而不是ChhuttiServe.Plugs.GoogleAuth. 我还可以通过在模块前面添加 Elixir 命名空间来解决这个问题。即通过使用下面的代码。
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
Run Code Online (Sandbox Code Playgroud)
但我无法理解为什么它会这样。谁能帮我一下。
更新
添加所有必需的代码。
问题是因为我已经ChhutiServer.Behaviour.GoogleAuth在内部声明了模块ChhutiServer.Plugs.GoogleAuth。但报告的错误非常奇怪且具有误导性。搬到ChhutiServer.Behaviour.GoogleAuth外面ChhutiServer.Behaviour.GoogleAuth就可以解决这个问题。我注意到的另一件奇怪的事情是移动到宏ChhutiServer.Behaviour.GoogleAuth下方__using__也消除了错误。由于错误报告看起来很奇怪,我提出了一个问题。下面是问题
https://github.com/elixir-lang/elixir/issues/4120
更新
Jose Valim 帮助我明白这不是一个问题,这是由于我们定义嵌套模块时创建的别名造成的。我将解释为什么我们会出现上述错误,以便其他面临类似问题的人可以理解这个概念。
每当我们在 Elixir 中创建嵌套模块时,我们都会为嵌套模块创建别名。下面是示例。
defmodule A do
defmodule B do
end
end
Run Code Online (Sandbox Code Playgroud)
上面的代码相当于
defmodule A.B do
end
defmodule A do
alias A.B, as: B
end
Run Code Online (Sandbox Code Playgroud)
因此,嵌套模块创建别名,以便可以在父模块内部访问它,而无需完全限定名称,即我们可以使用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
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,模块ChhutiServer.Behaviour.GoogleAuth嵌套在ChhutiServer.Plugs.GoogleAuth. 所以 的完全限定名称ChhutiServer.Behaviour.GoogleAuth是ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Behaviour.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
Run Code Online (Sandbox Code Playgroud)
由于这个别名现在ChhutiServer指向,ChhutiServer.Plugs.GoogleAuth.ChhutiServer所以我们可以使用ChhutiServer.Behaviour.GoogleAuth内部模块
ChhutiServer.Plugs.GoogleAuth而不是使用完整的限定名称ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Behaviour.GoogleAuth。然后,当我们使用
plug ChhutiServer.Plugs.GoogleAuth内部 __using__宏时,它会扩展plug ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth为现在
ChhutiServer指向的ChhutiServer.Plugs.GoogleAuth.ChhutiServer。由于 elixir 无法找到该模块,我们出现了错误
ChhutiServer.Plugs.GoogleAuth.ChhutiServer.Plugs.GoogleAuth
我还指出,当我们在宏ChhutiServer.Behaviour.GoogleAuth下面定义模块时__using__,上面的代码运行时不会出现任何错误。这是因为别名在 Elixir 中是词法范围的。因此,如果我们在宏ChhutiServer.Behaviour.GoogleAuth下方引入__using__别名,则别名将在宏下方定义。所以plug ChhutiServer.Plugs.GoogleAuth不会扩展到任何东西,并且保持原样。而且丹药是能够找到的ChhutiServer.Plugs.GoogleAuth。小例子来解释上面的事情。
defmodule AB 做 def b 做 end end
defmodule A do def a do # 将返回错误“模块 B 不可用”,因为 AB 尚未别名。# 如果我们要调用b我们必须将其写为A.B.b
Bb end别名AB,如:B end
如果我们在使用之前使用别名,B它将起作用。
defmodule A do 别名 AB,如: B
def a do Bb end end
希望这有助于其他人理解嵌套模块和别名。感谢何塞·瓦利姆的解释
参考:
http://elixir-lang.org/getting-started/alias-require-and-import.html
https://github.com/elixir-lang/elixir/issues/4120
| 归档时间: |
|
| 查看次数: |
875 次 |
| 最近记录: |