Friday, October 2, 2009

First Steps With Chicago Boss

All right. Well, hopefully you found a delightful Chicago Boss graphic greeting you as you completed the Getting Started article. But that wasn't particularly satisfying. So let's build a simple "todo" application and examine the steps (and pitfalls and caveats) as we go.

1. The Models

Our simple applicaiton will have two model definitions, one for "categories" of todo items (e.g. "personal", "work") and one for the todo items themselves.

Model/todo_category.erl:
-module(todo_category, [Id, Name, Description]).
-compile(export_all).
-has_many(todo_items).

Model/todo_item.erl:
-module(todo_item, [Id, TodoCategoryId, Name, Description]).
-compile(export_all).
-belongs_to(todo_category).

OK. A word about "belongs_to" -- you must have a CamelCaseId in the main module definition which corresponds exactly to the corresponding model referenced by "belongs_to". It could have been placed at the end of the list, e.g. "-module(todo_item, [Id, Name, Description, TodoCategoryId])." but I tend to put these "foreign key" bits at the beginning. YMMV.

2. The Controller

There's a lot going on in this controller, I'll explain it later. First, the code:

Controller/todo_controller.erl:
-module(todo_controller).
-compile(export_all).

item_list(Req) ->
    Items = boss_db:find(todo_item, [], 100),
    {ok, [{item_list, Items}]}.

items_by_category(Req) ->
    CategoryId = Req:get_qs_value("category_id"),
    Category = boss_db:find(CategoryId),
    Items = boss_db:find(todo_item, [{todo_category_id, CategoryId}], 100),
    {ok, [{item_list, Items}, {category, Category}]}.

new_item_form(Req) ->
    Categories = boss_db:find(todo_category, [], 100),
    {ok, [{category_list,Categories}]}.

new_item_submit(Req) ->
    CategoryId = Req:get_post_value("item_category_id"),
    Name = Req:get_post_value("item_name"),
    Description = Req:get_post_value("item_description"),
    Item = todo_item:new(id, CategoryId, Name, Description),
    SavedItem = Item:save(),
    {redirect, lists:concat(["item?id=", [SavedItem:id()]])}.

item(Req) ->
    ItemId = Req:get_qs_value("id"),
    Item = boss_db:find(ItemId),
    {ok, [{item, Item}]}.

category_list(Req) ->
    Categories = boss_db:find(todo_category, [], 100),
    {ok, [{category_list,Categories}]}.

new_category_submit(Req) ->
    Name = Req:get_post_value("category_name"),
    Description = Req:get_post_value("category_description"),
    Category = todo_category:new(id, Name, Description),
    SavedCategory = Category:save(),
    {redirect, "category_list"}.

category(Req) ->
    CategoryId = Req:get_qs_value("id"),
    Category = boss_db:find(CategoryId),
    {ok, [{category, Category}]}.

All right. First a word about controllers in general. Chicago Boss automatically (convention, not configuration) routes requests to /some/word to Controller/some_controller.erl:word(Req) if that controller and method exist. For a simple html file, these don't have to exist. We'll see an example of that in a moment.

What I've done here is define a few web entry points to the application: "/todo/item_list", "/todo/items_by_category", "/todo/new_category_submit", etc. Most of these methods return a simple "ok" tuple along with some data. Some of them, the callbacks for create form submission, use "redirect" to send the user along to an appropriate page instead of simply presenting a "OK, thanks for your submission" page.

You also see a few of the methods available: Req:get_qs_value to get a value from a query string, and Req:get_post_value to get a value from a form submit. Of particular interest might be the "boss_db:find" examples. We could add these as ease-of-use functions in our models themselves, but for now, let's go with what we have. (Noting that I'm not showing update or delete paths yet in this snippet: so far just create and read.)

3. The Views

All right. We have defined a model and some controller routes for our web application, but to present data back to the user, we'll need some views. I'm not going to present all the views necessary to implement this application, or present them in full xhtml-strict compliance glory (what would be the fun in that?) but I will give a few of the nicer bits which show off some of the templating goodness available.

View/todo/item_list.html:
<table>
  <tr>
    <th>category</th>
    <th>name</th>
    <th>description</th>
  </tr>

{% for item in item_list %}
  <tr>
    <td><a href="category?id={{ item.todo_category.id }}">{{ item.todo_category.name }}</a></td>
    <td><a href="item?id={{ item.id }}">{{ item.name }}</a></td>
    <td>{{ item.description }}</td>
  </tr>
{% endfor %}
</table>

<a href="new_item_form">create new</a>

All right. A for loop and some inline string data. Whee. Well, actually, that's pretty much all you need to do 90% of what a view should be doing. There's more under the covers, but that will have to wait until a more in-depth article on the view template features.

Getting Started with Chicago Boss

So, you heard about Chicago Boss, the new web application framework for Erlang, and tried to get started, and ran into the brick wall of undocumentation.


No worries, mate.


First, obviously, you'll need Erlang itself, specifically version 13 and up:


wget http://erlang.org/download/otp_src_R13B02-1.tar.gz
tar xzvf otp_src...
cd otp_src...
./configure
make
sudo make install

And for Chicago Boss you'll need Tokyo Cabinet, a new-ish file-based database:


wget http://1978th.net/tokyocabinet/tokyocabinet-1.4.33.tar.gz
tar xzvf tokyoca...
cd tokyoca...
./configure
make
sudo make install

And you'll need Tokyo Tyrant, which acts as a network server for Tokyo Cabinet:


wget http://1978th.net/tokyotyrant/tokyotyrant-1.1.34.tar.gz
tar xzvf tokyoty...
cd tokyoty...
./configure
make
sudo make install

Now, Chicago Boss says you need a "table" database up and running. All this means is, in one terminal window (or screen session), run:


ttserver my.tct

Now, "my.tct" doesn't have to exist, you didn't have to learn how to use Tokyo Cabinet's commands to create it (though there are worse uses of your time), and Tokyo Tyrant will create it. The ".tct" extension means "Tokyo Cabinet Table" database, and it knows now to treat it like a table database instead of one of its other supported formats (hash, btree, etc.).


OK. Now suck down the Chicago Boss git and build it:


git clone git://evanmiller.org/git/ChicagoBoss.git
cd ChicagoBoss
make

Finally, start-dev.sh inside the ChicagoBoss git repository you've just downloaded, and visit http://localhost:8001/hello/world to ensure you've got things runnings.


Now, and here's the fun part, in another screen start adding models, views, and controllers. They are included and reloaded automatically. More on how to actually do this (another undocumentation wall) in the next post.


Update: If you found this article useful, there's a followup 'first steps' article entitled First Steps With Chicago Boss.