Trellis Tutorial #1 - The HiLo Web Application

Posted by Brian Sam-Bodden Tue, 06 Oct 2009 19:08:00 GMT

Trellis HiLo Game

The Trellis Hilo Game is a simple application where the user guesses a number between one and ten. This example was ‘borrowed’ from the Apache Tapestry project and ported to Trellis.

The simple diagram shows the application’s page flow, which consists of three pages: Start, Guess and Game over.

Trellis Hilo Page Flow

Figure 1 - Hilo Application Page Flow

The Start page simply provides a link to start guessing. The guessing happens in the Guess page. The Guess page presents the user with ten links for the numbers from 1 to 10. When the user selects incorrectly the page responds with an appropriate message such as “Guess X is too low” or “Guess X is too high”. If the user selects the correct number the application navigates to the GameOver page, which reports how many guesses it took to find the correct answer.

The Application Structure

The HiLo web application structure consists of 1 Ruby file and possibly 3 XHTML templates:

Trellis Hilo Directory Structure

Figure 2 - Trellis Hilo Directory Structure

It is not required that you organized your application as shown but if you want to avoid having to specify your view template locations, Trellis will assume the structure above when searching for a template.

Installing Trellis

gem install trellis

The Trellis Application Class

Let’s start at the beginning and create a Trellis application. Let’s call it HiLoGame and tuck it in a module named Hilo. At the top we’ll require the RubyGems and Trellis Gems and to simplify the code we’ll include the Trellis module:

require 'rubygems'
require 'trellis'

include Trellis

module HiLo
  class HiLoGame < Application
    home :start
  end
end

The class HiLoGame extends Trellis::Application which represents a Web Application composed of one or more pages. Inside the class body the ‘home’ class method is used to declare the home page of the application. That is, the entry point and what the URI ‘/’ will be resolved to. In the case of the HiLoGame application the entry point is indicated by the symbol :start. The Trellis convention is that there will be a Trellis Page class named Start.

The Start Page

The Start page will provide a simple welcome message and a link for the users to start guessing numbers. Let’s start with the view and show the HTML markup:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Hi/Lo Game Start Page</title>
    </head>
    <body>
      <h1>Hi/Lo Guess</h1>
      <p>I'm thinking of a number between one and ten ... </p>
      <p>
        <trellis:action_link>Start guessing</trellis:action_link>
      </p>
    </body>
</html>

Everything in the markup above should be familiar except for the <trellis:action_link> tag. The tag represents one of Trellis’ core components, the ActionLink component. ActionLink is a simple stateless component that provides a hyperlink to trigger an action. The URI produced represents the creation of an event. Events in Trellis have names. The name of the event generated by the ActionLink component is called “select”. Now that we have a template, let’s create the Trellis page class that will serve that template. The Start page is a simple class that extends the Trellis Page class.

  class Start < Page
  end

Launching the Application

Next, let’s add a bit of code so that we can launch the application. In order to run the application we need an instance of HiLoGame for which we will invoke the start() method, as follows:

  web_app = HiLoGame.new
  web_app.start if __FILE__ == $PROGRAM_NAME

Note: The HiLoGame and all page classes are contained inside the HiLo module as well as the two lines above to instantiate and run the application.

With the skeleton page in place and its corresponding template we can now run the application like:

ruby hilo.rb

At the console you should see output similar to (Trellis uses Mongrel by default):

091005 19:51:51 INFO: Starting Trellis Application HiLo::HiLoGame on port 3000 
091005 19:51:51 INFO: watching /Users/bsbodden/Documents/projects/trellis/examples/test/source/../html/ for template changes... 

Launch your browser and point it to http://localhost:3000 and you should see the home page of our Hilo application as shown next:

Trellis Hilo Start Page v1.0

Figure 3 - Trellis Hilo Start Page

On the console we can see the GET request for the root “/” URI:

090921 15:30:22 INFO: 127.0.0.1 - - [21/Sep/2009 15:30:22] "GET / HTTP/1.1" 200 558 0.0025 

