From Nick Sutterer, A Computer Science Undergraduate at Albert-Ludwigs University (Freiburg, Germany)
When writing an article about Apotomo I had to make a decision: either introduce it as a simple widget plugin for rails or - as the name Apotomo (”all power to the model”) implies - end up in monologues about model-driven component-oriented enterprize concepts. Today I will simply introduce Apotomo as a widget library for rails.
Apotomo is a widget library for rails. The concept is familiar to everyone who’s already worked with a GUI library: Take a window, draw some frames in it and throw in some buttons. Attach some logic to the buttons, hook a method to the frame and you’re done.
In Apotomo (that’s a widget library), the central place - where all this drawing and attaching happens - is the modeling tree. For your convenience I prepared a meaningful model which is the foundation of an imaginary drinking application: people can track their drinks in a database, can list what they drank and can view their current blood alcohol value. Useful? Not very.
def drinking_model_tree top_page = page("Top Page!", 'top_page') track_page = section("Tracking Page", 'tracking_page') tracking_notebook = notebook('tracking_tabs') track_tab = tab("Track a Drink", 'track_tab') list_tab = tab("List tracked", 'list_tab') level_page = section("Permille Page", 'permille_page')
top_page << cell(:drinker, :pages_menu, 'pages_menu')
top_page << track_page track_page << tracking_notebook tracking_notebook << track_tab track_tab << cell(:drinker, :track_form, 'track_cell', :states => [:track_drink]) tracking_notebook << list_tab list_tab << cell(:drinker, :list_drinks, 'list_cell') top_page << level_page level_page << cell(:drinker, :show_level, 'show_level')
return top_page end
And a controller method:
def apotomo act_as_widget('top_page', drinking_model_tree) end
It may look a bit weird at first, but it is very simple: I nest widget objects to model my application. For example, I create a notebook widget which has two tab children, that again have children.
Using the #act_as_widget method in a Controller action I can command Apotomo to render my top page.
Have a look at the rendered states of the application! Can you see the cool tabs? This is all done by Apotomo since it provides some handy and ready-to-use widgets. We will now look at some of those widgets.
Apotomo (did I mention that this is a widget library for rails?) is based on another rails plugin called “rails cells”. A typical cell looks like a controller, with methods and respective views, but is not bound to a specific controller and thus can be called throughout the application.
Every widget in Apotomo is a derived cell and can be fully adjusted to the developers needs - behaviour as well as the templates used for rendering can be overwritten. This is an important principle in Apotomo.
Pages, sections, notebooks, and tabs are all structural widgets used for grouping parts of the application. They have predefined (but customizable!) views and behaviour.
Notebooks and pages are very similar, they render themself and their current sub page/tab. Apotomo’s state- and addressing system provides information about which pages or tabs are presently focused by the user.
The crucial parts of every application - the business logic - is packed into logic cells. Looking at our example app, the line
list_tab << cell(:drinker, :list_drinks ...)
attaches the #list_drinks method of the drinker cell class to the "Tracking Page" widget. This cell method could look like
def list_drinks id = param(:user_id) @drinks = drinks_for_user_id(id) end
This really looks common to us - it’s somehow identical to a controller method.
But wait, what is this param() call?
It’s more than a widget library!
In conventional Rails parameter values are accessed using the #params method. Apotomo (Hey! Did you know that this is a widg… ok, I’ll be quiet) provides a more progressive approach to parameter accessing. Instead of looking directly in the global request parameter hash, a parameter request through the param method travels up the widget hierarchy asking every ascendent widget if it knows the value.
This opens the way for a completely new parameter management and some innovative concepts which are already implemented in so called domain widgets in Apotomo. I’ll discuss this in the next article, promised!
Another feature is the cell addressing in Apotomo. Normally in Rails you have to know the respective controller/method combination to address a specific function, e.g. when linking to another page, or in a form. We rather address widgets in Apotomo.
Let’s take the line
top_page << cell(:drinker, :pages_menu ...)
that attaches the cell method pages_menu to the Top Page to render a clickable navigation menu. The view template for this method might look like
<ul> <li><%= link_to_cell("Tracking Page", 'tracking_page') %></li> <li><%= link_to_cell("What's my level?", 'permille_page') %></li> </ul>
That’s cool - we refer to cells (or, widgets) - and this brings some great advantage: the addressing mechanism travels - similar to the parameter thing - up the hierarchy and to the target cell, asking every cell on its way if they want add something to the address. This is extremely helpful for saving state-relevant information in urls. We’ll stick to this in the next article as well.
Basically Apotomo could control a complete application. However people will be sceptical with this new concept. The #act_as_widget method provides a way to render only parts of the modeling tree within views or controllers of an existing application. For example I could integrate only the tracking-notebook widget and its tabs in some view of my app - leaving it open to the developer how much “Apotomo” he wants.
This was a very brief overview about Apotomo, it has more features that will (hopefully) be discussed in another article. I’d love to see some discussion going on at http://nick.smt.de/trac/nick/wiki/ApotomoDiscussion . If you have any issues with Apotomo feel free to mail me at email@example.com.
Props go out to Google for sponsoring Apotomo during Summer of Code 2007 and my mentor Patrick Hurley for his help and support!
More about cells and Apotomo can be found at http://apotomo.rubyforge.org.
The example app can be downloaded as standalone rails environment at http://rubyforge.org/projects/apotomo, but please notice that the API may change in the near future.