Erlang Webmachine

In a web application, the browser sends a request, the server calls a function and returns a response. Each request is independent. This maps very well to the functional programming paradigm; hence, we continue our experiments with Erlang.

Webmachine is a REST toolkit in Erlang on top of mochiweb. As an illustration, we will start with the demo application included with webmachine and add a form to it. The form will allow us to add a key/value pair or get all the key/value pairs which have some value.

Getting started

We will be using the packages included in the Fedora 20 repositories.

Make a copy of the demo in /usr/share/doc/erlang-webmachine/demo/. As the demo depends on an earlier version of webmachine, some extra steps may be needed. In the demo directory, we do the following:

$ rm rebar.config

$ rebar compile

Note: install additional modules in case of dependency issues

$ ./start.sh

The application failed to start with messages which include phrases like “not_started,ssl”. To overcome the errors, start function in src/webmachine_demo.erl was modified as follows:

start() ->

ensure_started(inets),

ensure_started(crypto),

ensure_started(syntax_tools),

ensure_started(compiler),

ensure_started(xmerl),

ensure_started(asn1),

ensure_started(public_key),

ensure_started(ssl),

ensure_started(mochiweb),

application:set_env(webmachine, webmachine_logger_module,

webmachine_logger),

ensure_started(webmachine),

application:start(webmachine_demo).

Now compilation and execution of start.sh should go through properly. Browsing http://localhost:8000/demo should show a welcome message.

Adding Functionality

The mapping between the url paths and the modules is specified in file priv/dispatch.conf. Suppose the additional functionality is to be accessed by from the url path /key. The priv/dispatch.conf will become:

%%-*- mode: erlang -*-

{["demo", '*'], webmachine_demo_resource, []}.

{["key", '*'], webmachine_key_resource, []}.

{["fs", '*'], webmachine_demo_fs_resource, [{root, "/tmp/fs"}]}.

The code needed for this functionality will be in src/webmachine_key_resource.erl:

-module(webmachine_key_resource).

-export([init/1, to_html/2]).

-include_lib("webmachine/include/webmachine.hrl").

init([]) -> Context = undefined,

{ok, Context }.



to_html(ReqData, Context) ->

% if the key is not null, store the key/value pair in mnesia table

case wrq:get_qs_value("key",ReqData) of

Key when Key==undefined, Key=="" -> ok;

Key ->

db:insert_key(Key, wrq:get_qs_value("value",ReqData))

end,

% get all the key/value pairs which match the value in search_for

{Result,SendList} = db:match_value(wrq:get_qs_value("search_for",ReqData)),

% return the result in webapp template

case Result of

atomic -> {ok, Body} = webapp_dtl:render([{the_list, SendList}]);

_ -> {ok, Body} = webapp_dtl:render([{the_list, []}])

end,

% webmachine expects a tuple

{Body, ReqData, Context}.

The resource module needs at least two functions by default, init and to_html. The init function is used to specify the context needed for this resource. Webmachine passes it as is to various functions.

The default name of the function for the GET request is to_html. In case the key field is empty or not defined, the function does nothing; otherwise, it inserts the key/value pair in a table. It then queries the table for all records which have a the desired value and displays the result in a template.

The templates/webapp.dtl template is similar to the one explained in the previous article:

<html>

<body>

<form method="GET">

<p>Key: <input type="text" name="key">

Value: <input type="text" name="value"></p>

<p> Search for Value" <input type="text" name="search_for"></p>

<input type="submit">

</form>

{% for table,key,value in the_list %}

<p> {{ key }} Value {{ value }} </p>

{% endfor %}

</body>

</html>

For completeness, the code in src/db.erl for storing and querying data from mnesia is as follows:

-module(db).

-export([init/0,insert_key/2,read_data/1,match_value/1]).

-record(store,{key,value}).

init() ->

mnesia:create_table(store,

[{attributes,record_info(fields, store)}]).

insert_key(K,V) ->

F=fun() -> mnesia:write(#store{key=K,value=V})

end,

mnesia:transaction(F).

match_value(Val) ->

Pattern = #store{_ = '_', value=Val},

F = fun() ->

mnesia:match_object(Pattern)

end,

mnesia:transaction(F).

Finally, mnesia needs to be started. Add the following lines at the start of the start function in src/webmachine_demo.erl:

application:set_env(mnesia,dir,”db”),

mnesia:create_schema([node()]),

ensure_started(mnesia),

db:init(),

Compile and run start.sh. Now browse http://localhost:8000/key and explore the form.

Getting used to Erlang is not difficult. Functional programming is fun.

References

https://github.com/basho/webmachine/wiki

http://www.erlang.org/doc/apps/mnesia/Mnesia_chap2.html

http://en.wikiversity.org/wiki/Web_Development_with_Webmachine_for_Erlang

Comments