Я читал другие ответы SO по этому вопросу, но я специально хотел сосредоточиться на собственной документации Elixir , в которой говорится о миксинах и DSL.
В своем примере они говорят, что ониЕсть три варианта:
# 1. data structures
import Validator
validate user, name: [length: 1..100],
email: [matches: ~r/@/]
# 2. functions
import Validator
user
|> validate_length(:name, 1..100)
|> validate_matches(:email, ~r/@/)
# 3. macros + modules
defmodule MyValidator do
use Validator
validate_length :name, 1..100
validate_matches :email, ~r/@/
end
MyValidator.validate(user)
Конечно, примеры не полны.Поэтому я решил попробовать стиль № 3, чтобы лучше понять некоторые библиотеки, которые я использую, которые используют некоторые из этих стилей (не обязательно стиль № 3).
Есть некоторая ошибка с попыткамичтобы сделать это в файле test.exs
, потому что структура еще не была определена или к структуре обращаются в том же контексте, который ее определяет , но обход этого с помощью модуля Main приводит меня к чему-то вроде этого:
defmodule Validator do
defmacro __using__(_params) do
quote do
def validate_length(field, length_rules) do
String.length(field) >= length_rules.first and String.length(field) <= length_rules.last
end
end
end
end
defmodule MyValidator do
use Validator
validate_length :name, 1..10
end
defmodule User do
@enforce_keys [:name]
defstruct [:name]
end
defmodule Main do
def run do
user = %User{name: "Joe"}
MyValidator.validate(user)
end
end
Main.run
# undefined function validate_length/2
Я бы не ожидал, что это сработает, потому что я не понимаю связи между MyValidator
и "обратными вызовами" для того, что означает проверка.Должен ли я посмотреть (метапрограмму), какие проверки были использованы?Должен ли я реализовать _params
, чтобы увидеть, какие обратные вызовы будут использоваться в будущем?
Несмотря на это, ошибка составляет undefined function validate_length/2
, поскольку между ними нет "разводки".Конечно, если я изменю основной на что-то вроде:
defmodule MyValidator do
use Validator
end
def run do
user = %User{name: "Joe"}
MyValidator.validate_length(user.name, 1..10)
Тогда, конечно, это работает, но это не обратный вызов, это просто миксин.
Так как же вы можете закончить?пример № 3 для Elixir, действующий как миксин, который может иметь много валидаторов, много обратных вызовов?