Web Oriented Object Framework

User Guide (Version 0.5b4)

Woof!
User Guide (Version 0.5b4)

A simple controller example

Implementing a controller involves writing the controller class and action methods corresponding to the URL's through which the client will access the application. Although these can be implemented manually, the stubs generator of wag saves us a bit of typing.

> tclsh scripts/wag.tcl stubs url /math/index /math/add
Controller MathController:
         File app/controllers/math_controller.tcl will be created.
         Class MathController will be created.
         Methods to be added: index, add.
         View stubs to be added:
                app/controllers/views/math-index-main.wtf
                app/controllers/views/math-add-main.wtf
Do you want to continue? [YN] y
Created file app/controllers/math_controller.tcl
Created class MathController.
Created action MathController.index.
Created action MathController.add.
Created view app/controllers/views/math-index-main.wtf.
Created view app/controllers/views/math-add-main.wtf.

This will create the controller MathController and its action methods add and subtract as well as corresponding views in the shown locations. Views are described in the Page generation chapter; here we will only go into details about the controller.

The controller file app/controllers/math_controller.tcl contains the following content:

oo::class create MathController {
    superclass ApplicationController
    constructor args {
        # Very important to pass arguments to parent
        next {*}$args
    }
}

oo::define MathController {
    method index {} {
        # Raise an exception that allows woofer to detect unimplemented actions
        ::woof::errors::exception WOOF NotImplemented "Action index has no supporting implementation."
    }
    method add {} {
        # Raise an exception that allows woofer to detect unimplemented actions
        ::woof::errors::exception WOOF NotImplemented "Action add has no supporting implementation."
    }
}

As shown, the file contains stubs for the controller and its action method definitions. Naturally, all these stubs could be created manually as well. Using the script only saves a bit of typing.

Writing an action method

The index method is the default method callled when a request is made that does not contain an explicit action method name for the controller. In our case, the index method does not actually need to anything since all we want to return for that URL is a Web page where the user can enter the addends. That can be done within the view template itself, so the index method can be simply be empty.

method index {} {
    # Nothing needs to be done here
}

The corresponding view template app/controllers/views/math-index-main.wtf displays a form into which the addends can be entered.

<form method='POST' action='[my url_for -action add]'>
Enter the two numbers to be added:<br/>
<input type='text' name='a'/> +
<input type='text' name='b'/>
<input type='submit' value='Calculate'/>
</form>

This template is basically an HTML fragment that displays a form. Details of how this template is converted into a Web page are in the Page generation chapter. Here the only thing to note is the value set for the action attribute in the form tag. The Tcl statement my url_for -action add generates a URL that corresponds to the add action of this controller. So when the form's submit button is clicked, the browser will submit a HTTP POST request to the URL corresponding to the add action of the math controller. It is generally advisable to use the url_for method wherever possible instead of hardcoding the link since it will generate the correct URL even when the top level domain or application root or even the controller name is modified.

Browsing to the /math/index url will bring up the following page.

Math Index page

Entering two numbers and clicking Calculate will send an HTTP POST request with the URL /math/add which will be handled by the add method, the implementation of which is shown below.

method add {} {
    pagevar set result [expr {[params get a] + [params :b]}]
}

Parameters sent in a POST request or a query string in a GET request are accessible through the params object using the standard Map interface. The code above illustrates two methods of retrieving parameter values from the object - using an explicit get method call, and retrieval using the parameter name prefixed with a : character. By convention, values used by the display template are stored in a Map object, pagevar, in the controller context. As we see below, this is not mandated.

The corresponding template for displaying the result is

<p>[pagevar hget result]</p>
<p><a href='[request referer]'>Back</a></p>

which displays the result page:

Math Result page

There are some variations in how action methods can be written. The add method above could also have been written as follows:

method add {a b} {
    my variable result
    set result "The result of $a+$b is [expr {$a + $b}]."
}

Here the parameters are directly defined as method parameters. In this case, Woof! will extract request parameters of the same name and pass them to the method. Picking one of the two styles is a matter of personal taste.

The new method definition also uses an instance variable instead of the pagevar object to pass data to the view template below.

% my variable result
<p>$result</p>
<p><a href='[request referer]'>Back</a></p>

Again, this is a matter of personal choice but use of the pagevar object avoids cluttering the controller object namespace.