My favorites | Sign in
Project Logo
                
Search
for
Updated Jan 27, 2008 by maddiin
IntroductionTutorial  

Getting your feet wet with ErlyWeb

We´ll start out with a very basic blog application, which will be expanded step by step. At the end of the tutorials section, you will have a working, full blown blog application and should know everything important to roll out your own applications with ErlyWeb.

Help

If you face any kind of problems, don´t hesitate to ask for help on the mailing list or join #erlyweb on irc.freenode.net.

Assuming you already have ErlyWeb and Yaws installed, we start off making our first application.

Creating an application

Start Yaws in interactive mode (yaws -i) and type in the Yaws shell:

erlyweb:create_app("blog", "/path/to/your/apps").

This will create an ErlyWeb directory structure as well as a few files. (Note: this initial procedure will probably be shorter when ErlyWeb matures.) This is what you should see inside your apps directory:

/apps/blog/
/apps/blog/ebin/
/apps/blog/src/blog_app_controller.erl
/apps/blog/src/components/
/apps/blog/src/components/html_container_controller.erl
/apps/blog/src/components/html_container_view.et
/apps/blog/www/
/apps/blog/www/index.html
/apps/blog/www/style.css

Edit your yaws.conf file by adding a server configuration with the following docroot, appmod, and opaque directives, then type yaws:restart().

docroot = /path/to/your/apps/blog/www
appmods = <"/", erlyweb>
<opaque>
  appname = blog
</opaque>

Open your browser and point it at http://localhost:8000/ (note: your host/port may be different, depending on your Yaws configuration). You should see the following page:

Don´t give yourself a rest on your first success. Let´s move on.

Create a database and tables

Create a database called blog with the following tables for (depending on your choice)

MySQL

CREATE TABLE category (
  id integer primary key auto_increment,
  title varchar(100),
  description text
) type=INNODB;

CREATE TABLE entry (
  id integer primary key auto_increment,
  title varchar(100),
  body text,
  created timestamp,
  category_id integer
) type=INNODB;

PostgreSQL

CREATE TABLE category (
	id SERIAL PRIMARY KEY,
	title VARCHAR(100),
	description TEXT
);

CREATE TABLE entry (
	id SERIAL PRIMARY KEY,
	title VARCHAR(100),
	body TEXT,
	created TIMESTAMP,
	category_id INTEGER
);

Mnesia

(Not yet added)

We start with two tables in our blog database, one for categories and one for entries. The tables contain a many-to-one relation, this is indicated by the category_id column in the entry table.

ErlyDb follows some Primary and Foreign Key Naming Conventions. You have to know these, but can skip it for now as it will be explained later throughout the tutorials section with examples regarding our blog application.

Now that the database and tables are in place take a deep breath, we´re diving into ErlyWeb.

Setting up Models, Views and Controllers

Create the following files inside your components directory:

Category

category.erl

-module(category).
-export([relations/0]).
    
relations() ->
    [{one_to_many, [entry]}].

category_controller.erl

-module(category_controller).
-export([detail/2, entry/2]).

detail(A, Id) ->
    Category = category:find_id(Id),
    [{data, 
        {category:title(Category),
         category:description(Category)}},
     [{ewc, category, entry, [A, Entry]} || Entry <- category:entries(Category)]
    ].

entry(A, Entry) ->
    {data, 
        {integer_to_list(entry:id(Entry)),
         entry:title(Entry),
         entry:body(Entry)}
    }. 

category_view.et

<%@ detail([{Title, Description}, Entries]) %>
<h1><% Title %></h1>
<p><% Description %></p>
<% Entries %>

<%@ entry({Id, Title, Body}) %>
<h3><a href="/entry/detail/<% Id %>"><% Title %></a></h3>
<p><% Body %></p>

Entry

entry.erl

-module(entry).
-export([relations/0]).
    
relations() ->
    [{many_to_one, [category]}].

entry_controller.erl

-module(entry_controller).
-export([index/1, entry/2, detail/2]).

