Apotomo is a generic widget framework for Rails. While you write the actual widgets – like a twitter box, tab panels or a full-blown shopping cart – Apotomo provides you with all you need for a component-oriented GUI application.
Here’s a quick overview:
Apotomo::Widget– your starting point. It looks and feels like a mini-controller with views, states, event handling, and more. Did you know that
Apotomo::Widget < Cell:Base? Check the cells project if you’re lost now.
- A generator to stub out all the necessary assets for you.
- Widgets can respond to events within Ruby.
- You can setup widget trees and use them throughout your
What are we gonna do?
This is a brief tutorial for setting up a small Rails 3 app with Apotomo widgets. It’s meant as a starting point for learning Apotomo, so grab a beer and read on!
The code is available in a git repo, feel free to clone.
During the course we are developing a rich dashboard application for twittering. The first widget looks like this. Pretty straight-forward.
You've already got Rails? Ok.
$ rails new bar create create app/controllers create app/helpers create app/models create app/views/layouts ...
Now, tweak your
Gemfile, we need the Apotomo gem:
Install the gems with bundler.
$ bundle install
Next, we need a layout.
Notice that we already pull JQuery and Rails UJS support into the page (line 3).
jquery-ujs needs another step.
$ rails generate jquery:install
The Tweet model
Say we’re writing a small widget that let’s you post and displays your recent posts. We need a
Tweet model for that.
$ rails generate model tweet text:string $ rake db:migrate
Widgets are usually embedded into controllers, so let’s generate a dashboard controller.
$ rails generate controller dashboard index exists app/controllers/ exists app/helpers/ ...
The first Widget!
Writing a tweet widget is as easy as creating a new controller.
$ rails generate apotomo:widget twitter display -e haml exists app/widgets/ create app/widgets/twitter exists test/widgets create app/widgets/twitter_widget.rb create app/widgets/twitter/display.html.haml create test/widgets/twitter_widget_test.rb
The widget class should look as follows. Notice that widgets reside in
1class TwitterWidget < Apotomo::Widget 2 responds_to_event :submit, :with => :process_tweet 3 4 def display 5 @tweets = Tweet.find(:all) 6 render 7 end 8 9 def process_tweet(evt) 10 Tweet.new(:text => evt[:text]).save 11 12 @tweets = Tweet.find(:all) # this is wet! 13 replace :view => :display 14 end 15end
Feeling knocked down? No problem, let’s go through this step-by-step.
How do I render widgets in the dashboard controller?
Ok, I will start the other way round, with the dashboard controller which uses the twitter widget.
Apotomo requires you to “declare” which widgets you’re gonna use in an action. And this is done in the
has_widgets block (line 3-5). We simply append our brand-new twitter widget to the
root widget caused some headache for new users. Apotomo simply provides a default root widget for your widget tree. Check the API to learn more.
Nothing will happen until we actually render the widget in the action view.
This will invoke our twitter widget’s
#display method and simply return the markup (or whatever else you do).
#render_widget invokes the default state
#display on the widget if no other options are given. Check the API to learn more.
The widget got a view
Let’s peek at the widget’s source, again.
First, we grab all available tweets (line 5). Next, we call
#render which will render
#display’s view. This is roughly similar to ordinary controllers.
The entire view is wrapped by
#widget_div (line 1) – which is just a helper doing something like
<div id="twitter"> ... </div>
#widget_div per default uses the widget’s id for the div. Check the API to learn more.
Most of the view is boring stuff. We list all twitter items (line 3-5) and then create an AJAX form. Notice how we delegate computation of the form’s action url to Apotomo by calling
#url_for_event (line 10).
#url_for_event returns an apotomo-generated url for triggering an event in your widget tree. Check the API to learn more.
Triggering an event
Now that you click “Submit”, what happens?
- The js logic wired in
rails.jsdetects a form submission. Since the form is tagged with
:remote => :truethis usually means it wants to be submitted via AJAX.
- An AJAX request is sent, to something like
- Apotomo processes this request and triggers the
:submitevent in the twitter widget.
- Now… how does the widget react?
Responding to events
Remember, our widget started like this:
That’s where we set our observer, saying “If I see a :submit event, I run my #process_tweet state!”.
The term state refers to a rendering method in a widget and is similar to an action in plain old controllers.
Updating the widget in the browser
What does the
#process_tweet method do in order to respond to the event?
Please note how the method receives the event object as argument (line 9) – how cool’s that?
The event may be queried for parameters sent with the event – this is how we retrieve the entered text from the form (line 10).
We then reload the tweets list (line 12) and call
#replace to, well, replace the widget on the page (line 13). In other words, we re-render the
display view and replace it in the browser.
#render plus an optional selector. Check the API to learn more.
You don’t have to use these helpers – they’re here for your convenience. However, you may return any content, JS or whatever using
render :text => "...".
Finally, let’s run it!
Now that we figured all the important parts, start the engine! I can hear the theme of Rocky I in the background…
$ rails s
…and browse to
Now, let’s learn how to improve our view architecture by using partials and states.