Quickstart Guide to QuarterMaster

Christopher Ross-Gill
April 21, 2007



QuarterMaster is still at an early stage of development, and therefore should be used with utmost caution.  It is, however, at a stage where testing and feedback is greatly appreciated.

1. Installation

1.1. Apache

Requires: A working version of REBOL.  REBOL/Core or even REBOL/Base will do; and Mod_Rewrite access is a must.

  1. Copy qm.r to a CGI capable folder
  2. Set permissions (on ’nix or Mac) to 755
  3. Create a folder for your application, setting permissions to 777.  It’s best if this folder is not available through the HTTPD service.  If you must, consult .htaccess documentation for methods to prevent unwanted access to this folder
  4. Point the shebang line to your REBOL binary
  5. At your site root, create (or modify) an .htaccess file to include the following:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+) /cgi-bin/qm.r [QSA,L]

That should be enough to start.

1.2. Cheyenne

Currently this does not work with the source version of Cheyenne.

  1. Copy qm.r to the root directory as defined in httpd.cfg
  2. In httpd.cfg, set your 404 handler to “/qm.r”.  If you wish QM to handle your root requests, add %qm.r at the front of your list of defaults

A sample httpd.cfg:

default [
root-dir %/path/to/www/
default [%qm.r %index.html %index.rsp]
on-status-code [
404 "/qm.r"

2. Configuration

Example of configuration settings:

config: construct [
public-key: "my-app"
private-key: "a private key - for encrypting passwords"
session-timeout: 0:02:00
zone: -6:00
default-controller: "blog"
spaces: [
"system" %/Library/WebServer/QM/Application/
"space" %/Library/WebServer/QM/Space/
"site" %/Library/WebServer/Documents/
"support" %/Library/WebServer/QM/Support/
Public Key

This is used where a unique identifier is required, but may be publicly visible (e.g. a cookie name).

Private Key

This is used to encrypt passwords, or other sensitive data.

Session Timeout

This is used to set the length of time a session is active.  For example usage, see the User Management pattern.


This is used to set the Time Zone in which the application is operational.

Default Controller

This is used to select the controller where none is specified — specifically where QM is used as the site root.


Spaces are an important concept within QM.  It provides protected access to the server filesystem, shielding against attempts to sniff directories above the allocated sandbox, use of undesirable filenames and vastly simplifies access to server resources.  A QM application requires values for:

this is where the core application components reside (should be readable)
this is where persistent data is stored (should be readable, writeable and executable)
this is the root of your site
this is where additional support modules are placed (should be readable)

Additional file spaces can be added dependent on your needs.

3. Application

An application consists of one or more of the Model, View and Controller components, contained within their relative subfolders:

+-- models/

Note: Ensure that in QM’s config, the “system” file space points to /path/to/my-application/.

3.1. Controllers

Controllers evaluate user requests, communicates with the Model, then selects the appropriate View.  The particular Controller is determined by the request URI:


This URI would invoke the ‘pages’ Controller.

+-- controllers/
+-- pages.r

A controller is a REBOL file that contains: a router and some actions.  It can also contain helper functions related to that controller:

title: "Pages Controller"
type: 'controller
default: "welcome"
event "prepare" does [
welcome-message: "Welcome!"
action "welcome" does [
render %welcome.rsp
action "hello" does [
redirect-to %/pages/welcome

Left untouched, an action automatically invokes a View with the corresponding name.  In this case, the ‘contact’ action renders %contact.rsp


Views primarily consist of RSP pages, though other pre-processors can be used.  The Views folder contains subfolders that correspond to controllers, containing files that correspond to actions:

+-- views/
+-- pages/
+-- welcome.rsp

Documenting RSP is beyond the scope of this introduction.  A sample for %welcome.rsp might be:

<p>Welcome this fine <%= form-date now "%A" %>!</p>

3.3. Models

By default, QuarterMaster uses a flat-file, single-table DBMS.  Records are manipulated using the Active Record pattern.

What it lacks in sophistication, it makes up for in flexibility.  Each record is assigned its own subdirectory that can be used to store all manner of related files.  Tables are defined in the models folder:

+-- models/
+-- pages.r

A definition includes a router (id->folder), key indices, active record methods.

title: "Pages Database"
type: 'roughcut
record: make record [
get-web-content: does [
read path/web-content.html
index: [name tags] ; indexing is not yet implemented

Once a definition exists within the Model folder, you can go straight to work:

page: select pages "welcome"
notes: select pages [where tags = "notes"] ; queries are not yet implemented
map notes func [page][page/get-web-content]
close pages

4. Key Features

4.1. Environment


File sandboxes are built into QM.  The sandbox method used by QM restricts access to predefined folders.  It will prevent access to parent folders or attempts to save files with special characters.

err404: read qm://system/views/errors/not-found.rsp
logo: read/binary qm://site/images/logo.png
Intuitive HTTP Response

Natural polymorphic publishing functions:

render "This"
render/as read/binary qm://site/images/logo.png image/png
render %welcome.rmd ; REBOL MakeDoc
render/status %errors/not-found.rsp 404
redirect-to %/this/page
redirect-to http://rebol.com
External Helpers
>> know %make-doc/scanner.r


Title: "Make-Doc Scanner"
Exports: [scan-doc]

4.2. Data Driven Helpers

Functions that assist data-driven operations:


Pagination allows you to extract a section from a data source in order to browse data in bite sizes:

>> pages: paginate wiki 1
>> ? pages
PAGES is an object of value:
last integer! 1
current integer! 1
next logic! false
previous logic! false
records block! length: 2
offset integer! 0
upper block! length: 0
lower block! length: 0
start logic! true
end logic! true

More to follow…


A variation of strfdate:

form-date now "%A %d"

Data import and validation:

import [one "1" two "2.0"][
one: integer! is less-than 2
two: decimal! three: opt integer!


[one 1 two 2.0 three none]

Quickly transform a block:

map-each [num name][1 "one" 2 "two" 3 "three"][
num: num * num name: uppercase name


[1 "ONE" 4 "TWO" 9 "THREE"]
System-Wide Link Dialect

Paths are evaluated and transformed to links:

redirect-to wiki/show/(page-name)
<%! a wiki/show/(page-name) %><%= page-name %></a>
<%! form blog/new %>