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.
Requires: A working version of Rebol. Rebol/Core or even Rebol/Base will do; and Mod_Rewrite access is a must.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+) /cgi-bin/qm.r [QSA,L]
That should be enough to start.
Currently this does not work with the source version of Cheyenne.
A sample httpd.cfg:
default [
root-dir %/path/to/www/
default [%qm.r %index.html %index.rsp]
on-status-code [
404 "/qm.r"
]
]
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/
]
]
This is used where a unique identifier is required, but may be publicly visible (e.g. a cookie name).
This is used to encrypt passwords, or other sensitive data.
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.
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:
Additional file spaces can be added dependent on your needs.
An application consists of one or more of the Model, View and Controller components, contained within their relative subfolders:
/Path/to/app/my-application/
+-- models/
views/
controllers/
Note: Ensure that in QM’s config, the “system” file space points to /path/to/my-application/.
Controllers evaluate user requests, communicates with the Model, then selects the appropriate View. The particular Controller is determined by the request URI:
http://a-site.net/pages/welcome
This URI would invoke the ‘pages’ Controller.
/Path/to/app/my-application/
+-- 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:
Rebol [
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:
/Path/to/app/my-application/
+-- views/
+-- pages/
+-- welcome.rsp
contact.rsp
Documenting RSP is beyond the scope of this introduction. A sample for %welcome.rsp might be:
<html>
<head><title>Welcome</title></head>
<body>
<h1>Welcome!</h1>
<p>Welcome this fine <%= form-date now "%A" %>!</p>
</body>
</html>
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:
/Path/to/app/my-application/
+-- models/
+-- pages.r
users.r
A definition includes a router (id->folder), key indices, active record methods.
Rebol [
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"
page/get-web-content
notes: select pages [where tags = "notes"] ; queries are not yet implemented
map notes func [page][page/get-web-content]
close pages
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
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
>> know %make-doc/scanner.r
qm://root/support/make-doc/scanner.r
Rebol [
Title: "Make-Doc Scanner"
Exports: [scan-doc]
]
...
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!
]
Yields:
[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
]
Yields:
[1 "ONE" 4 "TWO" 9 "THREE"]
Paths are evaluated and transformed to links:
redirect-to wiki/show/(page-name)
<%! a wiki/show/(page-name) %><%= page-name %></a>
<%! form blog/new %>