By now, you probably have already clicked on the “Start Guessing” link and the application has appropriately blown chunks and showed you an error page describing the problem such as:

Trellis Error Page

Figure 4 - Trellis Error Page

Events

The error page indicates that the application could not find a method named on_select(). If you remember I mentioned that the ActionLink component generates an event called ‘select’. When a page gets an event with a name “bacon”, it looks for an event handler method named on_bacon().

Start Guessing Link

Figure 5 - Start Guessing Link

The return value of an event handler method determines where the application will navigate to next. Inside of a page for example, returning ‘self’ will make the application navigate to the same page.

Let’s add a place holder on_select() event handler to the start page and for now let’s just make it navigate back to the start page.

    def on_select
      self
    end

Go back to / or /start to refresh the page. If you hover over the link on the page you can see the URI generated by the ActionLink component. As you can see a page has an /events namespace in which we can ‘drop’ and event. In this case ‘select’. You can see the pattern now: /page/events/event

Component Event Produced Event Handler
ActionLink select on_select()

Clicking on the “Start Guessing” link again should now take you back to the start page.

By now you have probably noticed that it wasn’t necessary to restart the application after each change. Trellis will detect changes to your application’s code and templates and automatically reload them. We can now navigate to the same page but what we really want to do is navigate to the not-yet-existent Guess page.

Navigating To Other Pages

Trellis pages need to declare any other pages that they might redirect to. To declare the possible page transitions we use the class method ‘pages’. Since the Start page can only navigate to the Guess page, we declare that using the :guess symbol referring to a Guess page that we’ll develop next.

  class Start < Page
    pages :guess

    def on_select
      self
    end
  end

Injected pages such as :guess get their own instance variables (@guess) available to the page. We can change the on_select() event handler to return an instance of the Guess page and also perform some work along the way as shown below:

    def on_select
      @guess.initialize_target
    end

Obviously we haven’t yet implemented the initialize_target() method, hell we don’t even have a Guess page yet!

Stand In Pages

Yet, if you refresh the start page and click the “Start Guessing” link you should see something like:

Trellis Stand-In Page for Guess

Figure 6 - Trellis Stand-In Page for Guess

In development mode (which is the default) Trellis will generate synthetic pages or “Stand-In” pages for any injected pages for which it cannot find an implementation. Invoking any method on a stand in page returns the page object itself. In the case of the on_select() method, the return value is the guess page itself, since calling the initialize_target() method will just return the guess page object.

Stand-In pages enable incremental development, you can think of them as virtual scaffolding that automatically fades away as you provide a page’s implementation.

Markup Generation Choices

Trellis doesn’t force you into static HTML templates. External HTML templates are the right medium for the markup if you are somewhat detached from the look and feel specifics and somebody else is providing the markup for you to incorporate into the application.

I find that in most cases the type of relationship with a designer happens at the CSS level rather than at the HTML markup level. In terms of productivity and I find it easier to inline my templates in the page class definition using one of Trellis supported formats such as ERubis, Markaby, HAML, Textile or Markdown. For simple pages the markup does not overwhelm the Ruby code (and if using Markaby it actually is Ruby code!). For completeness sake let’s show how we could implement the template for the start page using an inline Markaby template:

  class Start < Page
    pages :guess

    def on_select
      @guess.initialize_target
    end

    template do
      xhtml_strict {
        head { title "Hi/Lo Game Start Page" }
        body {
          h1 "Hi/Lo Guess"
          p "I'm thinking of a number between one and ten ..."
          p {
            text %[[Start guessing]]
          }
        }
      }
    end
  end

The Guess Page

We have reach the point were we can add a basic implementation of the Guess page and the initialize_target() method. For the template we can quickly be up and running by in-lining with Markaby:

  class Guess < Page
    def initialize_target
      self
    end

    template do
      xhtml_strict { body { h1 "Place Holder for Guess Page" }}
    end  
end

Clicking the “Start guessing” link results in the application navigating to the newly developed Guess page as shown next:

Trellis Hilo Guess Page

