Friday, February 23, 2007

ErlyWeb Tutorial: Life In the Intersection of FP and Dynamic HTML

Web applications often have pages in which the user can edit the fields of an existing database record and then save the updated record by submitting the form. To generate such an 'edit' page, you generally have to read the record from the database, generate an HTML form whose fields are pre-populated with the record's data, and then send the form back to the browser. Let's look at a few ways of doing this with ErlyWeb (for instructional purposes, we won't be using "-erlyweb_magic(on)." :) ).

Let's suppose we want to provide a form for editing the records of a database table called 'language'. We can create an ErlyDB module for working with this table as follows:

language.erl:


-module(language).


When we call erlyweb:compile() (or when ErlyWeb auto compiles our modules becase we have turned 'auto_compile' on, as one should do during development), ErlyDB reads the metadata for the 'language' database table and generates a large number of functions in the 'language' module covering the whole exciting CRUD gamut.

Now let's create a basic controller implementing the data access logic for the 'language edit' page. I will only show the most stripped-down logic necessary for rendering the 'edit' form, which is the focus of this tutorial.

language_controller.erl:

-module(language_controller).
-export([edit/2]).

edit(A, Id) ->
case language:find_id(Id) of
undefined ->
{data, {error, no_such_record}};
Language ->
{data, Language}
end.


When ErlyWeb receives a request such as "http://myapp.com/language/edit/1", it will invoke the 'edit/2' function in language_controller. This function will look up the database record using a SQL query such as "SELECT * FROM language WHERE id=1", and send the resulting record directly to the view function, or the tuple {error, no_such_record} if no record matches the requested id.

Now let's take a first pass at making a the view for this component (note that in this tutorial, we'll avoid any fancy automatic form-generation methods and write most of the HTML by hand).

language_view.et:

<%@ edit({error, no_such_record}) %>
ouch, the record doesn't exist


<%@ edit(Language) %>

name:


paradigm:

....




Looks good so far? Well, it's not really supposed to, because this code has some problems.

The most urgent problem is that this code doesn't check that the record's fields are formatted as iolists. If any of the record's values is 'undefined', for example, this code will cause the process to crash (by 'crash' I don't mean the catastrophic definition of 'crash' that is used with most programming languages, but the Erlang definition, which means the lightweight process in which the crash occurs has died -- an isolated event inside the VM, nothing more).

We can address this issue in a few different ways. I'll show you one simple way, in which we create a function in the view module to do the proper conversion. Using this method, this would be the modified code for the new language.et:


<%@ edit(Language) %>

name:


paradigm:

....



<%@ show(Language, Field) %>
<% erlydb_base:field_to_iolist(language:Field(Language)) %>


Note that this code uses the Erlang way of doing dynamic function dispatch: you use a variable in place of the module's function (and/or the module itself) you want to call. This is an extremely useful feature -- know it and it will serve you well.

The code works, but it irks me somewhat. The issue is that I don't want the view logic to know anything about ErlyDB records and how to access and format their fields. Views are supposed to be simple, and should only use the most rudimentary logic required to render their parameters. Putting ErlyDB logic in the view violates this rule.

So, how do we fix this issue? We have a few options. One option is for the controller to pass the view a list of pre-rendered fields instead of the Language record itself. This could be done in the controller by returning the following:


{data, [erlydb_base:field_to_iolist(language:Field(Language)) ||
Field <- language:db_field_names()]}


To handle this list, the view function would have to change, too:


<%@ edit([Name, Paradigm, ....]) %>

name:


paradigm:

....


Using this approach, we've succeeded at stripping the view function of ErlyDB-related logic, but there is a downside: we now have to maintain as the function parameter a list of field variables that matches exactly the length and order of the field list returned from language:db_field_names(). This is acceptable for records whose field list is small and doesn't change much, but for records with large numbers of fields this quickly becomes a pain to maintain.

