首先,我有一个模块,用于定义用户模型的用户角色和验证:
defmodule ClubHomepage.Web.UserRole do
...
@roles %{
"administrator": "user with all rights",
"member": "a registered user",
"match-editor": "editor of matches and reporter of live match events",
"news-editor": "author/editor of news",
"player": "an active sports man/woman",
"team-editor": "right to edit teams",
"text-page-editor": "author/editor of static page contents",
"user-editor": "user administrator"
}
@spec defined_roles_keys() :: [String.t]
def defined_roles_keys do
Map.keys(@roles)
|> Enum.map(fn(role) -> Atom.to_string(role) end)
end
...
end
我有一个模块,它定义了从前面定义的用户角色的密钥生成的插件函数:
defmodule ClubHomepage.Web.AuthByRole do
...
@spec plug_function_name(String.t) :: Atom.t
def plug_function_name(user_role_key) do
"is_#{String.replace(user_role_key, "-", "_")}"
|> String.to_atom()
end
for user_role_key <- UserRole.defined_roles_keys() do
function_name = plug_function_name(user_role_key)
@spec unquote(function_name)(Plug.Conn.t, Keyword.t) :: Boolean
def unquote(function_name)(conn, _options) do
has_role(conn, unquote(user_role_key))
end
end
...
end
问题1:我从for comprehension中提取了一些代码到函数plug_function_name
中。但在我收到错误之后:
== Compilation error on file lib/club_homepage/web/commands/auth_by_role.ex ==
** (CompileError) lib/club_homepage/web/commands/auth_by_role.ex:31: undefined function plug_function_name/1
我怎样才能以更好的方式做到这一点,但保持我的代码干燥?
我的意思是在我的测试模块中使用plug_function_name
生成相同的函数名称并在测试中调用生成的插件函数:
defmodule ClubHomepage.Web.AuthByRoleTest do
use ClubHomepage.Web.ConnCase
alias ClubHomepage.Web.UserRole
alias ClubHomepage.Web.AuthByRole
setup do
conn =
build_conn()
|> bypass_through(ClubHomepage.Web.Router, :browser)
|> get("/")
{:ok, %{conn: conn}}
end
for user_role_key <- UserRole.defined_roles_keys() do
function_name = AuthByRole.plug_function_name(user_role_key)
test "#{function_name} halts when no current_user exists", %{conn: conn} do
conn = AuthByRole.is_administrator(conn, [])
assert flash_messages_contain?(conn, "You are not authorized to view this page.")
assert conn.halted
end
test "#{function_name} continues when the current_user has the #{user_role_key} role", %{conn: conn} do
conn =
conn
|> assign(:current_user, %ClubHomepage.User{roles: "member #{user_role_key}"})
|> AuthByRole.is_administrator([])
refute conn.halted
end
end
end
问题2:在生成的测试中,我需要使用AuthByRole.is_administrator(conn, [])
function_name AuthByRole.
替换对(conn, [])
的调用。
我该怎么做?
答案 0 :(得分:1)
对于第一个问题,您需要在单独的模块中定义要在编译时调用的函数,以便Elixir编译器首先编译该模块,并在主模块时使其可用于主模块正在编译中。这是一个你可以适应的例子:
defmodule M.Helper do
def function_name(x), do: :"#{x}#{x}"
end
defmodule M do
import M.Helper
for x <- ~w(a b c) do
name = function_name(x)
def unquote(name)(), do: unquote(name)
end
end
iex(1)> M.aa
:aa
iex(2)> M.bb
:bb
iex(3)> M.cc
:cc
对于第二个问题,您可以使用AuthByRole.unquote(function_name)(conn, [])
。这是一个例子:
defmodule MTest do
use ExUnit.Case
import M.Helper
for x <- ~w(a b c) do
name = function_name(x)
test name do
assert M.unquote(name) == unquote(name)
end
end
end
$ m mix test --trace
MTest
* test aa (1.2ms)
* test cc (0.00ms)
* test bb (0.00ms)
Finished in 0.02 seconds
3 tests, 0 failures