Elixir + Phoenix : __MODULE__ undefined inside a quote
Elixir + Phoenix : __MODULE__ undefined inside a quote
(代码引用已匿名化)
在我的 phoenix 模型中,我有一些多余的方法,比如这个基本方法:
def build(params) do
changeset(%__MODULE__{}, params)
end
因为我将它们放在我的模型模块中,所以它们工作正常,但我想避免代码重复,并且我想通过这样的辅助模块使它们对我的所有模型可用:
defmodule MyApp.Helpers.Model do
defmodule Changeset do
defmacro __using__(_opts) do
quote do
def build(params) do
changeset(%__MODULE__{}, params)
end
end
end
end
end
执行此操作时出现错误:
== Compilation error on file lib/my_app/model/my_model.ex ==
** (CompileError) lib/my_app/model/my_model.ex:3: MyApp.Model.MyModel.__struct__/1 is undefined, cannot expand struct MyApp.Model.MyModel
(stdlib) lists.erl:1354: :lists.mapfoldl/3
相关模型基本是这样的:
defmodule MyApp.Model.MyModel do
use MyApp.Helpers, :model
use MyApp.Helpers.Model.Changeset # here for comprehension, should be in MyApp.Helpers quoted :model method
schema "my_table" do
field :name, :string
timestamps()
end
@required_fields ~w(name)a
@optional_fields ~w()
@derive {Poison.Encoder, only: [:name]}
def changeset(model, params \ %{}) do
model
|> cast(params, @required_fields)
|> cast(params, @optional_fields)
|> validate_required(@required_fields)
|> validate_format(:name, ~r/^[a-z]{3,}$/)
|> unique_constraint(:name)
end
end
我认为这是因为该模块在编译时尚未在宏中定义,但我不确定,也不确定如何解决此问题并使其正常工作。
这里有些灯会非常感谢,谢谢。
问题是结构是通过调用defstruct
宏定义的,不能提前使用,因为编译器不知道如何扩展它。在 ecto 模式的情况下,结构由下面的 schema
宏声明。
幸运的是,查看 defstruct
的文档我们可以看到它在声明结构的模块上创建了一个名为 __struct__/0
的函数。并且函数可以调用其他局部函数,甚至在它们被定义之前!使用这些知识,我们可以将您的宏更改为:
defmodule MyApp.Helpers.Model do
defmodule Changeset do
defmacro __using__(_opts) do
quote do
def build(params) do
changeset(__struct__(), params)
end
end
end
end
end
也就是说,在 __using__
中定义函数通常被认为是一种不好的做法,如 Kernel.use/1
的文档中所述
Finally, developers should also avoid defining functions inside the
__using__/1
callback, unless those functions are the default implementation of a previously defined @callback
or are functions
meant to be overridden (see defoverridable/1
. Even in these cases,
defining functions should be seen as a “last resource”.
在 __using__
中定义函数有很多缺点,包括:编译速度慢(函数在它注入的每个模块中都被反复编译)、调试困难 ("where is this coming from?") 和可堆肥性差。
更好的方法可能是定义一个单一的、可重用的函数。例如:
defmodule MyApp.SchemaUtils do
def build(schema, params) do
schema.changeset(struct(schema), params)
end
end
PS。 @derive
调用必须在结构之前声明。
(代码引用已匿名化)
在我的 phoenix 模型中,我有一些多余的方法,比如这个基本方法:
def build(params) do
changeset(%__MODULE__{}, params)
end
因为我将它们放在我的模型模块中,所以它们工作正常,但我想避免代码重复,并且我想通过这样的辅助模块使它们对我的所有模型可用:
defmodule MyApp.Helpers.Model do
defmodule Changeset do
defmacro __using__(_opts) do
quote do
def build(params) do
changeset(%__MODULE__{}, params)
end
end
end
end
end
执行此操作时出现错误:
== Compilation error on file lib/my_app/model/my_model.ex ==
** (CompileError) lib/my_app/model/my_model.ex:3: MyApp.Model.MyModel.__struct__/1 is undefined, cannot expand struct MyApp.Model.MyModel
(stdlib) lists.erl:1354: :lists.mapfoldl/3
相关模型基本是这样的:
defmodule MyApp.Model.MyModel do
use MyApp.Helpers, :model
use MyApp.Helpers.Model.Changeset # here for comprehension, should be in MyApp.Helpers quoted :model method
schema "my_table" do
field :name, :string
timestamps()
end
@required_fields ~w(name)a
@optional_fields ~w()
@derive {Poison.Encoder, only: [:name]}
def changeset(model, params \ %{}) do
model
|> cast(params, @required_fields)
|> cast(params, @optional_fields)
|> validate_required(@required_fields)
|> validate_format(:name, ~r/^[a-z]{3,}$/)
|> unique_constraint(:name)
end
end
我认为这是因为该模块在编译时尚未在宏中定义,但我不确定,也不确定如何解决此问题并使其正常工作。
这里有些灯会非常感谢,谢谢。
问题是结构是通过调用defstruct
宏定义的,不能提前使用,因为编译器不知道如何扩展它。在 ecto 模式的情况下,结构由下面的 schema
宏声明。
幸运的是,查看 defstruct
的文档我们可以看到它在声明结构的模块上创建了一个名为 __struct__/0
的函数。并且函数可以调用其他局部函数,甚至在它们被定义之前!使用这些知识,我们可以将您的宏更改为:
defmodule MyApp.Helpers.Model do
defmodule Changeset do
defmacro __using__(_opts) do
quote do
def build(params) do
changeset(__struct__(), params)
end
end
end
end
end
也就是说,在 __using__
中定义函数通常被认为是一种不好的做法,如 Kernel.use/1
Finally, developers should also avoid defining functions inside the
__using__/1
callback, unless those functions are the default implementation of a previously defined@callback
or are functions meant to be overridden (seedefoverridable/1
. Even in these cases, defining functions should be seen as a “last resource”.
在 __using__
中定义函数有很多缺点,包括:编译速度慢(函数在它注入的每个模块中都被反复编译)、调试困难 ("where is this coming from?") 和可堆肥性差。
更好的方法可能是定义一个单一的、可重用的函数。例如:
defmodule MyApp.SchemaUtils do
def build(schema, params) do
schema.changeset(struct(schema), params)
end
end
PS。 @derive
调用必须在结构之前声明。