Using annotations to influence runtime logic
As we learned in Chapter 11, Introducing Annotations, annotations are a great way to add additional metadata to various Crystal features such as types, instance variables, and methods. However, one of their major limitations is that the data held within them is only available at compile time.
In some cases, you may want to implement a feature using annotations to customize something, but the logic that needs that data cannot be generated with macros alone and needs to execute at runtime. For example, say we wanted to be able to print instances of objects in various formats. This logic could use annotations to mark which instance variables to expose, as well as configure how they get formatted. A high-level example of this would look like this:
annotation Print; end class MyClass include Printable @[Print] property name : String = "Jim" @[Print(format: "%F")] ...