How to create an annotation

The annotation mechanism has been described here.

In this tutorial we will focus on creating the before annotation that will be dealing with the model validation. Note that creating an after annotation is exactly the same (the only differences are in the meaning of the function arguments and using ?AFTER annotation instead of ?BEFORE). Let's start!

Step 1 - defining the problem

Assume we want to create an annotation that will be used for validating the user form's input. What we want to achieve is to call:

   1 validate_tool:validate_cu(ModelName, OperationType)

always before we will save the content to the database (so - in fact before each create/update operation).

The new annotation should check the model correctness and decide whether to proceed the call flow or not. The result should look like:

   1 -VALIDATE({ModelName, OperationType}).

Step 2 - writing the function's body

We will use utils.erl module for storing all of our application annotations. Let's implement the given functionality:

   1 -module(utils).
   2 
   3 -export([validate/4]).
   4 
   5 -include_lib("eptic/include/e_annotation.hrl").
   6 
   7 ?BEFORE.
   8 validate({ModelName, OperationType}, TgtMod, _TgtFun, _TgtFunArgs) ->
   9   case validate_tool:validate_cu(ModelName, OperationType) of
  10     {ok, CorrectModel} ->
  11       {proceed, [CorrectModel]};
  12     {error, Error} ->
  13       {error, {TgtMod, validation_error, [OperationType, Error]}}
  14   end.

If the validation has been performed correctly, we are allow to call the actual target function (which will either save the record to the DB or update it). Otherwise the validation_error function from the target function's module will be called with an error description (and its returning value will be returned to the server's engine).

Step 3 - generating annotation

Having the implementation ready we are able to generate the proper header file with the ready-to-use macros. In order to do this, we must simply compile the module that holds the annotation definition:

./bin/compile.erl
Recompile: lib/test_app/src/utils.erl
Compilation completed!

As a result of compilation process, two new files has been created:

The latter file contain the ready-to-use macro ?VALIDATE(Args).

Note that the separate header file is created for each of the module that is containing any annotation definition. The header file will be situated in the include directory of the application we are working on and will be named: MODULE_NAME_annotations.hrl. The header file will contain the macro definitions of all annotations from the source file: the macro names will be the same as the function names (but uppercased). Each macro will be parametrized with only one argument. In order to change the behavior one must edit the header the file to satisfy its needs.

Step 4 - using the annotation

Ok, so now we are able to embed the annotation created in the previous step inside our module:

   1 -module(blog).
   2 
   3 -export([post_create/1, post_update/1]).
   4 -export([validation_error/2]).
   5 
   6 -include_lib("blog/include/utils_annotations.hrl").
   7 
   8 ?VALIDATE({post, create}).
   9 post_create(BlogPost) ->
  10   wtype_blog:create(BlogPost),
  11 
  12   {redirect, "/"}.
  13 
  14 
  15 ?VALIDATE({post, update}).
  16 post_update(BlogPost) ->
  17   wtype_blog:update(BlogPost),
  18 
  19   {redirect, "/"}.
  20 
  21 validation_error(Type, Reason) ->
  22   error_logger:warning_msg("~p module, error during ~p validation, reason: ~p~n",
  23                            [?MODULE, Type, Reason]),
  24   
  25   NotValidated = wtype_blog:prepare_initial(),
  26   wpart:fset("__edit", NotValidated),
  27   
  28   if
  29     Type == update ->
  30       {template, "blog/post/update.html"};
  31     true ->
  32       {template, "blog/post/add.html"}
  33   end.

From now, each post_create or post_update function call will be preceded by the validation (and in case of validation failure - will not be called).

HowTo/CreateAnnotation (last edited 2009-07-01 15:02:02 by RobertoAloi)