index(A) ->
    Entries = entry:find_with({order_by, [{id, desc}]}),
    [{ewc, entry, entry, [A, Entry]} || Entry <- Entries].

entry(A, Entry) ->
    Category = entry:category(Entry),
    {data, 
        {integer_to_list(entry:id(Entry)),
         entry:title(Entry),
         entry:body(Entry),
         integer_to_list(category:id(Category)),
         category:title(Category)}
     }.     

detail(A, Id) ->
    Entry = entry:find_id(Id),
    Category = entry:category(Entry),
    {data, 
        {entry:title(Entry),
         entry:body(Entry),
         integer_to_list(category:id(Category)),
         category:title(Category)}
    }.

entry_view.et

<%@ index(Entry) %>
<h1>Latest Entries</h1>
<% Entry %>

<%@ entry({Id, Title, Body, Category_Id, Category_Title}) %>
<h3><a href="/entry/detail/<% Id %>"><% Title %></a></h3>
<p><% Body %></p>
<p>Category: <a href="/category/detail/<% Category_Id %>"><% Category_Title %></a></p>


<%@ detail({Title, Body, Category_Id, Category_Title}) %>
<h1><% Title %></h1>
<p><% Body %></p>
<p>Category: <a href="/category/detail/<% Category_Id %>"><% Category_Title %></a></p>

ErlyWeb treats all Erlang modules whose names don't end with controller or view and whose files exist under src/components as models.

Coming back to the many-to-one relation we previously talked about, have a look at the models to guess how its done.

Controllers are implemented in Erlang source files. Views are typically implemented in ErlTL files, but views can be implemented in plain Erlang as well. The controller file is named ComponentNamecontroller.erl and the view file is named ComponentNameview.Extension, where ComponentName is the name of the component, and Extension is erl for Erlang files and et for ErlTL files.

For more details about this see the api docs for Components, Models, Controllers and Views.

We´ve set up our Models, Controllers and Views, so its time to start the database and compile our app.

Connect to the database and compile your app

Type this commands in the Yaws Shell for (depending on your choice)

Mysql

erlydb:start(mysql, [{hostname, "localhost"}, {username, "username"},
  {password, "password"}, {database, "blog"}]).
erlyweb:compile("/path/to/apps/blog", [{erlydb_driver, mysql}]).

PostgreSQL

(Not yet added)

Mnesia

(Not yet added)

We are nearly at the end of this tutorial, but there isn´t much to see right now, so let´s add some records.

Creating Records

While still beeing in the Yaws shell, we´ll first add some categories.

Cheer = category:new(<<"Cheer">>, <<"Kermit shouts: Applause, Applause, Applause!">>).
category:insert(Cheer).

ErlyWeb = category:new(<<"ErlyWeb">>, <<"The Erlang Twist on Web Frameworks">>).
category:insert(ErlyWeb).

Now we´ll add some entries with relation to a specific category, don´t forget to unbind the previously set variables with f().

f().
Cheer = category:find({title,'=',<<"Cheer">>}).

E1 = entry:new(<<"Welcome to my new blog">>, <<"Hello World, this is my first ErlyWeb powered application. Yippie-ki-yay!">>, [], Cheer).   
entry:save(E1).

ErlyWeb = category:find({title,'=',<<"ErlyWeb">>}).

E2 = entry:new(<<"About ErlyWeb">>, <<"ErlyWeb simplifies building database-driven webapps that follow the tried and true MVC pattern using a great language with many outstanding strengths.">>, [], ErlyWeb).   
entry:save(E2).

Let´s check some ways to query the data we just created.

Explore the database api

A simple module:find() returns all records for a module.

category:find().
[{category,false,1,<<"Cheer">>,
           <<"Kermit shouts: Applause, Applause, Applause!">>},
 {category,false,2,<<"ErlyWeb">>,
           <<"The Erlang Twist on Web Frameworks">>}]

entry:find().
[{entry,false,1,<<"Welcome to my new blog">>,
        <<"Hello World, this is my first ErlyWeb powered application. Yippie-ki-yay!">>,
        {datetime,{{2008,1,23},{21,43,42}}},
        1},
 {entry,false,2,<<"About ErlyWeb">>,
        <<"ErlyWeb simplifies building database-driven webapps that follow the tried and true MVC p"...>>,
        {datetime,{{2008,1,23},{21,44,20}}},
        2}]