Figure 7 - Trellis Stand-In Page for Guess

The console output shows the result of processing the “select” event:

090921 16:24:45 INFO: 127.0.0.1 - - [21/Sep/2009 16:24:45] "GET /start/events/select HTTP/1.1" 302 - 0.0006 
090921 16:24:45 INFO: 127.0.0.1 - - [21/Sep/2009 16:24:45] "GET /guess HTTP/1.1" 200 242 0.0007 

Notice that clicking on the link generated two HTTP requests. After the event handler code is executed Trellis will generate a redirect response (HTTP code 302) to the target page. This is a common pattern in Web frameworks called redirect-after-post (except we weren’t posting).

With a basic understanding of how Trellis handles events and renders pages, we can now complete the Guess page (the workhorse of the HiLo application). In the Guess page we need a way to keep the number to be guessed, the count of how many guesses have been attempted and the message to be shown on the page (“Guess X is too low” or “Guess X is too high”).

Let’s use 3 instance variables @target, @count and @message. We’ll use Ruby’s parallel assignment to initialize those values in the initialize_target() method. We’ll also set @target to a random number between 1 and 10 using Ruby’s rand().

    def initialize_target
      @target, @count, @message = rand(9) + 1, 0, ''
      self
    end

Let’s progressively build the template for the Guess page. This will allow us to introduce other Trellis components one at a time. Let’s start with a simple static template as shown next, call it guess.xhtml (also let’s remove the inline template in the Guess page):

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Guess A Number</title>
    </head>
    <body>
        <p>Make a guess between one and ten:</p>
    </body>
</html>

The Guess page should now look like:

Trellis Hilo Guess Page

Figure 8 - Trellis Hilo Guess Page

Let’s start by adding the message (@message) variable value to the page. To do so we can use the Value component. The value component can output the value (to_s) of a page instance variable. For example to output the value of message we would use:

Too low or two high message

Of course since we’ve initialize the value to an empty string we won’t see anything. To quickly test we can change the value to something like:

      @target, @count, @message = rand(9) + 1, 0, 'Hello'

Clicking the “Start guessing” link still brings a page where the message is empty. The problem we are facing is that as we learned before all event handling processing results in a redirect (HTTP 302). Pages are not persistent objects, therefore every time we process the redirect we lose any store state in the page.

Page Persistent Data

To make page properties persistent between requests we need to declare them with the class method ‘persistent’, passing any symbols for the instance variables. In the case of the Guess page we need to make the @target, @message and @count instance variables persistent as shown next:

persistent :target, :message, :count

Navigating back to the Guess page via the “Start Guessing” link should now show the initialized value of the message variable:

Using The Value Component With Persistent Fields

Figure 9 - Using The Value Component With Persistent Fields

The Loop Component

Next, well need to show the numbers from 1 to 10 as links for the user to pick a value. To do so we’ll use the loop component. The trellis loop component is another simple stateless component that can loop through a range defined with the tag attribute source:

        
                guess
        

Inside of the body of the loop tag the variable defines in the value is available with the current index of the loop. The page now should look like:

Trellis Hilo Guess Page

Figure 10 - Trellis Hilo Guess Page

Now that we have the numbers from 1 to 10 showing we need to turn them into hyperlinks that the user can click to make a guess.

We can use the ActionLink component (which we used earlier in the Start page) to create a hyperlink that can pass the value of the selected number. ActionLink can take an optional ‘context’ attribute that will pass a value

        
            
                guess
            
        

All of the previous uses of components have been anonymous; that is, each component tag/instance did not have an identity. In the case of the ten instances of the ActionLink component we are using for the guess page, we will make them unique. If you notice in the markup we use an attribute called “tid”; which stands for Trellis Id. The Trellis Id is used as uniqueness identifier to any component (this will become more important as we venture into using Stateful components in future installments). For stateless components it simply becomes the “id” attribute of the markup.

Guess Page with Number Links

Figure 11 - Guess Page with Number Links

