Archive for the ‘Cookbook’ 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!

Cells/Apotomo FAQ

Sunday, October 19th, 2008

Apotomo

I’m getting a CookieOverflow error

You need to set the session store to something else than cookie store. In environment.rb:

config.action_controller.session_store = :p_store # or better :memcache

I’m getting 500 Internal Server Error

Delete the session in your rails app
$> rm tmp/sessions/*
and restart the server.

I’m getting errors but don’t see what’s happening. What can I do?

Sorry, due to some unfixed problem with Rails’ rendering Exceptions sometimes keep stuck somewhere. Anyway, you can observe what’s going on in the log, do
$> tail -f log/development.log

Cells

How can i update my cell with RJS or/and AJAX?

Use Apotomo. What you get is basically a stateful cell on steroids.

Apotomo can upload files with AJAX!

Tuesday, August 5th, 2008

The Problem

Uploading files with AJAX is a well-known problem in the web-world. The browser security mechanism doesn’t allow JavaScript to upload from local filesystems. A work-around is the iFrame Remoting Pattern where the form is sent to an iFrame and the response is processed by the iFrame’s parent page.

The traditional Rails solution

The setup for this work-around in Rails is quite expensive, a revealing example can be found here. This example uses the responds_to_parent plugin. I’m glad people found a railish solution and released it, but I really dislike the knowledge spreading, where the controller action, the RJS code and the form template need to know about this iFrame.

How Apotomo does it

Thanks to Apotomo’s component-based nature this file upload with AJAX can be solved very clean and easy. A key principle is that the developer of the form widget doesn’t even know about this upload problem and its (current?) solution!

app/cells/backend_cell.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class BackendCell < Apotomo::StatefulWidget
  def transition_map
    { :settings => [:_update_settings],
      :_update_settings => [:_update_settings],
    }
  end
 
  def settings
    @user = param(:be_user)
    nil
  end
 
  def _update_settings
    @user.update_attributes(params[:user])
    state_view :settings
  end
end

The sample widget is very simple and has two states, one for loading and displaying the initial form (line 8-11) and one for processing the form input (line 13-16). The uploaded image is added to the @user, this is Rails magic (line 14).

app/cells/backend_cell/settings.html.haml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
%h3
  Settings for
  = @user.username
 
= form_to_event({:state => :_update_settings}, {:multipart => :true})
 
  Email: 
  = text_field :user, :email
 
  Avatar: 
  = file_field :user, :avatar_path
  = image_tag @user.avatar_path, :width => 90
 
  = submit_tag "Update"

As usual, we connect the form to its widget with a call to form_to_event (line 6), we also direct it to go into state :_update_settings. All of the iFrame work is in the {:multipart => :true} assignment in the html_options hash.

That’s all we need to tell Apotomo to provide a file upload possibility, somehow, maybe via an iFrame. You - as the programmer - don’t have to care about that. And please note that there’s no big magic involved in this call! After the form has been submitted through the iFrame the form (and maybe other widgets) is updated automatically as you’re used from Apotomo. You are used to it, aren’t you?

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!

Acting upon clicks in an ExtJS TreePanel

Tuesday, June 10th, 2008

ExtJS’ TreePanel absolutely rock. You can display complicated data structures without worrying about the rendering process at all. However, what if you want to catch a click on a node and somehow process it in the Apotomo/Ruby environment?

In this example I’ll attach an event handler to a TreePanel which updates another widget whenever a node is clicked.

First we’ll have a look at the WidgetTree setup.

app/apotomo/apotomo_widget_tree.rb

1
2
3
4
5
6
7
8
9
10
ts = Apotomo::TreeStore.new
ts[:root] = {:two=>{:two_one=>{}}, :three=>{}}
 
w << pnl = ext_panel('big_panel', :title => "Big Panel")
  pnl << tpanel = ext_tree_panel('an_ext_tree', :width => 200)    
    tpanel.set_store(ts)
 
  pnl << peer_panel = ext_panel('peer_panel', :title => "Tree peer", :width => 300)
    peer_panel << cell(:tree_peer, :init, 'my_tree_peer')
      tpanel.watch(:click, 'my_tree_peer', :_show_clicked)

When rendered, this leads to the following app state.

We can see the TreePanel widget and the “Peer Panel” widget being pushed into the “Big Panel” (line 5 and 8).

The my_tree_peer widget is in state :init, displaying a message that nothing special happened (line 9). If a click in the tree is encountered, my_tree_code is asked to go into the state :_show_clicked. (line 10).

Let’s click on a node.

In our new application state, the my_tree_peer widget reports which node was clicked. How spectacular! The Apotomo event dispatcher sent it to another state. We can find out how this new state works by looking at the widget code.

app/cells/tree_peer_cell.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TreePeerCell < Apotomo::StatefulWidget
 
  def transition_map
    { :init => [:_show_clicked],
      :_show_clicked => [:_show_clicked],
    }
  end
 
  def init
    "No item has been clicked."
  end
 
  def _show_clicked
    node_id = param(:node_id)
    "You clicked node #{node_id}."
  end
 
end

The two state methods just send static html to the screen, which is ok for demonstration purposes.

Note how we mapped a real ExtJS client-side event into a server-side Ruby event and managed all the event-handling in pure ruby. That’s Apotomo!

A Hangman game for Mike

Sunday, May 18th, 2008

My buddy Mike Pence motivated me to write a Hangman game. It shows some Apotomo concepts in a playful (painful?) way.

A screenshot from the exciting Hangman game.

The complete game is encapsulated in a widget.

app/cells/hangman_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 HangmanCell < Apotomo::StatefulWidget  
 
  def transition_map
    { :hangman => [:_guess],
      :_guess => [:_guess, :hangman]
    }
  end  
 
  def hangman
    @word     = random_word
    @guessed  = []
    @hits     = []
    @try      = 0
    @total_tries = 6
    @tries_left = @total_tries - @try
 
    nil
  end
 
  def _guess
    @try+=1
    @tries_left = @total_tries - @try
 
    guess     = param(:guess)
    @guessed  << guess 
    @hits     << guess if @word.include?(guess)
 
    if @hits.uniq.size == @word.split("").uniq.size
      jump_to_state :_you_won
    end
 
    if @tries_left == 0 
      jump_to_state :_game_over 
    end
 
    state_view :hangman
  end
 
  def _you_won
  end
 
  def _game_over
  end
 
  def random_word
    "Apotomo"   # this game IS hard.
  end  
end

To actually play, we should plug the game widget into our application widget tree.

app/apotomo/application_widget_tree.rb

  some_pg << cell(:hangman, :hangman, 'hangman_game')

I set :hangman as the start state. The respective method initializes the game variables (line 9-18) and shows the according view.

app/cells/hangman/hangman.haml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%h1
  Hangman goes Apotomo
 
(Tries left: 
= @tries_left
)
 
%h2
  - @guess_str = ""
  - @word.split("").each { |c| @guess_str+= (@guessed.include?(c) ? c : "-") }
  = @guess_str
 
= form_to_event(:state => :_guess)
%input{:type=>:text, :id=>"guess", :name=>"guess", :size => 3, :value => ""}
</form>
 
%p
  You already tried 
  = @guessed.split("").join(", ")

Whenever the form is submitted, the widgets’ state machine is set to the state :_guess (line 13), which is a valid state method in our hangman cell (line 20-37).

The _guess method just processes the guess from the player, decreases the number of tries and decides what to do next. Normally, this means it stays in the state :_guess and reuses the state view hangman (line 36).

However, if the player guessed the complete word the widget goes into the state :_you_won (line 29). Awesome.

A state diagram for the exciting hangman game.

BTW, if someone knows a good state chart drawing app, please contact me right away.

Cheers, Mike!

Observing a form

Sunday, May 18th, 2008

Apotomo introduces an event-driven approach for Rails applications. Some widgets fire events, other register handlers for these events. I’ll demonstrate this with an example, where one small widget observes a form and acts upon a certain event from this form.

Taking our simple form example, I add one line to the form processing state.

app/cells/simple_form_cell.rb

14
15
16
17
18
19
20
  def _process_form
    res = @user.update_attributes(param(:user))
    return state_view :form unless res
 
    trigger(:userCreated)
    state_view :user_created
  end

Easy: if the user can successfully be created, the form widget informs all other (interested!) widgets about this spectacular incident by triggering a generic :userCreated event (line 18).
That’s all for this form widget. It no longer is actively involved in the following steps.

The Observer
Another widget is interested in that user creation and registers an observer for that.

app/cells/form_observer_cell.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class FormObserverCell < Apotomo::StatefulWidget
  def transition_map
    { :lurk => [:_observe_form]
    }
  end
 
  def lurk
    "lurking..."
  end
 
  def _observe_form
    "I smell a userCreated event!"
  end
end

Normally the widgets lurks around in the state :lurk. When the :userCreated event is fired, it wants itself to go into the state :_observe_form. How to do that?
A common place to attach observers is the application widget tree. I love this word.

app/apotomo/application_widget_tree.rb

  playgrd << simple_form = cell(:simple_form, :form, 'simple_form')
 
  playgrd << cell(:form_observer, :lurk, 'form_observer')
  simple_form.watch(:userCreated, 'form_observer', :_observe_form)

I first add the simple_form widget to the page. Then the form_observer widget with start state :lurk.

The watch call attaches the observer to simple_form, instructing the event system to invoke :_observe_form on the widget form_observer whenever an userCreated event is triggered.
This sounds confusing but it makes sense. In GUI architectures, it is common to attach callbacks as event handlers - in our case, we assigned a state method of a widget as an event handler.

Counter

Saturday, May 17th, 2008

After being compared to seaside I had a look at this project. Looks great, it is a stateful component framework in Smalltalk. To demonstrate my concepts in Apotomo I had to steal the counter example from the page.

As usual, we implement the counter logic in a cell.

app/cells/counter_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
class CounterCell < Apotomo::StatefulWidget
 
  def transition_map
    { :_increase => [:_increase, :_decrease],
      :_decrease => [:_increase, :_decrease],
    }
  end
 
  def count
    @count = 0
    jump_to_state :_increase
  end
 
  def _increase
    @count += 1  
    state_view :_count
  end
 
  def _decrease
    @count -= 1  
    state_view :_count
  end
end

The widget is plugged into an arbitrary part of the GUI.

app/apotomo/application_widget_tree.rb

some_pg << cell(:counter, :count, 'my_counter')

Start state is :count, which initializes the counter and jumps to the state :_increment (line 10-11). Here we actually increment our counter and render the view _count (line 16). Let’s look at the view.

app/cells/counter/_count.html.erb

<h1><%= @count %></h1>
 
<%= link_to_event("--", :state => :_decrease) %>
<%= link_to_event("++", :state => :_increase) %>

Simple stuff. We create two links which reference to the current widget my_counter and push the widget state machine into the respective state. Looking at the transition map (line 4-5) we see that the requested states are permitted.

Counter state diagram

After all this coding, I have to admit that the comparison with seaside made me somewhat proud.

A “Toggle-Friendship” widget on a user profile page

Thursday, May 8th, 2008

We take an imaginary forum application. When viewing another user’s profile page, there is often an “Add buddy” button, where you can add users to your friends list. Enough talk, let’s code that.

app/cells/buddy_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
class BuddyCell < Apotomo::StatefulWidget
 
  def transition_map
    {:toggle => [:_chumup, :_breakup]}
  end
 
  def toggle
    @user    = current_user
    @viewed  = param(:drinker)
 
    if @user.friends?(@viewed)
      return state_view :break
    end
 
    state_view :chum
  end
 
  def _chumup
    @viewed  = param(:drinker)  # i forgot we're stateful!
    @user.add_friend(@viewed)
 
    jump_to_state :toggle
  end
 
  def _breakup
    @user.remove_friend(@viewed)
    jump_to_state :toggle
  end
end

The widget is inserted in the tree as follows:

app/apotomo/application_widget_tree.rb

1
2
3
profile = cell(:user, :profile, 'user_profile')
  profile << main_col = section('main_col')
    main_col << cell(:buddy, :toggle, 'buddy_toggle')

I add the friendship widget with id buddy_toggle to a section, which itself is a child of the user_profile widget, that we soon will inspect.
Why the section? Well, maybe I like to split my profile into different parts. Sections are great for that.

Since we want to display a button either for chumming up or for breaking up the friendship, toggle is a simple decider state, rendering the respective button view (line 12 and 15).
The chumup view could look like:

app/cells/buddy_cell/chum.rhtml

<%= link_to_widget("Become friend", false, 
  :state => :_chumup ) %> with <%= @viewed.username %>!

Very simple. When the link is clicked, it advises our current widget buddy_toggle to go into the state _chumup. This is allowed by the state machine, since it is a valid transition (line 4). Further on, the state _chumup will execute its state method, add our new friend and jump back to the start state toggle (line 22).
The same applies for the other way round, the breakup.

So where do @user and @viewed come from (line 8 and 9)? Let’s forget about the currently logged in user and assume it is provided by some library call. More interesting is, how comes the currently viewed user (”drinker”) into our widget: Normally you would find out who’s viewed by inspecting the request parameters for something like “&user_id=9″. This is fine, but Apotomo provides a cleaner way to do this.

The call to param (line 9) will travel up the widget tree, asking the ascending widgets for :drinker. Since we await an object of type User, it would be nice, if someone up there could provide this object. Fortunately, the user_profile widget does this.


The User profile

app/cells/buddy_cell.rb

1
2
3
4
5
6
7
class BuddyCell < Apotomo::StatefulWidget
  def profile
    @drinker = User.find(param(:drinker_id))
    set_child_param(nil, :drinker, @drinker)
    nil
  end
end

The profile method provides the view for - guess what - the profile page of the viewed user, or “drinker”, as we call him here. The actual user object is retrieved by asking for the requested drinker id, which in turn is found out by querying param (line 3). This will end up in the root widget looking at the request parameters and returning the value.

Ok, when the profile page is opened, the user_profile widget saves the currently viewed user. How can the descending widgets like buddy_toggle access it? Well, they kindly ask for it.
By calling set_child_param (line 4), the profile widget is now the official provider for the :drinker object. Since we do not set a concrete widget id (the first argument is nil), this widget will provide the :drinker object for any descending widget. Wow.

Please note line 19 in BuddyCell. Here we ask for the object again, although it is already available as instance var in the state. It will retrieve the correct object, since user_profile provides it.

A stateful form

Saturday, April 26th, 2008

You probably want some interaction in your application. BTW, that’s what makes a piece of software to an application. Let’s learn how forms are handled in Apotomo. To implement this typical workflow we write a form widget.

app/cells/simple_form_cell.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class SimpleFormCell < Apotomo::StatefulWidget
 
  def transition_map
    { :form           => [:_process_form],
      :_process_form  => [:_process_form],
    }
  end
 
  def form
    @user = User.new
    nil
  end
 
  def _process_form
    res = @user.update_attributes(param(:user))
    return state_view :form unless res
 
    state_view :user_created
  end
 
end

The form widget is hooked into the application as usual.

app/apotomo/application_widget_tree.rb

def draw(root)
  # ...
  root <<  cell(:simple_form, :form, 'my_simple_form')
end

We advise the widget to invoke its state form when it’s rendered. Let’s go stepwise through this invocation:

1. Apotomo executes the method form, where we create a new User object (line 10).

2. The associated view form is rendered.

app/cells/simple_form/form.html.erb

1
2
3
4
5
6
<%= error_messages_for 'user' %>
 
<%= form_to_event(:state => :_process_form) %>
  Username: <%= text_field 'user', 'username', :size => 9 %>
  Email:    <%= text_field 'user', 'email', :size => 9 %>
<input type="submit" />

That’s a common template known from many many other rails apps.
But wait- line 3 has a method call form_to_widget spitting out a form tag.

3. The sole argument is a hash, containing the assignment :state => :_process_form. When sent, it advises the Apotomo event dispatcher to invoke the state _process_form on our form widget.

4. The second argument for form_to_widget is usually the target widget where we want to send the form input. Here, we omit it, meaning we want to address the current widget (which is, of course, my_simple_form).

In other words, if the form is submitted by the user, our form widget goes from state form into _process_form. This state checks the input and either returns the form again (line 16), with error messages, or returns another view stating success (line 18).


What about those states?

Keep in mind that we’re stateful, so you don’t have to worry about restoring the environment between the requests. This rocks. We neither have to care about restoring the User object in _process_form (line 15 - look, it’s simply already there), nor do we have to worry about where our new state views are sent to and what part of the page they update. This is all done by the StatefulWidget!

Also note that the widget automatically goes to the correct state (line 3-6). When leaving form it stays in the state _process_form. A look at the state chart makes it clear: