Architecture of types

Overview

One of the first steps during the building of any application is the design. This is the point when we make decisions on the level of abstraction represented by modules, functions and variables. MVC pattern gives the developer major guidelines on the internal structure. This chapter will describe in detail the Model part, however in general it is the result of the portion of data required by user. So each choice made about the model will be mirrored directly to the final user and to the amount of information given to the user.

Origin of a problem

Dynamic web services are built on top of the database. Custom types are represented by tables. E.g. Article could be a custom complex type stored in table articles. It is a complex type because it consists of basic types: string, text and date. Working with Erlang on the edge of the functional and the declarative world raises some issues about typing. Mnesia does not store data as a specific type because Erlang is dynamically typed. Building, for example, an HTML code of forms usually uses information from the database fields definitions. To sum up we need a mechanism on the framework side to map the complex types elements to their types with a minimum of storage and performance requirements.

User friendly complex types

To solve the issue described in the previous paragraph, there is a nice and convenient way of defining your own types in Erlang Web. Such a definition consists of two records. For articles it would be article and article_types. Let’s take a look at the definitions below:

-record(article, {
          id,
          title ,
          text
         }).

-record(article_types, {
          id = {integer, [{description, "ID"},
                          {min, 1},
                          {private, true},
                          {primary_key}]},
          title = {string, [{description, "Title"},
                            {max_length, 255},
                            {min_length, 1},
                            {html, []}]},
          text = {text, [{description, "News Content"},
                         {max_length, 1 bsl 15},
                         {rows, 10},
                         {cols, 40},
                         {html, ["u", "b", "i", "a", "h1", "h2", "h3",
                                 "ul", "ol", "li", "br", "hr", "img",
                                 "center"]}]}
        }).

We place the record definition in article_records.hrl file in the include folder of our web application. The only place we should include them is wtype_articles.erl which represents the model. Record names end with _types and is obligatory for those who want to use the generic functions of the framework.

Now we know what a complex type is (article) and the location of its definition.

What kind of benefits we can expect?

Thanks to record building, HTML forms are created automatically. Validation of input values is generic. Selecting values from post and feeding controllers - generic. It is dynamic — because wtype_ implements obligatory function which returns types, this is the time and place to modify and prepare dynamic types.

Complex types can be nested. To achieve that just use as a name of type – newly defined type.

In this tutorial we will try to explain each of those mechanisms to give you a chance to customize or improve them. Look for the specification and users guide in the Reference Manual.

Deeper

As you can see in the source code above, record article_types consists of fields which values are default types and options of those types. E.g.:

title = {string, [{description, "Title"},
                  {max_length, 255},
                  {min_length, 1},
                  {html, []}]},

Specification:

Name = {Type, Options}
Type = atom()
Options = [Tuple]
Tuple = tuple()

A list of the basic types is held in basic_types.conf, which is loaded to ETS table at the start up of the application or during the configuration reload. For now, it covers almost all useful types (and we can add new types at any time).

{integer, string, date, bool, enum, text, upload, password,
 multilist, time, datetime, autocomplete, csv, float, atom}.

User-defined basic types could be also defined inside the project.conf file: this technique is described inside the project.conf section.

Options for basic types are handled in Wparts application. They are in pairs wtype_name.erl and wpart_name.erl. Supported options are listed in the Reference Manual. wpart_valid can be understood as a router delegating validation of a declared value to corresponding wtype_name.erl which must implement functions validate/1 and, in case of user defined type, get_record_info/1.

Validator is called from the controller with an argument saying where to look for the definition of the complex type. Of course it goes to wtype_complex_name.erl (in the application folder - where it represents the model). It must implement the function validate/1 which passes included types with the options to the validator. Finally, to understand this better, let’s analyze an example of adding a link to the bookmark database (URL(string), title(string), category(enum)) with the option of adding our own category (not just listed by enum type). The source code of the records will be shown in later examples - at the moment just focus on the data flow.

Simplified control flow:

  1. The controller is asked to render the form (we want to add some new link)
    • wpart_form uses wpart_derived to build adequate input forms

    • wpart_derived asks wtype_link for complex type definition.

    • wtype_link prepares the feed for enum value from the category store and sends complete dynamically created types to be derived.

    • wpart_derived calls wpart_string, wpart_enum (each of these is a basic type included in the link) to build HTML form tags with unique names

  2. Form is rendered, we fill it in and click submit button
    • all values are sent as POST
    • dispatcher identifies function link:add, but there is a validation required before calling the add function
    • the controller calls wpart_valid

    • wpart_valid goes to the model and asks for types of link; recreates unique names and takes the values from POST; sends them to adequate wtype_some basic type

    • every wtype_some_basic_type validates input value from the HTML form with built-in rules and checks, and the options defined by the user are obeyed. On success (true) returns response with {ok, Input}. Otherwise (false), returns an error: {error, {Reason, Input}}.

    • when all responses are returned, wpart_valid prepares a list with the validation results and values; sends it to the controller

    • the controller uses generic tools to retrieve information from the list, feeds the function link:add with values or builds an error message

  3. Function gets arguments
    • values can be interpreted and are sent to the model
    • wpart_link knows about the record definition and can save them into the database

Custom types are built on the base of basic types and are an integral part of the framework. Even if it is more popular now to declare the entire form as a Java Script with built in validation, it is still useful to have an internal check of HTML types with predefined attributes. On the other hand, it is very simple to build and add your own basic types, hypothetically as Java Script form, and use it inside a custom type – one example of that is type: autocomplete which is fed by the controller with the necessary values to autocomplete.

ArchitectureOfTypes (last edited 2009-03-02 20:01:30 by mwillson)