Archive for the ‘Real world examples’ Category

How to build a Dashboard with Rails

Monday, November 16th, 2009

Presenting Apotomo at the RuPy 2009 in Poznan/Poland was fun. I simply took an imaginatory dashboard, threw it in a Rails project and explained that there shouldn’t be any dashboard-less site out there.

So, my idea was to use a simple example and – of course - to make fun of my co-speaker Michał in front of the crowd. People were deeply convinced after I demonstrated a couple of useless widgets.

They instantly saw the need for small, independent components that can be reused through the whole application.

This is a brief tutorial, if you follow it you will learn how I built that simplistic dashbard with Apotomo. The sources for this part live at github. It took me no more than some hours to implement, the hardest part were all those fancy icons. I gotta love Gimp.

The dashboard controller

An empty dashboard controller.

The slim controller’s job is to display the widget slots and to define the used widgets.

There’s no black magic here, just a plain template trying to look good.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class DesktopController < ApplicationController
 
def index
 
    use_widgets do |root|
      root << cell(:weather,  :display,     'weather_forecast')
      root << cell(:michal,   :sober,       'drinker')
 
        # a section is just a container widget to group things together:
      root << todo = section('todo')
        todo << cell(:todo,   :list_init,   'todo_list')
        todo << cell(:todo,   :new,         'todo_form')
 
      root << cell(:beer,     :display,     'yummy')
      root << cell(:todo,     :today,       'todo_counter')
 
      # let michal drink:
      root.find_by_id('drinker').respond_to_event(:beerDropped, :with => :drinking)
    end
 
 
    @slot_0 = render_widget('weather_forecast')
    @slot_1 = render_widget('drinker')
    @slot_2 = render_widget('todo')
    @slot_3 = render_widget('todo_counter')
    @slot_5 = render_widget('yummy')
 
    render
  end

Again, this is kept easy and’s absolutely not the best way to handle things.

In the #use_widgets block we define what widgets to use in the dashboard. After everything is setup the widgets are rendered into controller instance variables (line 22-26).

Widgets in the Dashboard.

The only interesting here is the #respond_to_event call – I add an event handler to drinker telling it to enter :drinking state as soon as a :beerDropped event is encountered (line 18).

This should usually be done in the widget, and not in the controller code, anyway, that’s for demonstration purpose so stop the whining.

Using a sandbox

Two widgets in the sandbox.

Naturally these widgets have to be written.

By you.

When developing a widget, it’s helpful to plug it to some sandbox controller first to have an isolated environment for the new born. That’s what I did with the two alcoholic widgets that formed my main attraction.

The SandboxController action would look like this.

1
2
3
4
5
6
7
8
9
10
11
12
class SandboxController < ApplicationController
  def drink
    use_widgets do |root|
      root << drink = section('drink')
        drink << cell(:michal,   :sober,       'drinker')
        drink << cell(:beer,     :display,     'yummy')
 
        drink.respond_to_event(:beerDropped, :with => :drinking, :on => 'drinker')
    end
 
    render :text => render_widget('drink'), :layout => 'rails'
  end

That’s the end already?

This was brief. Anyway, two things became obvious:

Components are cool. You can actually use Apotomo widgets to write ..eh, web-based interactive widgets as you know them from desktop or your blog backend.

Dashboards need Components. No other application does it show better: to model dynamic user interfaces like a dashboard, you need strong encapsulation. You need widgets.

And… the widgets?

It is early morning so I will spare me any details about writing the actual widgets. Check out the source or head back to the Webhunter tutorial to learn the real shit.

In the next step we will implement the dashboard itself as a widget and make it more dynamic, so you can drop widgets in slots and thus change the layout. Nobody needs that but it looks awesome.

Ah, something I usually miss: feel free to give feedback, the comments are all yours!

A typical application: a list-and-edit widget

Saturday, June 14th, 2008

In almost any application you need listings of business objects and forms to edit these. Here’s a small example to illustrate how to implement such a …thing in Apotomo. This example is a bit more complicated, since it involves 3 nested widgets, and events. Don’t be scared anyway, it’s simple.

There’s a huge widget containing two smaller, the listing and the edit form, which are widgets itself.

Clicking on “edit” loads the respective edit form. After saving the form, the listing is updated as well.

This is awesome. How is it done? Let’s look at the widget tree first.

app/apotomo/apotomo_widget_tree.rb