Our search for the ideal solution isn't done, but from my experience with Erlang in specific and functional programming in general, the ideal solution -- elegant, clean, flexible -- almost always exists, even if you haven't found it yet :)

What we ultimately want is to be able to look up properly formatted field values by name without worrying about the fields' order and without calling ErlyDB functions (or functions generated by ErlyDB) directly. To do this, we can go the "traditional" (i.e. lame :) ) route of passing the view function a data structure mapping field name keys to data values. In Erlang, this could be a simple property list, a dict (i.e. a hash table), a gb_trees structure, or some other associative container. With this approach, the view function would populate the field values using snippets such as


<% dict:find(name, LanguageDict) %>


However, I don't like this approach because it incurs the overhead of creating and using the associative container (in a real app, this overhead would probably be negligible, but I still don't like it :) ), and it's tedious to repeatedly call the associative container functions to look up field values. In addition, real purists would argue that the view function shouldn't know the type of the container, or even that we're using some associative container to look up the values of our fields in the first place!

FP to the rescue.

My favorite solution to our view dilemma is to change the controller's 'edit' function as follows:


edit(A, Id) ->
case language:find_id(Id) of
undefined ->
{data, {error, no_such_record}};
Language ->
{data,
fun(Field) ->
erlydb_base:field_to_iolist(language:Field(Language))
end}
end.


What's going on here? Instead of passing the view function some data structure, we pass it a closure, i.e. a function whose logic uses one or more variables that exist in the closure's lexical scope. Using this closure, we can get the properly formatted value for any field of the Language record with minimal performance -- and coding -- overhead. Here's how we use it in the view function:


<%@ edit(F) %>

name:


paradigm:

....


As you can see, using closures in this scenario gives us the best of all worlds: almost no performance overhead, minimal code, no maintenance of field lists, and clean separation between controller and view logic.

In summary, if you want to keep your code clean, simple, and properly decoupled, closures are often your best friends.

Monday, February 12, 2007

Introducing... The ErlyWeb Logo!

My plea for help in the graphics design department in the making of an ErlyWeb logo has yielded a few candidates, and although I sincerely appreciate the efforts of the people who made them, and they gave me some good ideas, none of them has truly captured my fancy. So, I decided to take matters into my own hands and finally create a real logo for ErlyWeb.

Without further ado, I hereby present ErlyWeb's shiny new logo:





