Macros 如何在Elixir中扩展多个宏?
我要用长生不老药开始我的冒险,我需要一点帮助 我试图通过使用宏来简化我的结构定义和验证。目标是根据使用它的模块中提供的选项,自动注入Macros 如何在Elixir中扩展多个宏?,macros,elixir,Macros,Elixir,我要用长生不老药开始我的冒险,我需要一点帮助 我试图通过使用宏来简化我的结构定义和验证。目标是根据使用它的模块中提供的选项,自动注入defstruct和Vex库验证器 我提出了如下代码: defmodule PdfGenerator.BibTypes.TypeDefinition do @callback valid?(%{}) :: boolean defmacro __using__(mod: mod, style: style, required: required, optio
defstruct
和Vex库验证器
我提出了如下代码:
defmodule PdfGenerator.BibTypes.TypeDefinition do
@callback valid?(%{}) :: boolean
defmacro __using__(mod: mod, style: style, required: required, optional: optional) do
required_props = required |> Enum.map(&{:"#{&1}", nil})
optional_props = optional |> Enum.map(&{:"#{&1}", nil})
quote location: :keep do
defstruct unquote([{:style, style}] ++ required_props ++ optional_props)
@behaviour PdfGenerator.BibTypes.TypeDefinition
use Vex.Struct
def cast(%{} = map) do
styled_map = Map.put(map, :style, unquote(style))
struct_from_map(styled_map, as: %unquote(mod){})
end
defp struct_from_map(a_map, as: a_struct) do
keys =
Map.keys(a_struct)
|> Enum.filter(fn x -> x != :__struct__ end)
processed_map =
for key <- keys, into: %{} do
value = Map.get(a_map, key) || Map.get(a_map, to_string(key))
{key, value}
end
a_struct = Map.merge(a_struct, processed_map)
a_struct
end
validates(
:style,
presence: true,
inclusion: [unquote(style)]
)
end
Enum.each(required, fn prop ->
quote location: :keep do
validates(
unquote(prop),
presence: true
)
end
end)
end
end
defmodule PdfGenerator.BibTypes.Booklet do
defstruct style: "booklet",
title: nil,
author: nil,
howpublished: nil,
address: nil,
month: nil,
year: nil,
note: nil
@behaviour PdfGenerator.BibTypes.TypeDefinition
use Vex.Struct
def cast(%{} = map) do
styled_map = Map.put(map, :style, "booklet")
struct_from_map(styled_map, as: %PdfGenerator.BibTypes.Booklet{})
end
defp struct_from_map(a_map, as: a_struct) do
keys =
Map.keys(a_struct)
|> Enum.filter(fn x -> x != :__struct__ end)
processed_map =
for key <- keys, into: %{} do
value = Map.get(a_map, key) || Map.get(a_map, to_string(key))
{key, value}
end
a_struct = Map.merge(a_struct, processed_map)
a_struct
end
validates(
:style,
presence: true,
inclusion: ["booklet"]
)
validates(
:title,
presence: true
)
end
我希望在宏展开后,PdfGenerator.BibTypes.bulkbook模块的外观如下所示:
defmodule PdfGenerator.BibTypes.TypeDefinition do
@callback valid?(%{}) :: boolean
defmacro __using__(mod: mod, style: style, required: required, optional: optional) do
required_props = required |> Enum.map(&{:"#{&1}", nil})
optional_props = optional |> Enum.map(&{:"#{&1}", nil})
quote location: :keep do
defstruct unquote([{:style, style}] ++ required_props ++ optional_props)
@behaviour PdfGenerator.BibTypes.TypeDefinition
use Vex.Struct
def cast(%{} = map) do
styled_map = Map.put(map, :style, unquote(style))
struct_from_map(styled_map, as: %unquote(mod){})
end
defp struct_from_map(a_map, as: a_struct) do
keys =
Map.keys(a_struct)
|> Enum.filter(fn x -> x != :__struct__ end)
processed_map =
for key <- keys, into: %{} do
value = Map.get(a_map, key) || Map.get(a_map, to_string(key))
{key, value}
end
a_struct = Map.merge(a_struct, processed_map)
a_struct
end
validates(
:style,
presence: true,
inclusion: [unquote(style)]
)
end
Enum.each(required, fn prop ->
quote location: :keep do
validates(
unquote(prop),
presence: true
)
end
end)
end
end
defmodule PdfGenerator.BibTypes.Booklet do
defstruct style: "booklet",
title: nil,
author: nil,
howpublished: nil,
address: nil,
month: nil,
year: nil,
note: nil
@behaviour PdfGenerator.BibTypes.TypeDefinition
use Vex.Struct
def cast(%{} = map) do
styled_map = Map.put(map, :style, "booklet")
struct_from_map(styled_map, as: %PdfGenerator.BibTypes.Booklet{})
end
defp struct_from_map(a_map, as: a_struct) do
keys =
Map.keys(a_struct)
|> Enum.filter(fn x -> x != :__struct__ end)
processed_map =
for key <- keys, into: %{} do
value = Map.get(a_map, key) || Map.get(a_map, to_string(key))
{key, value}
end
a_struct = Map.merge(a_struct, processed_map)
a_struct
end
validates(
:style,
presence: true,
inclusion: ["booklet"]
)
validates(
:title,
presence: true
)
end
但是有了它,当我试图在iex
控制台中发出以下命令时:%PdfGenerator.BibTypes.Booklet{}
我得到:
** (CompileError) iex:1: PdfGenerator.BibTypes.Booklet.__struct__/1 is undefined, cannot expand struct PdfGenerator.BibTypes.Booklet
你知道我做错了什么吗?任何提示都将不胜感激,因为我对整个长生不老药和宏世界都很陌生 由于您没有提供,因此测试解决方案非常困难,但乍一看,问题在于您希望从中获得一些魔力,虽然它不会隐式地在任何地方注入任何内容,但它只会生成一个AST 当你打电话的时候
Enum.each(…)
作为quote do
块的最后一行,此调用的结果作为AST从quote do
返回。也就是说,当前的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。您需要的是构建要注入的子句列表:
defmacro\uuuu使用(mod:mod,…)do
#准备工作
ast_defstruct=
报价位置::保留do
#defstruct的全部内容
结束
#注意!最后一个学期将使用返回!
[
ast_defstruct|
枚举映射(必需,fn属性->
报价位置::保留,
do:验证(取消引用(prop),状态:true)
(完)
]
通过使用,我们为每个元素收集引用的AST,并将它们附加到已构建的AST中,以便defstruct
创建。我们返回一个包含许多子句的列表(这是一个适当的AST)
尽管如此,我不确定这是否是由于缺乏MCVE而导致的唯一故障,但这绝对是一个正确的解决方案。是的,就是这样。我忘记了一个非常简单的事实:Elixir中的每个函数都会返回一些东西。非常感谢你的帮助和投入的时间:)没有MCVE——被否决了。