Метод flag?
{% if flag?(:linux) && flag?(:x86_64) %}
, которые будут выполняться только в том случае, если система, компилирующая программу, использует 64-битная ОС Linux.Пользовательские флаги можно определить с помощью опций --define
-D
. Например, если вы хотите проверить наличие flag? :foo
, флаг можно определить, выполнив crystal run -Dfoo main.cr
. Флаги времени компиляции либо присутствуют, либо нет; они не могут включать значение. Однако переменные окружающей среды могут стать хорошей заменой, если требуется большая гибкость.Переменные среды можно прочитать во время компиляции с помощью метода макроса env. Хорошим вариантом использования этого является возможность встраивания в двоичный файл информации о времени сборки, такой как эпоха сборки, время сборки и т. д. В этом примере во время компиляции значение константы будет установлено либо в значение переменной среды BUILD_SHA_HASH
COMMIT_SHA = {{ env("BUILD_SHA_HASH") || "" }}
pp COMMIT_SHA
При запуске этого кода обычно печатается пустая строка, а при установке связанной переменной env
env
, а не генерация внутри самого макроса с помощью системного вызова, гораздо более переносима, поскольку не зависит от Git, а также гораздо проще интегрируется с внешними системами сборки, такими как Make.Одним из ограничений макросов является то, что сгенерированный из макроса код также должен быть действительным кодом Crystal, как показано здесь:
def {{"foo".id}}
"foo"
end
Этот предыдущий код не является допустимой программой, поскольку метод неполный и не полностью определен в макросе. Этот метод можно включить в макрос, обернув все тегами {% begin %}/{% end %}
{% begin %}
def {{"foo".id}}
"foo"
end
{% end %}
На этом этапе вы должны иметь четкое начальное представление о том, что такое макросы, как их определять и для каких случаев использования они предназначены, что позволит вам сохранить ваш код СУХИМ (DRY). Далее мы рассмотрим API макросов, чтобы можно было создавать более сложные макросы.
Понимание API макросов
В примерах из предыдущего раздела в контексте макроса использовались различные переменные разных типов, такие как числа, которые мы перебираем, строки, которые мы используем для создания идентификаторов, и логические значения, которые мы сравниваем для условной генерации кода. Было бы легко предположить, что это напрямую соответствует стандартным типам Number
String
и Bool
. Однако это не так. Как мы упоминали в разделе NumberLiteral
, StringLiteral
и BoolLiteral
.Все типы макросов находятся в пространстве имен Crystal::Macros
• Def
• TypeNode
• MetaVar
• Arg
•Annotation
Crystal предоставляет удобный способ получить экземпляр первых двух типов в виде макропеременных @def
@type
. Как следует из их названий, использование @def
внутри метода вернет экземпляр Def
, представляющий этот метод. Аналогично, использование @type
вернет экземпляр TypeNode
для связанного типа. Доступ к другим типам можно получить через методы, основанные на одном из этих двух типов. Например, запуск следующей программы выведет "Метод hello внутри Foo"
:class Foo
def hello
{{"The #{@def.name} method within #{@type.name}"}}
end
end
pp Foo.new.hello