(This is the big version. A small version is on http://erlyweb.org.)

This logo is inspired by Erlang, Big Sur and Lisa Simpson.

(When I wrote that my graphic design skills were non-existent, you didn't think it really meant there was a Picasso inside me trying to break out, did you? :-) )

Wednesday, February 07, 2007

ErlyWeb Tutorial: Using Components To Create Dynamic Containers

ErlyWeb's app views are a basic mechanism for sharing common view elements between components. An app view is a simple Erlang module or ErlTL template file that nests pre-rendered components in static (generally HTML) content. Most simple applications probably have an app view that looks like this:




My App


<% Data %>




This mechanism works well when your components share only static view elements. But what do you do when your components share dynamic view elements? If this is the case, you have a few options:

- You could put the logic for generating the dynamic content in the view module. However, this would be ugly, and it would go against one of ErlyWeb's primary purposes, which is to help you create a clean separation between application logic and view logic. Let's forget this option and move on.

- If all your components share an identical set of dynamic elements, you can change your app controller's hook function, so instead of returning '{ewc, A}', it returns a list containing the 'ewc' and/or 'data' tuples common to all components. For example, if all your pages have sidebar, your hook/1 function could return '[{ewc, sidebar, [A]}, {ewc, A}]'. This tells ErlyWeb render each component in the list, and then pass into the app view a 2 element list, wherein the first element is the rendered sidebar and the second element is the rendered component that corresponds to the browser's request. To properly handle this new parameter type, you could change your app view as follows:


<%? [Sidebar, Content] = Data %>


My App







<% Sidebar %> <% Content %>





Again, this works well when all your pages have the same sidebar component. However, what if different pages have different sidebar components? Or what if some pages have a sidebar and some don't? Let's look at the next option.

- Starting from ErlyWeb 0.4, your controllers can return a {response, Elems} tuple, whose Elems list may include the {app_view_param, Ewc} tuple (for more info on the 'response' tuple, check out the ErlyWeb docs. Similar to the first option, this response tells ErlyWeb to render the (list of) 'ewc' and/or 'data' tuples contained in the Ewc variable, and then pass into the app view a 2-element list containing the rendered components followed by the rendered content for the request. In addition, you can use the {app_view, ModuleName} tuple to pick a specific app view (or disable the app view entirely) instead of using the default one.

This approach works, and it offers greater conveniece in customizing the dynamic app view data for each component (or each component function), but I don't like it. The reason is that I don't think that components should know or control what happens in the areas of the page outside of their own rendered HTML. In addition, it gets confusing if multiple nested sub-components define the 'app_view_param' element in their responses: which one should ErlyWeb be use? Given these shortcomings, I'm considering deprecating if not outright removing support for the 'app_view' and 'app_view_param' tuples from the 'response' tuple in ErlyWeb (it is and an experimental feature, after all, as the documentation notes). So, let's look at the next option.

- The last, and arguably best approach, is to use ErlyWeb's component system to create dynamic containers. A container is a component that contains one or more sub-components, which are given to the container as parameters. The container tells ErlyWeb to render its parameters together with any dynamic data specific to the container. For example, to create a container that renders its sub-component(s) together with a sidebar, you could use the following code:

sidebar_container_controller.erl:

-module(sidebar_container_controller).
-compile(export_all).

%% This tells ErlyWeb to reject browser requests that try to
%% access this component directly
private() -> true.

index(A, Ewc) ->
[{ewc, sidebar, A}, Ewc].


sidebar_container_view.et:

<% Data %>

<%@ index([Sidebar, Contents]) %>





<% Sidebar %><% Contents %>



Using this container, any component can render a subcomponent with a sidebar by simply returning the tuple '{ewc, sidebar_container, [A, {ewc, subcomponent, [A]}]}'. What I like about this container, beside its flexibility and reusability, is that it doesn't require any special logic nor does it rely on any features beyond ErlyWeb's simple and elegant component model.

An ideal place to use this container is in the app controller's hook function. For example, to achieve the same effect as the app view-based approach from the second option, you could implement this function as follows:


hook(A) ->
Ewc = erlyweb:get_initial_ewc({ewc, A}, myapp_erlyweb_data),
{ewc, sidebar_container, [A, Ewc]}.


(The first line may look a bit cryptic -- indeed, it relies on a function that isn't included yet in the official distribution -- but just know that it enforces the 'private' policies on component requests that come from clients.)

A nice thing about this approach is that it helps simplify the app view, which doesn't require unpacking the Data parameter anymore and laying out the sidebar and the contents as the second example illustrates.

We could take this a step further and make the app view obsolete by creating an HTML page container as follows:

html_container_controller.erl:

-module(html_container_controller).
-compile(export_all).

private() -> true.

index(A, Ewc) ->
Ewc.


html_container_view.et:

<% Data %>

<%@ index(Data) %>


My App


<% Data %>




Now, we could implement hook/1 as follows:


hook(A) ->
Ewc = erlyweb:get_initial_ewc({ewc, A}, myapp_erlyweb_data),
{ewc, html_container,
[A, {ewc, sidebar_container, [A, Ewc]}]}.


With this container structure, out app view becomes


<% Data %>


As you can see, using the container approach, we've replaced the somewhat crippled app view with a much more flexible and powerful ErlyWeb component. Not bad, huh?

So where does this lead us? Will app views soon be a thing of the past? I think this may very well happen :)