Annotations

Since 1.3

Overview

Together with 1.3 annotation mechanism has been introduced. In short, Erlang Web annotations allow developer to separate the business logic of the target function from different kind, non-domain specific wrappers, such as validators, converters, loggers, authenticators and so on.

Assume we want to call the controller function blog:post(BlogPost). But before doing the actual posting, we would like to do the following things:

After the successful posting we would like to do:

Of course we can use dataflow mechanism in order to separate those activities from the actual blog:post function's body, but unfortunately, dataflow has some major drawbacks:

The annotation mechanism get rid off all of those disadvantages and helps developers to write cleaner and more understandable code.

There are two types of Erlang Web's annotations: before and after. before annotations are called before the actual function execution, after are triggered when the target function ends.

Syntax

In order to annotate the function (that has been mentioned in the previous section) we must use the following syntax:

   1 -module(blog).
   2 
   3 -export([post/1]).
   4 
   5 -include_lib("blog/include/utils_annotation.hrl").
   6 
   7 ...
   8 
   9 %% Before annotations
  10 ?AUTHORIZE(editor).
  11 ?VALIDATE(blog_post).
  12 ?LOG({?MODULE, post}).
  13 
  14 %% After annotations
  15 ?INVALIDATE(["post", "^/index\.html$", "recent"]).
  16 ?NOTIFY_ML(new_post).
  17 post(BlogPost) ->
  18   wtype_post:create(BlogPost),
  19   
  20   {template, "blog/post/successful.html"}.

As we can see on the example above, the annotations are simply the macros put right before the function definition (the annotation is bound the first function that is declared below it). The macros definitions are put inside the header file (in this case it is utils_annotations.hrl), which is autogenerated during the compile phase.

The order of the annotations does matter: the functions corresponding to the annotations will be triggered in the same order as the order of the annotations. Although it is possible to mix the before and after annotations (the order authorize, validate, log, invalidate, notify_ml is the same as authorize, invalidate, validate, notify_ml, log) it is not recommended, since it leads to misunderstands and is error-prone.

Moreover, in order to recognize the before and after annotations, if their names do not exactly specify what kind of annotation is it, developers, when creating the annotations, can prefix/suffix their names with the proper marker (like LOG_BEFORE and INVALIDATE_AFTER).

The only limitation that is put on the annotated functions (target functions) is that their signatures must not contain bare underscore jokers: _. So we can annotate:

   1 ?LOG(hello).
   2 test(_Args) ->
   3   {template, "test.html"}.

but we can't:

   1 ?LOG(hello).
   2 test(_) ->
   3   {template, "test.html"}.

Specification

Erlang Web framework provides an easy way to define our own annotations and use them all across the project. But before we will see the annotation creation tutorial, let's look at the specification.

Annotation is simply an Erlang function that is annotated with some special, built-in annotations, that specify what kind of annotation it is (before or after). Let's take a look at the function signature:

   1 ?BEFORE. %% ?AFTER.
   2 annotation_name(AnnotationArg, TargetModule, TargetFunction, TargetFunctionArgs | TargetFunctionResult) -> Result
   3 
   4 AnnotationArg :: term()
   5 TargetModule, TargetFunction :: atom()
   6 TargetFunctionArgs :: list(term())
   7 TargetFunctionResult :: term()
   8 
   9 Result :: {proceed, NewFunctionArgs | NewResult} | {skip, NewResult} | {error, {ErrorMod, ErrorFun, ErrorArgs}}
  10 NewFunctionArgs :: list(term())
  11 NewResult :: term()
  12 ErrorMod, ErrorFun :: atom()
  13 ErrorArgs :: list(term())

The AnnotationArg is simply the parameter we pass to the macro when we call annotate some function (such as ["post", "^/index\.html$", "recent"] in ?INVALIDATE(["post", "^/index\.html$", "recent"]).). It can be an arbitrary Erlang term.

A pair TargetModule:TargetFunction and the length of the TargetFunctionArgs exactly determines the function that we are annotating (in the previous example: blog:post/1).

The last parameter of the function depends on the type of the annotation. When the annotation is of type before it becomes a TargetFunctionArgs - a list of arguments that will be passed to the target function. The annotation can modify the elements that are on the list but should not change its length (add/remove elements - it should only replace them). Otherwise, when the function is an after annotation it becomes a TargetFunctonResult - an Erlang term that has been returned from the target function.

Now let's take a look at the function result value. Function that is going to be an annotation must return one of the following values:

Built-in annotations

Erlang Web framework is distributed together with some pre-defined annotations:

Defining new annotation

The proper tutorial is here.

Annotations (last edited 2011-03-20 10:02:59 by BruceFitzsimons)