One of Ruby's most popular features is its support for
metaprogramming, that is features that act like they change the
language itself - introducing things like new keywords. Although the mainstream curly-brace languages have less support
for metaprogramming in general, one useful feature they do have is
Annotations. Annotations are an important capability for
the language enhancement InternalDslStyle. At first
glance Ruby doesn't support annotations, but if tilt your head
you'll find it does. With annotations you tag a language element (class, method, field
etc) with a marker - essentially metadata about that element. You
can then use that metadata at runtime or compile time. A good example of this is marking of tests that was pioneered by NUnit2. [Test] public void SomeTestMethod() {... At run time the NUnit framework looks at classes, finds the
methods annotated with [Test] and runs them. Annotations can take parameters. So if you have a hankering for
the subranges of Pascal, which allow you to indicate a valid range
for a variable, you define them with something like this
@ValidRange(lower = 1, upper = 1000)
private int weight; // in lb
@ValidRange(lower = 1, upper = 120)
private int height; // in inches
This isn't quite the same as the Pascal subrange idea, this only
defines the valid range, you still have to build the mechanism to
check the value, such as calling an isValid method on
your object (I agree that's not much of a
ContextualValidation). But the key here is that you have
defined your own declarative statement about that variable. So how do you do this in Ruby? Something like this:
validate_range :@height, :with => 1..120
validate_range :@weight, :with => 1..1000
In terms of syntax, the big difference is that with Java (or
.NET) annotations you place the annotation immediately before the
element you're annotating. In ruby you name the element you're
annotating in the annotation. Although this adds some typing (a rare
statement for Ruby) it does give you the freedom to place your
annotations anywhere in your class. It also makes it easier to
construct annotations that reference more than one language element. A deeper difference between the two styles is how they get
implemented. Curly Brace annotations are a special language
construct that attach special objects as metadata to language
elements. These annotation objects can be queried and processed
either at compile time (with Java's apt) or at runtime. Ruby's annotations, on the other hand, are class methods usually
defined in a superclass or included module. By writing them directly
in the class definition they are executed when the class is loaded. As
a result they aren't a particular language feature, more of a way of
using class methods. You don't have to create metadata objects
(although you could if you wanted to), more likely you'll just build
the objects to carry out the task you want them to. Due to Ruby's dynamic nature, you can do a lot more interesting
things with annotations. In particular you can do code
generation. The most obvious example of this is things like
attr_accessor :height which will generate get and set
methods for the field, effectively modifying the class itself. With
apt Java can do some similar things, but you can't modify the class
itself. With build scripts and partial classes you probably could do
something like this in C#, but this kind of run-time code generation
is certainly more natural in Ruby, it's one of its Lispish elements. Rubyists don't call these things
annotations. One of the things I like doing is to find common
techniques that cross languages, for me this is a common technique
and 'annotation' seems like a good generic word for it. I don't know
if Rubyists will agree.
|