We can also insert a WHERE clause to find specific data as you have just seen while creating records for entries.

category:find({title,'=',<<"Cheer">>}).

Using module:find_with() we can define EXTRA expressions such as ordering and limiting records.

category:find_with({order_by, {id, desc}}).
category:find_with([{order_by, {id, desc}}, {limit,1}]).

With module:find_id() we can lookup a record with the given id value.

C1 = category:find_id(1).

You can also query for a records attributes.

category:title(C1).
category:description(C1).

Or find related records.

category:entries(C1).
entry:category(E1).

You´ll read more about the database api in full detail later.

We´re done with the introduction, congratulations.

Look Ma, no hassle

Finally, you should see this screens pointing your browser to http://localhost:8000/entry and browsing around.


Comment by maddiin, Jan 27, 2008

I don´t see the images. :( I think its because of this dumb traffic control from google asking time by time to retype a captcha. Its so "clever", it asked me for this even on logout.

Comment by marijnh, Feb 05, 2008

At the start of the tutorial, before connecting to the new app for the first time, I had to do erlyweb:compile("/path/to/app"). before it worked. (No big problem, since erlyweb showed a page suggesting just that.)

Comment by jasm.sison, Jul 11, 2008

It would be nice if the author of the intro above demonstrated how to make http://domain.somewhere/ url input would redirect to http://domain.somewhere/entry

Comment by jasm.sison, Jul 11, 2008

Also, a tutorial on how to manipulate the generated hook function by doing a erlyweb:create_app(... would be welcome

Comment by berlin.brown, Aug 05, 2008

In the event it wasn't already obvious, if you are working with cygwin or anything win32; I ended up always reverting to the windows path with the forward slash.

erlyweb:create_app("riki", "C:/tmp").

Comment by alexott, Aug 14, 2008

i found, that first example isn't working without issuing erlyweb:compile("blog"). command in yaws shell

Comment by josh.wnj, Aug 14, 2008

Great tutorial, thank you.

One thing I got caught by was, when you call erlyweb:compile("/path/to/apps/blog", [{erlydb_driver, mysql}]), make sure you give an absolute path. I tried calling erlyweb:compile(".", [{erlydb_driver, mysql}]), and it crashed halfway.

Also, I'm not sure if there was a typo in the tutorial, but in the Creating Records section I had to change:

Cheer = category:find({title,'=',<<"Cheer">>}).

to

Cheer = category:find_first({title,'=',<<"Cheer">>}).

in order to save the entry. It makes sense that I would want to use a list of categories, rather than a single category record, but when I do it crashes:

** exception exit: {{'EXIT',{badarg,[{erlang,iolist_to_binary,
                                             [{'EXIT',{badarg,[{erlang,list_to_binary,
                                                                       [[39,
                                                                         {category,false,1,<<"Cheer">>,<<"Kermit shout"...>>},
                                                                         {category,false,2,<<"ErlyWeb">>,<<"The Erla"...>>},
                                                                         39]]},
                                                               {erlsql,encode,2},
                                                               {erlsql,'-make_list/2-fun-0-',3},
                                                               {lists,foldl,3},
                                                               {erlsql,make_list,2},
                                                               {erlsql,'-insert/3-fun-2-',1},
                                                               {erlsql,'-make_list/2-fun-0-',3},
                                                               {lists,foldl,3}]}}]},
                                     {mysql_conn,do_query,5},
                                     {erlydb_mysql,update,2},
                                     {erlydb_base,'-do_save/1-fun-0-',4},
                                     {mysql_conn,do_transaction,2},
                                     {mysql_conn,loop,1}]}},
                    {rollback_result,{updated,{mysql_result,[],[],0,[]}}}}
     in function  erlydb_base:do_save/1
     in call from erlydb_base:hook/4

Sign in to add a comment
Hosted by Google Code