1
2
3
4
5
6
root << manager = cell(:manager, :manage, 'manager_example')
  manager << manager_list = cell(:manager, :list_employees, 'manager_list')
  manager << manager_edit = cell(:manager, :edit_empty_employee, 'manager_edit')
 
  manager_list.watch(:editClick, 'manager_edit', :_edit_employee)
  manager_edit.watch(:employeeChanged, 'manager_list', :list_employees)

We attach the two widgets to its parent, called manager_example (line 2-3). The listing widget goes to its start state :list_employees, whereas the edit form widget goes to :edit_empty_employee and shows an empty form first.
Another exciting thing are the two observers (line 5-6), but more on that later.

When clicking on “edit” something happens, the edit form for the respective employee shows. We should investigate on that.

First we should look how the listing is created.

The employee list

app/cells/manager_cell.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class ManagerCell < Apotomo::StatefulWidget
 
  def transition_map
    { :edit_empty_employee => [:_edit_employee, :edit_empty_employee, ],
      :_edit_employee => [:_edit_employee, :_check_employee],
      :_check_employee => [ :_edit_employee, :_check_employee,]
    }
  end
 
  def manage
    nil
  end
 
  def list_employees
    @employees = my_employees
 
    jump_to_state :_list_employees
  end
 
  def _list_employees
  end
 
  def edit_empty_employee
    state_view :_edit_employee
  end
 
  def _edit_employee
    @employee = param(:employee)
    nil
  end
 
  def _check_employee
    name = param(:name)
 
    if @employee == name
      @msg = "You didn't change anything, idiot!"
    else
      @msg = "Changes saved."
      save_employee_name(@employee, name)
      trigger(:employeeChanged)
      @employee = name
    end
 
    state_view :_edit_employee
  end
 
  # helper methods go here...
end

The listing widget’s start_state is :list_employees, so the respective state method is executed. It loads the initial employees list and jumps to the state :_list_employees (line 14-18). Since nothing special happens here, the view for :_list_employees is rendered.

app/cells/manager_cell/_list_employees.rhtml

1
2
3
4
5
6
7
8
<ul>
  <% @employees.each do |emp| %>
    <li>
      <%= emp %>
      <%= link_to_event("edit", :type => :editClick, :employee => emp) %>
    </li>
  <% end %> 
</ul>

Common stuff. A list is rendered with links. The links are created from the Apotomo method link_to_event.

The “click-edit” event

When clicking on “edit”, an event is triggered, the source is the current widget with the id manager_list. The type of the event is :editClick.
Fine, and who cares about that? Remember the listeners? Here we go.

The “click-edit” listener

When looking at the widget tree we see that an event handler is attached to act on exactly that kind of event (line 5).

app/apotomo/apotomo_widget_tree.rb

5
manager_list.watch(:editClick, 'manager_edit', :_edit_employee)

In english, this means “if you see an editClick event fired by the manager_list widget, invoke the state :_edit_employee on the manager_edit widget!”. This is equivalent to a callback in GUI environments you’re used to. You are used to GUI development, aren’t you?

If you have a look at the respective state method :_edit_employee, you can see that the method loads the edited employee and renders its view (line 27-30).
Please note that whenever a widget changes its state also its content on the page is updated automatically. That’s why you instantly see an editable form after clicking the link.

Summary: A list widget fires an event, which leads another widget to change an empty form into a real edit form.

The edit form

After editing and clicking the submit button, the form input is sent to the manager_edit widget and asks it to go to the state :_check_employee (line 2 below).

app/cells/manager_cell/_edit_employee.rhtml

1
2
3
4
5
6
7
<%= @msg %>
<%= form_to_event :state => :_check_employee %>
 
  Employee name:
  <%= text_field_tag 'name', @employee, :size => 9 %>
  <%= submit_tag "Save changes" %>
</form>

Looking at the _check_employee state method (line 32-45) we see a form workflow as usual: check the input, and render the updated form. If the new input was valid, another event is triggered (line 40), this time it’s a :employeeChanged event.

The “employee updated” event

Now that we know how to handle events, we instantly see what happens now (line 6 below).

app/apotomo/apotomo_widget_tree.rb

6
  manager_edit.watch(:employeeChanged, 'manager_list', :list_employees)

The manager_list widget indicates interest in this event and wants itself to be pushed into the state :list_employees. The state method (line 14-18) reloads the employee list and renders the updated list.

Whoo, this was quite a lot! Three widgets, two events and a lot of states. Time to lean back and grab yourself a beer. Cheers, Peter!