Обходной путь — вложить еще один модуль ClassMethods
в основной. Однако для этого пользователю потребуется вручную включить основной модуль и расширить вложенный модуль, что не очень удобно для пользователя. Лучшим вариантом было бы определить в основном модуле ClassMethods
. Таким образом, макрос будет расширяться внутри включенного класса, автоматически расширяя модуль методов класса. Это будет выглядеть примерно так:
module MyModule
module ClassMethods
def foo
"foo"
end
end
macro included
extend MyModule::ClassMethods
end
def bar
"bar"
end
end
class Foo
include MyModule
end
pp Foo.foo
pp Foo.new.bar
Таким образом, пользователю нужно только включить модуль, чтобы получить оба типа методов, что в целом улучшит взаимодействие с пользователем.
Резюме
Метапрограммирование — одна из областей, в которой Кристалл преуспевает. Он предоставляет нам довольно мощную систему, которую можно использовать для генерации кода и уменьшения количества шаблонов/повторений, при этом оставаясь при этом достаточно простой по сравнению с другими языками. Однако, когда это необходимо, эту силу следует использовать экономно.
В этой главе мы узнали, как и когда использовать макросы для сокращения шаблонного кода, как подключаться к различным событиям Crystal с помощью перехватчиков макросов, а также познакомились с API макросов для поддержки создания более сложных макросов.
В следующей главе мы рассмотрим аннотации и то, как их можно использовать в сочетании с макросами для хранения данных, которые можно прочитать во время компиляции.
11. Знакомство с аннотациями
Как упоминалось в предыдущей главе, макросы могут быть мощным инструментом для генерации кода, позволяющим уменьшить дублирование и сохранить ваше приложение DRY. Однако одно из ограничений макросов, особенно тех, которые находятся за пределами определения макроса, заключается в том, что сложно получить доступ к данным для использования внутри макроса, поскольку они должны быть доступны во время компиляции, как переменная среды или константа.
Ни один из этих вариантов в большинстве случаев не является отличным вариантом. Чтобы лучше решить эту проблему, нам нужно изучить следующую концепцию метапрограммирования Crystal: аннотации.
В этой главе мы рассмотрим следующие темы:
• Что такое аннотации?
• Хранение данных в аннотациях.
• Чтение аннотаций
К концу этой главы вы должны иметь четкое представление о том, что такое аннотации и как их использовать.
Технические требования
Требования к этой главе следующие:
• Рабочая установка Crystal.
Инструкции по настройке Crystal можно найти в
Все примеры кода, использованные в этой главе, можно найти в папке
Что такое аннотации?
Проще говоря, аннотация — это способ прикрепить метаданные к определенным функциям кода, к которым впоследствии можно получить доступ во время компиляции внутри макроса. Crystal поставляется в комплекте с некоторыми встроенными аннотациями, с которыми вы, возможно, уже работали, например @[JSON::Field]
или аннотацией @[Link]
, которая была рассмотрена в JSON::Field
существует в стандартной библиотеке Crystal и реализована/используется таким образом, что вы можете воспроизвести ее в своем собственном коде с помощью собственной аннотации. С другой стороны, аннотация Link
имеет особые отношения с компилятором Crystal, и часть ее поведения не может быть воспроизведена в пользовательском коде.
Пользовательские аннотации можно определить с помощью ключевого слова annotation:
annotation MyAnnotation; end
Вот и все. Затем аннотацию можно было применить к различным элементам, включая следующие:
• Методы экземпляра и класса.
• Переменные экземпляра
• Классы, структуры, перечисления и модули.
Аннотацию можно применять к различным объектам, помещая имя аннотации в квадратные скобки синтаксиса @[]
, как в следующем примере:
@[MyAnnotation]
def foo
"foo"
end