If we view the source for the Guess page we see the effect of the ActionLink. Anchors with an href like “/guess/events/select.link/1” were created for each number following the pattern “/page/events/event_name.source/value” which can be read as “create an event named select coming from a source named link and pass it a value of N”. Notice in this case the source was the same for all links (simply “link”) and what made each link unique was the addition of the value portion of the URI.

<a href="/guess/events/select.link/1" id="link_1">1</a>
<a href="/guess/events/select.link/2" id="link_2">2</a>
<a href="/guess/events/select.link/3" id="link_3">3</a>
<a href="/guess/events/select.link/4" id="link_4">4</a>

With the links in place we can now try to click on one of the links and as expected Trellis will promptly show us a that is looking for an event handler named ‘on_select_from_link’ in the Guess page.

Error Page for on_select_from_link()

Figure 12 - Error Page for on_select_from_link()

Responding To User Guesses

Let’s implement the heart of the Hilo application; the event handler method to respond to user guesses. If look at Figure 1; the application page flow we see what needs to happen:

  1. If the user selects the correct value, we should navigate to the GameOver page and show them a message telling them how many guesses it took to get there.
  2. If the guessed is not the correct value, we will show them the Guess page again with a message telling them that the guess was either too high or too low.

Before we implement the event handler let’s declare the possible page transitions for the Guess page. Since the Guess page can only navigate to the GameOver page we’ll use the pages class method to inject the page.

pages :game_over

Now we can implement the on_select_from_link() event handler. First thing to notice is that the signature of the method takes the value as a parameter.

    def on_select_from_link(value)
      guess_val = value.to_i
      next_page = self
      @count = @count + 1
      if guess_val == @target 
        @game_over.count = @count
        next_page = @game_over
      else 
        @message = "Guess #{guess_val} is too #{guess_val < @target ? 'low' : 'high'}"  
      end

      next_page
    end

Inside the method we are:

  1. Turning the value into an Integer value.
  2. Default the next page to be the Guess page itself (self).
  3. Incrementing the number of guesses.
  4. If the guessed value matches the target value we’ll set the count onto the GameOver page and set the next page to navigate to the GameOver page.
  5. If the guessed value does not match the target we’ll set the appropriate message value.
  6. The return value for the method is the local variable next_page which will point to either the @game_over variable or self, navigating to the GameOver page or the Guess page respectively.

With the Guess page now finished, we can navigate back to the start page and test the application. We should be able now to guess values and have the application respond appropriately for incorrect guesses.

Guessing Incorrect Values

Figure 13 – Guessing Incorrect Values

The GameOver Page

Of course if we happen to select the correct number, the application will navigate to the stand in page for the GameOver page.

Stand In for GameOver Page

Figure 14 – Stand In for GameOver Page

To implement the GameOver page we need to be able to pass the final “count” of guesses it took to arrive at the correct answer. To accomplish this we’ll use the persistent class method and pass the symbol :count as shown below:

  class GameOver < Page
    persistent :count
  end

Now, all we need is a template (game_over.xhtml) to display the message and provide a link for the user to play again (that is navigate back to the start page)

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Game Over!</title>
    </head>
    <body>
    <h1>Game Over</h1>
    <p>You guessed the secret number in <trellis:value name="count">n</trellis:value> guesses!</p>
    <p>[<trellis:page_link tpage="start">Play Again</trellis:page_link>]</p>    
    </body>
</html>

The template uses the now familiar Trellis Value component to display the count and it uses a Trellis PageLink component to navigate back to the Start page. The PageLink component takes the name of the page in the “tpage” attribute and generates an appropriate link.

GameOver Page

Figure 15 – GameOver Page

Conclusions

In this article we have only scratched the surface of what Trellis can do. We built a simple 3-page application using only stateless core components. As we explore the more components we’ll learn how components, especially stateful components can help us mitigate the construction and maintenance of complex web applications.

We built the Hilo Web Application iteratively without ever having to restart the application. In future installments we will learn how to accomplish this in a Test-Driven Fashion (TDD) that will lead to a fully tested and more robust application in the end.

Tags , , ,

Comments are disabled