How to catch a mouse with Apotomo
Thursday, October 15th, 2009Jaded and with half-closed eyes you look at your screen, dozens of windows of all sizes floating around in front of an army of desktop icons, and right in this digital maze a small black mouse, whizzing between menus, windows and folder symbols.
With dreamy look you follow the mouse cursor, fixing your eyes on this small speedy fellow who jumps easily through the virtual chaos. Man, it would be fun to go on an exiting mouse-hunting, like a real hunter, with javelin and traps, having thrilling adventures with the small savage gnawer.
Too tempting is the mouse-hunting. The actual finger exercise for your poor man’s browser game would be not using JavaScript but implementing with Apotomo, a widget layer for Ruby on Rails.
Apotomo?
Apotomo provides components – widgets – that behave like small Rails controllers, whereas you can use as many widgets as you need on one page. Their event-driven nature makes them perfect for rich client apps, or for your browser game.
Dozily you create a new Rails project
projects$ rails webhunter
with the growling name webhunter, after that you install the Apotomo plugin and Cells, which is the base for Apotomo.
webhunter$ script/plugin install git://github.com/apotonick/cells.git webhunter$ script/plugin install git://github.com/apotonick/apotomo.git
A widget acts as bear trap
Your first concern is having a trap since this type of hunting seems to be the more suceeding than a spear assault. A widget in the form of an oversized bear trap should be your mate when hunting the mouse cursor, the unforgivingly snapping steel claws fascinated you ever since Rocky I.
webhunter$ script/generate widget BearTrap charged snapped exists app/cells/ create app/cells/bear_trap create app/cells/bear_trap_cell.rb create app/cells/bear_trap/charged.html.erb create app/cells/bear_trap/snapped.html.erb create test/functional/test_bear_trap_cell.rb
After a courageous stroke on Enter you get your widget class and early view stubs, that all looks very similar to a conventional controller with views. Speaking about controllers: that’s another thing you would need to plug-in your trap into the page.
webhunter$ script/generate controller forest undergrowth exists app/controllers/ create app/controllers/forest_controller.rb ...
To escape from your daily office routine you put up a dark ForestController with an action undergrowth where you insidiously place your trap. By now you can hear the cracking sound of animal paws in the untergrowth, mysterious songbirds chirp their lonly melodies, the trees get shrouded in light mist.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class ForestController < ApplicationController include Apotomo::ControllerMethods def undergrowth use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') root << trap end @trap = render_widget('evil_trap') render end end |
Real Widgets in Rails
To come to the magic power of Apotomo you pull the module Apotomo::ControllerMethods in your controller (line 2). Man, that’s exciting, just yet you’re feeling like the guy from Predator.
5 6 7 8 | use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') root << trap end |
First of all you create the widget evil_trap in your controller action (line 6). The #cell method assists you when passing the name of the widget class, the start state and a unique ID, therefore :bear_trap would be BearTrapCell, :charged is the method to be executed the first time the widget is rendered and evil_trap is the unique name of your evil trap.
After you created the widget object, you attach it to an ominous root widget (line 7), which is kindly provided by the #use_widgets method (line 5).
With these lines you brought a unique trap widget into being, the hunt is on!
A bear trap on rails
For the sake of completeness you glance at your BearTrapCell to assure the state :charged isn’t doing something unreasonable, like blowing a fire or some other outrage in the bush.
app/cells/bear_trap_cell.rb
1 2 3 4 5 | class BearTrapCell < Apotomo::StatefulWidget def charged render end end |
A call to #render_widget in the controller passing the evil_trap widget seems to do nothing more than invoking the state method #charged of a certain BearTrapCell instance, where the method itself just renders its view (line 3).
10 | @trap = render_widget('evil_trap') |
To place the trap in the undergrowth you render your nasty widget to an instance variable of the controller (line 10).
Holding the rendered markup in your hand you can situate it in any location in the undergrowth.html.erb view.
/app/views/forest/undergrowth.html.erb
1 2 3 | <div id="forest"> <%= @trap %> </div> |
When peeking from your deerstand in the forest you see the rendered standard view of the trap widget in your undergrowth action.
So, Apotomo helps you building small mini-controller, or widgets, and embedding them in real existing controllers. In so doing, the widgets expose a behaviour similar to controllers, they have actions („states“) and can render views.
An interactive trap
A dark forest, the smell of firs and moss in your nose, in the distance you can hear a pecker getting his job done. You inhale deeply the humid air, you already scent it, the small gnawer, which is out for trouble in the undergrowth. Anyway you should instantly exchange the boring standard view of your trap with the likeness of a frightening bear trap, otherwise your victim will break out into roaring laughter at this sight.
app/cells/bear_trap/charged.html.erb
1 | <%= image_tag "bear_trap_charged.png" %> |
Yeah, bare steel.
OnMouseover – and the trap snaps shut!
Your mouse trap looks awesome now, anyway it lacks a triggering mechanism and a respective state for the bear trap widget when the trap has snapped.
Well, for what we got the :onMouseover event? That’s exactly what your trap needs. You extend the charged view.
app/cells/bear_trap/charged.html.erb
1 | <%= image_tag "bear_trap_charged.png", :onMouseover => trigger_event(:mouseAlarm) %> |
If the mouse steps into the trap this will trigger a :mouseAlarm typed event, the helper method #trigger_event surely worries about that.
The event is delivered via AJAX to Rails and it is Apotomo taking care of the event bubbling up from the triggering widget, the bear trap, up to the very highest root widget. Your mouse- or bear trap is directly attached at the root widget, so the bubbling won’t be that spectacular, anyway, you like the bubbling.
After reloading the page you move the mouse over the trap, and something happens… not. Indeed there is some AJAX request triggered, but the page remains the same.
Stop the mouse, eh, the event!
If you roll the mouse over the trap, seeking for a sudden death of this poor little animal, then Apotomo admittedly triggers an event and it bubbles up to the top. The bubble bursts at the top in the root widget, nothing more happens. Even worse, the bubble doesn’t burst, it is simply ignored.
Something is missing. An event hunter manifesting interest for this event, and acting uncompromising when it sees the event. An event hunter to snap the trap.
app/controllers/forest_controller.rb
5 6 7 | use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') trap.respond_to_event(:mouseAlarm, :with => :snapped) |
In your extended version of the ForestController you take the bear trap widget and stick such a watcher to it using #respond_to_event (line 7). Knowing that #trigger_event will fire an event that bubbles up from evil_trap to root, we also know the event will surely pass this watcher. This one will „see“ the event and will snap the trap by sending the evil_trap to a new state :snapped.
Widgets are deadly state machines
Excited you roll the mouse once more over the trap, again, nothing happens. What the heck is wrong here? Ouh, well, you forgot to allow this state transition in the bear trap. Without having defined transitions the widget will stay in its start state that was specified in the creating #cell(...) call.
app/cells/bear_trap_cell.rb
1 2 3 | class BearTrapCell < Apotomo::StatefulWidget transition :from => :charged, :to => :snapped |
The class method ::transition and two of its parameters :from and :to define the transition from state :charged to :snapped. Your murderous intend in mind you also extend the state method :snapped. It should output a JavaScript alert, telling us that it would have snapped.
app/cells/bear_trap_cell.rb
1 2 3 4 5 6 7 8 9 10 11 | class BearTrapCell < Apotomo::StatefulWidget transition :from => :charged, :to => :snapped def charged render end def snapped render :js => 'alert("gotcha!")' end |
So if there really is a transition happening from :charged to :snapped the state method #snapped is invoked. The method itself injects an alarm message with render :js into the page. If that will work? Once again you let the mouse step into the diabolical bear trap, and… it works!
Well, a ridiculous message doesn’t catch a mouse. A snapping claw clinging its bag with a bare-knuckle metallic „clack“, that’s what you want.
In place of harmless JavaScript your #snapped method would have to render a snapped trap.
app/cells/bear_trap_cell.rb
9 10 11 | def snapped render end |
This #render call looks for a view snapped.html.erb, which is set up out of hand.
app/cells/bear_trap/snapped.html.erb
1 | <%= image_tag 'bear_trap_snapped.png' %> |
As expected the trap snaps as you roll the mouse over it. Clack!
Summing up
Alright. You embedded your widget into a controller action utilizing #render_widget. A widget looks like a controller, has methods and respective views. In the view you used #trigger_event to report an event to rails, or better: Apotomo.
To react upon the event you used #respond_to_event. If it encounters the event your trap is sent to another state. The new state is a plain method again and renders a view. The new view automatically replaces the view of the former state on the page. That’s… awesome.
Cheese in the trap: composed widgets
For hours you’ve been lying in ambush. With bated breath you constantly keep staring at the trap, your necks starts to hurt. Darkness is falling around you.
For some reasons you didn’t catch any mouse, yet. And suddenly the scales fall from your hair: Mice do love cheese!
You decide to put a piece of cheese in the form of a separate widget into the trap widget. To hide knowledge about cheese from the trap the encapsulation of the cheese as autonomous widget makes sense, and traps usually shouldn’t be interested in the type of bait. I mean, have you ever met a customary trap that prefers gentle melting chocolate in favour of smelling cheese?
The cheese widget should consist of exactly one state, in particular it should be the state that displays a yummy piece of cheese.
webhunter$ script/generate widget cheese smelling create app/cells/cheese_cell.rb create app/cells/cheese/smelling.html.erb
Sometimes generators in rails have a right to exist, yet. You create a CheeseCell widget having the solely state :smelling, which represents the bait now.
app/cells/cheese/smelling.html.erb
1 | <%= image_tag "cheese.png" %> |
That’s incredibly exciting.
Again you open the ForestController and plug the piece of cheese into the trap by extending the widget tree.
4 5 6 7 | def undergrowth use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') trap << cell(:cheese, :smelling, 'piece_of_cheese') |
The assignment in line 7 adds a child to the evil_trap widget.
Yeah, that’s the way it goes!
Cheese!
Of course, the child is the CheeseCell widget with start state :smelling, as a unique ID you pass piece_of_cheese.
The last thing you gotta do is extending the view of your trap so it displays the bait.
app/cells/bear_trap/charged.html.erb
1 2 | <%= image_tag "bear_trap_charged.png", :onMouseover => trigger_event(:mouseAlarm) %> <%= rendered_children['piece_of_cheese'] %> |
Delighted you notice that your trap looks very attractive once it contains a piece of cheese.
Getting already rendered children seems to work by accessing @rendered_childrenrendered_children. That’s how you can place children in the views of their parents.
For completeness you have to extend the view of the snapped trap, too. For example, it could happen that a bear would actually trigger the trap, a bear that wouldn’t eat the cheese and thus be stuck in the trap together with the cheese.
app/cells/bear_trap/snapped.html.erb
1 2 | <%= image_tag 'bear_trap_snapped.png' %> <%= rendered_children['piece_of_cheese'] %> |
Assault of the Hungry Mouse
So there is cheese in the charged trap. As soon as the mouse toddles into the trap it snaps shut, but the cheese remains in the trap. „What a non-sense!“ you think, shouldn’t the cheese disappear, and the trap stay charged? Considering the fly weight a mouse wouldn’t trigger a bear trap.
The cheese-eating logic neither belongs in the cheese widget, nor in the trap. You need a new widget, a mouse widget.
$ script/generate widget mouse lurking eating exists app/cells/ create app/cells/mouse create app/cells/mouse_cell.rb create app/cells/mouse/lurking.html.erb create app/cells/mouse/eating.html.erb create test/functional/test_mouse_cell.rb
To have the mouse placed in the forest you append it to root, as you did with the trap. In order to do that you have to return to #use_widgets in the controller to call the mouse to life.
app/controllers/forest_controller.rb
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def undergrowth use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') trap << cell(:cheese, :smelling, 'piece_of_cheese') #trap.respond_to_event(:mouseAlarm, :with => :charged) root << trap root << cell(:mouse, :lurking, 'hungry_mouse') end @trap = render_widget('evil_trap') @mouse = render_widget('hungry_mouse') render end |
First of all you uncomment the watcher that snaps the trap (line 8). You’re gonna think about that later, and moreover you don’t want to hurt the sweet mouse.
Next, you add the new mouse widget to the widget tree, namely in the start state :lurking (line 11). To place the mouse in the controller view you render it using #render_widget (line 15).
app/cells/mouse/lurking.html.erb
1 | <%= image_tag "mouse.png" %> |
After you polished the view of the :lurking state you got the two duellists mouse and trap standing face to face.
Bubbling events
To steal the cheese from the trap the mouse has to catch the :mouseAlarm event and eat the cheese. Again you extend the #use_widgets block in the controller.
app/controllers/forest_controller.rb
4 5 6 7 8 9 10 11 12 | def undergrowth use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') trap << cell(:cheese, :smelling, 'piece_of_cheese') root << trap root << cell(:mouse, :lurking, 'hungry_mouse') root.respond_to_event(:mouseAlarm, :with => :eating, :on => 'hungry_mouse') end |
You append a new watcher to root (line 11) and read out aloud that line.
„Root, if you catch an event of type :mouseAlarm respond to it by invoking the state :eating on the hungry_mouse widget.“
You cannot add this watcher directly to the mouse widget. Why? Well, the :mouseAlarm event is triggered by the trap and it bubbles up to root. On its way to the top it doesn’t pass the mouse at all, so it’s root who does the job.
Finally: Statefulness!
Instead of making the cheese disappear you decide to count the pieces of eaten cheese in the mouse widget. That widget class already got a bunch of lines.
app/cells/mouse_cell.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class MouseCell < Apotomo::StatefulWidget transition :from => :lurking, :to => :eating transition :in => :eating def lurking @cheese_count = 0 render end def eating @cheese_count += 1 render :view => :lurking end end |
Hey, wouldn’t it be fun if the mouse would get fatter with every piece of cheese it eats? You implement that instantaneously by enhancing the view of :lurking.
app/cells/mouse/lurking.html.erb
1 | <%= image_tag "mouse.png", :width => 90 * (@cheese_count+1) %> |
The image width is recalculated every time the widget renders, making the mouse fatter and fatter with every piece.
Ok, what’s going on here?
From state to state
When sending the mouse in the trap this triggers the well-known :mouseAlarm event. That one is catched and sends the hungry_mouse widget to the state :eating. As this is a state transition initiated from outside (namely by the #respond_to_event watcher) this transition has to be allowed (line 2).
After that, the mouse keeps eating in the state :eating. Another event would send the widget from :eating to :eating. And that must be allowed as well (line 3).
Counting cheese
You initialize the cheese counter in the start state :lurking and set it to zero, so the mouse has an empty stomach (line 6).
app/cells/mouse_cell.rb
5 6 7 8 9 10 11 12 13 | def lurking @cheese_count = 0 render end def eating @cheese_count += 1 render :view => :lurking end |
As you already defined the mouse stays in :eating state, its state method is executed again and again with every event. The method worries about incrementing the cheese counter (line 11).
When it’s up-to-date the view of the :lurking state is rendered, and a growing mouse is displayed (line 12).
Statefulness and automatic updates
You note happily that the instance variable @cheese_count keeps actually established although there are multiple requests between the events. That’s the stateful widgets everybody’s on.
Useless to say that every widget, even our fat mouse, keeps itself updated on the page via AJAX. Or did you write one line of JavaScript yet, since you develop this trailblazing mouse-game?
Talking widgets
In fact it would be consequent to make the trap snap shut as the mouse grows and reaches a sufficient weight to trigger.
Somehow the trap has to detect if the mouse is heavy enough. Maybe you should bring the trap watcher back into play.
app/controllers/forest_controller.rb
4 5 6 7 8 9 | def undergrowth use_widgets do |root| trap = cell(:bear_trap, :charged, 'evil_trap') trap << cell(:cheese, :smelling, 'piece_of_cheese') trap.respond_to_event(:mouseAlarm, :with => :snapped) ... |
Not only the mouse but also the trap are watching the :mouseAlarm event now.
Yes- it’s absolutely ok to have multiple watchers for one event.
The bear traps’ state :snapped should decide if it’s worth snapping or if we’re better of waiting for fatter booties.
At the moment the trap snaps and the mouse gets a bit fatter when moving the cursor over the trap. However, the mouse should be really fat to trigger the trap, at least 3 pieces of cheese have to be eaten, you decide.
app/cells/bear_trap_cell.rb
1 2 3 4 5 6 7 8 9 10 11 | class BearTrapCell < Apotomo::StatefulWidget transition :from => :charged, :to => :snapped def charged render end def snapped jump_to_state :charged unless root.find_by_id('hungry_mouse').cheese_count >= 3 render end |
The bear trap is sent to :snapped at each and every :mouseAlarm. However, it will stay in this state only if the cheese counter of the mouse is really greater than 3 (line 9).
If not, it calls the method #jump_to_state and jumps back to :charged and waits for bigger booties.
Obviously this does work properly only if the mouse answers to #cheese_count.
app/cells/mouse_cell.rb
1 2 3 4 5 | class MouseCell < Apotomo::StatefulWidget transition :from => :lurking, :to => :eating transition :in => :eating attr_reader :cheese_count |
Not only the mouse gets bigger now when stealing cheese from the trap, also the trap’s in and queries the mouse if it is heavy enough by chance.
And, wow, that works! The fourth time rolling over the trap snaps shut.
That’s enough for today, folks!
It would be nice if both cheese and mouse would disappear, since the cheese is eaten and the fat mouse slopes off as the trap snaps with a loud „clack!“. Or have you ever seen a mouse that gets caught in a bear trap?
Also the dependency annoys you that you created in the bear trap – at the moment it is aware of the hungry_mouse and asks the mouse widget in the #snapped state method, that shouldn’t be the case with independent components.
„Well, I bet this can be done even better with Apotomo, but for today I’m done!“ you say to yourself, lean back and enjoy the fact that no mouse has been harmed in your game, yet.
The running example code can be found at github.












