Archive for April, 2008

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:

Using view templates in cells

Wednesday, April 23rd, 2008

Cell states are mapped to methods. They may return a string as view. However, if you want to use a real template as state view, just return nothing. Recall our Hello World! example.

app/cells/hello_world_cell.rb

1
2
3
4
5
6
7
class HelloWorldCell < Apotomo::StatefulWidget
  def shout_hello
    @my_object = "Me? I said nothing!"
 
    nil
  end
...

When the state shout_hello is invoked, it won’t return a string but nil. This tells Apotomo to look for a template file called shout_hello.[rhtml|haml|...]. Instance variables assigned in the method can be used within the view file - just as you’re used to from controller actions!

A view might look like this:

app/cells/hello_world/shout_hello.rhtml

<h1>Hey, hello world!</h1>
What did you say? <%= @my_object %>

This gives you all the power and functionality known from the traditional rails controllers approach, along with Apotomo’s component flexibility.

But what if you dont want to name your state view after the method? Well, keep cool. Use state_view to command Apotomo to look for a view with a different name.

app/cells/hello_world_cell.rb

8
9
10
11
  def yell_hello
    # do something senseful...
    state_view :another_view
  end

This will result in the view another_view.[rhtml|haml|...] being rendered.

Hello world!

Tuesday, April 22nd, 2008

Every cookbook needs a Hello world recipe. I won’t omit this.

We will write the first small widget here. It will plug into the application and show the magic words. As widgets are derived cells, widgets that contain user application logic are called Cell in Apotomo.

app/cells/hello_world_cell.rb

1
2
3
4
5
class HelloWorldCell < Apotomo::StatefulWidget
  def say_hello
    "Hello world!"
  end
end

Note that our fresh cell widget is derived from Apotomo::StatefulWidget, which is the superclass of any widget.

So, how do we plug that widget into our application? In Apotomo, widgets are organized in a widget tree. The initial widget tree is defined in a well-known ruby class file.

app/apotomo/application_widget_tree.rb

1
2
3
4
5
class ApplicationWidgetTree < Apotomo::WidgetTree
  def draw(root)
    root << cell(:hello_world, :say_hello, 'hello_world_cell')
  end
end

By using the method cell, we advise Apotomo to render the widget HelloWorldCell and its state say_hello. A state is mapped to a method that itself returns the state view.
The third argument is an id string - it’s needed when referencing to that widget.

To push the widget in the actual application we have to go into a controller method.

1
2
3
4
5
6
7
class SomeController < ApplicationController  
  include Apotomo::ControllerHelper
 
  def top
    act_as_widget('hello_world_cell')
  end
end

The method act_as_widget plugs in our handsome widget. In the end, when navigating to some_controller/top, you will see the string “Hello world!”. Wow, this is awesome. Congratulations to your first widget.