Выбрать главу

Прежде всего, давайте посмотрим, как мы можем работать с данными, хранящимися в классе, используя данные из аннотации для создания вывода:

annotation MyClass; end

Annotation MyAnnotation; end

@[MyClass(true, id: "foo_class")]

class Foo

  {% begin %}

    {% ann = @type.annotation MyClass %}

    {% pp "#{@type} has positional arguments of:

      #{ann.args}" %}

    {% pp "and named arguments of #{ann.named_args}" %}

    {% pp %(and is #{ann[0] ? "active".id :

      "not active".id}) %}

    {% status = if my_ann = @type.annotation MyAnnotation

                  "DOES"

                else

                  "DOES NOT"

                end %}

    {% pp "#{@type} #{status.id} have MyAnnotation applied." %}

  {% end %}

end

Запуск этой программы выведет следующее:

"Foo has positional arguments of: {true}"

"and named arguments of {id: \"foo_class\"}"

"and is active."

"Foo DOES NOT have MyAnnotation applied."

Мы также можем сделать то же самое с аннотацией, примененной к методу:

annotation MyMethod; end

@[MyMethod(4, 1, 2, id: "foo")]

def my_method

  {% begin %}

    {% ann = @def.annotation MyMethod %}

    {% puts "\n" %}

    {% pp "Method #{@def.name} has an id of #{ann[:id]}" %}

    {% pp "and has #{ann.args.size} positional arguments" %}

    {% total = ann.args.reduce(0) { |acc, v| acc + v } %}

    {% pp "that sum to #{total}" %}

  {% end %}

end

my_method

Запуск этой программы выведет следующее:

"Method my_method has an id of \"foo\""

"and has 3 positional arguments"

"that sum to 7"

В обоих этих примерах мы использовали все три метода, а также некоторые сами типы коллекций. Мы также увидели, как обрабатывать необязательную аннотацию, следуя той же логике обработки nil, что и в коде Crystal, не являющемся макросом. Если бы к нашему классу была применена аннотация, мы могли бы получить доступ к любым дополнительным данным из него через переменную my_ann, так же, как мы это делали с переменной ann в предыдущих строках. Этот шаблон может быть невероятно полезен, позволяя влиять на логику макроса наличием или отсутствием аннотации. Это может привести к более читабельному коду, для которого в противном случае потребовалась бы одна аннотация со множеством различных полей.

Как и в предыдущем примере с несколькими аннотациями для одного элемента, метод #annotation возвращает последнюю аннотацию, примененную к данному элементу. Если вы хотите получить доступ ко всем примененным аннотациям, вместо этого вам следует использовать метод #annotations. Этот метод работает почти идентично другому методу, но возвращает ArrayLiteral(Annotation) вместо Annotation?. Например, мы могли бы использовать этот метод для перебора нескольких аннотаций, чтобы напечатать индекс аннотации вместе со значением, которое она хранит:

annotation MyAnnotation; end

@[MyAnnotation("foo")]

@[MyAnnotation(123)]

@[MyAnnotation(123)]

def annotation_read

  {% for ann, idx in @def.annotations(MyAnnotation) %}

    {% pp "Annotation #{idx} = #{ann[0].id}" %}

  {% end %}

end

annotation_read

Запуск этого приведет к печати следующего:

"Annotation 0 = foo"

"Annotation 1 = 123"

"Annotation 2 = 123"

Вот и все. Аннотации сами по себе являются довольно простой функцией, но могут быть весьма мощными в сочетании с некоторыми другими функциями метапрограммирования Crystal.

Резюме

В этой главе мы рассмотрели, как определять и использовать аннотации для расширения различных Функции Crystal с дополнительными метаданными, включая способ хранения как именованных, так и позиционные аргументы, как читать одиночные и множественные аннотации и какие преимущества/Аннотации вариантов использования выполняются поверх макросов.

Аннотации — это жизненно важная функция метапрограммирования, которую мы обязательно будем использовать в следующих главах. До сих пор весь макрокод, который мы писали для доступа к данным типа или метода, находился в контексте этого типа или метода.

В следующей главе мы собираемся изучить функцию самоанализа типов во время компиляции Crystal, которая представит новые способы доступа к той же информации.

12. Использование интроспекции типов во время компиляции