Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
== Outdated egg! This is an egg for CHICKEN 4, the unsupported old release. You're almost certainly looking for [[/eggref/5/awful|the CHICKEN 5 version of this egg]], if it exists. If it does not exist, there may be equivalent functionality provided by another egg; have a look at the [[https://wiki.call-cc.org/chicken-projects/egg-index-5.html|egg index]]. Otherwise, please consider porting this egg to the current version of CHICKEN. == Awful [[toc:]] === Description awful provides an application and an extension to ease the development of web-based applications. Here's a short summary of awful features: * Straightforward interface to databases (currently supported are [[http://www.postgresql.org/|PostgreSQL]] and [[http://www.sqlite.org/|SQLite3]]) * Support for page dispatching via regular expressions * Easy access to query string and body request variables from HTTP requests * Ajax support via [[http://jquery.com/|jQuery]] * Reasonably flexible (several configuration parameters) * Compiled pages made easy * Session inspector * Web REPL * SXML support ==== Screencasts Here are some screencasts about awful: ; [[//parenteses.org/mario/awful/awful.ogv|Awful in 35s]] : a 35 seconds video showing the installation of the awful egg and the development of a "hello, world" example ; [[//parenteses.org/mario/awful/awful-guess.ogv|Number guessing game]] : a video demonstrating the development of a very simple number guessing game using ajax and the web REPL === Author [[/users/mario-domenech-goulart|Mario Domenech Goulart]] === Repository The git repository for the awful source code is hosted by GitHub: [[https://github.com/mario-goulart/awful|https://github.com/mario-goulart/awful]]. Additionally, there are some extra repositories related to awful (they are not required to use awful): * [[https://github.com/mario-goulart/awful-init-scripts|awful-init-scripts]]: init scripts for some operating systems * [[https://github.com/mario-goulart/awful-doc|awful-doc]]: the source code for this document * [[https://github.com/mario-goulart/awful-codemirror|awful-codemirror]]: the JavaScript files used by the web REPL === Requirements The following eggs are required: * [[/egg/spiffy|spiffy]] * [[/egg/spiffy-cookies|spiffy-cookies]] * [[/egg/spiffy-request-vars|spiffy-request-vars]] * [[/egg/json|json]] * [[/egg/http-session|http-session]] * [[/egg/sxml-transforms|sxml-transforms]] * [[/eggref/5/srfi-1|srfi-1]] (CHICKEN 5 only -- in CHICKEN 4 it is a core unit) * [[/eggref/5/srfi-13|srfi-13]] (CHICKEN 5 only -- in CHICKEN 4 it is a core unit) * [[/eggref/5/srfi-69|srfi-69]] (CHICKEN 5 only -- in CHICKEN 4 it is a core unit) === Components awful is composed by two parts: an application, which can be thought as a web server; and an extension, which provides most of the supported features. === A "Hello, world!" example Here's a "Hello, world!" example to be run by awful ({{hello-world.scm}}). <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () "Hello, world!")) </enscript> To run this example, execute (assuming {{awful}} is installed and its location is in your system's {{PATH}}): $ awful hello-world.scm Then access http://localhost:8080 using your favorite web browser. Without any configuration, awful listens on port 8080. Since awful uses Spiffy behind the scenes, you can configure the web server parameters using Spiffy's. {{define-page}} is the primitive procedure to define pages. In the simplest case, it takes as arguments a path to the page and a procedure to generate the page contents. The path to the page you use as the first argument is the same to be used as the path part of the URL you use to access the page. In the example we use the {{main-page-path}} parameter, which is one of the awful configuration parameters. The default is {{"/"}}. If you look at the page source code, you'll see that awful created an HTML page for you. Awful uses the [[/egg/html-utils|html-utils]]'s {{html-page}} procedure behind the scenes. You can customize the page either by passing {{html-page}}'s keyword parameters to {{define-page}} or by creating you own page definer based on {{define-page}} (see more information on how to do that in the Tips & Tricks section). You can also use some global page-related parameters if all pages use the same CSS, doctype and charset ({{page-css}}, {{page-doctype}} and {{page-charset}}, respectively). An alternative way to write Awful '''scripts''' is by using the {{#!/path/to/awful}} shebang line. Example: <enscript highlight=scheme> #! /usr/bin/awful (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () "Hello, world!")) </enscript> Then you just need to run your script (assuming the file has execution permissions): $ ./hello-world.scm === Accessing request variables Awful provides a procedure ({{$}}) to access variables from the request, both from the query string (GET method) and from the request body (e.g., POST method). Here's a modified "Hello, world!" example to greet some person instead of the whole world: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () `("Hello, " ,($ 'person "world") "!"))) </enscript> So, restart the web server to reload the code, then access the main page using an argument, represented by the {{person}} query string variable: {{http://localhost:8080/?person=Mario}}. You'll see a page showing {{Hello, Mario!}}. === Re-evaluating the code by reloading the page When we upgraded our "Hello, world!" example to the improved one which can use an argument passed through the URL, we needed to modify the code and restart the web server to reload the application code. Awful provides a way to reload the code via URL without restarting the server. To do that, we can define a special page whose handler just reloads the code: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page "/reload" (lambda () (reload-apps (awful-apps)) "Reloaded")) </enscript> and restart the awful server. Now, whenever you want to reload the application code, access {{http://localhost:8080/reload}}. You can control which IP addresses can access the reload page by using the {{page-access-control}} parameter. For example, allowing only the localhost to reload the apps: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful spiffy)) (chicken-5 (import awful spiffy))) (page-access-control (lambda (path) (if (equal? path "/reload") (member (remote-address) '("127.0.0.1")) #t))) </enscript> When used in development mode (see the {{--development-mode}} command line option for the awful application server), awful automatically defines a {{/reload}} path (available to any host) for reloading all the applications. === Using ajax Awful provides a way to (hopefully) make the use of ajax straightforward for web applications. By default, the ajax support is disabled, but it can be easily globally enabled by setting the {{enable-ajax}} parameter to {{#t}}. When you enable ajax via {{enable-ajax}}, all the pages defined via {{define-page}} will be linked to the jQuery JavaScript library. If you want just a couple of pages to have ajax support (i.e., not global ajax support), you can use the {{use-ajax}} keyword parameter for {{define-page}}, so only the pages defined with {{use-ajax: #t}} have ajax support. When you have ajax enabled and you want to disable it for specific pages, you can pass {{#t}} as the value for the {{define-page}} keyword parameter {{no-ajax}}. The URL of the jQuery file can be customized by setting the {{ajax-library}} parameter (the default is Google's API jQuery file). So, if we now change our code to <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () `("Hello, " ,($ 'person "world") "!")) use-ajax: #t) </enscript> and reload the application, we'll have our page linked to jQuery. Awful provides some procedures to do ajax. We start by the more generic one ({{ajax}}) to reply the page greetings when we click "Hello, <person>!". <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (ajax "greetings" 'greetings 'click (lambda () '(b "Hello, awful!")) target: "greetings-reply") `((a (@ (href "#") (id "greetings")) "Hello, " ,($ 'person "world") "!") (div (@ (id "greetings-reply"))))) use-ajax: #t) </enscript> The {{ajax}} procedure uses at least four arguments: 1. The URL path to the server-side handler (a string). This path is relative to {{ajax-namespace}} parameter (default is {{ajax}}. So, in the example, we'll have {{/ajax/greetings}} as the ajax path to be generated if we pass {{ajax}} as the first argument. 2. The ID of the DOM element to observe. 3. The event to be handled 4. The procedure to be run on the server-side. So, in the example, {{ajax}} will bind the fourth argument (the procedure) to the first argument (the path) on the server side. Then it will add JavaScript code to the page in order to wait for click events for the element of ID {{greetings}}. When we click "Hello, <person>!", we'll get {{Hello, awful!}} printed on the page as reply. {{ajax}} updates the DOM element whose id is the value of the {{target}} keyword parameter ({{"greetings-reply"}}, in the example). For the very specific case of creating links that execute server side code when clicked, awful provides the {{ajax-link}} procedure. So our example could be coded like: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () `(,(ajax-link "greetings" 'greetings "Hello, awful!" (lambda () '(b "Hello, awful!")) target: "greetings-reply") (div (@ (id "greetings-reply"))))) use-ajax: #t) </enscript> === Adding arbitrary JavaScript code to pages Awful provides a procedure which can be used to add arbitrary JavaScript code to the page. It's called {{add-javascript}}. Here's an example using JavaScript's {{alert}} and our "Hello, world!" example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (add-javascript "alert('Hello!');") `(,(ajax-link "greetings" 'greetings "Hello, awful!" (lambda () '(b "Hello, awful!")) target: "greetings-reply") (div (@ (id "greetings-reply"))))) use-ajax: #t) </enscript> === Database access To access databases, you need some of the awful eggs which provide database access. Currently, these are the possible options: * [[/egg/awful-postgresql|awful-postgresql]] (for PostgreSQL databases, using the [[/egg/postgresql|postgresql]] egg) * [[/egg/awful-sqlite3|awful-sqlite3]] (for SQLite3 databases, using the [[/egg/sqlite3|sqlite3]] egg) * [[/egg/awful-sql-de-lite|awful-sql-de-lite]] (for SQLite3 databases, using the [[/egg/sql-de-lite|sql-de-lite]] egg) As with ajax, database access is not enabled by default. To enable it, you need to pick one the awful database support eggs and call the {{enable-db}} procedure. Since version 0.10, and differently from the {{enable-*}} parameters, {{enable-db}} is a zero-argument procedure provided by each of awful database-support eggs. So, if you use {{awful-postgresql}}, the {{enable-db}} procedure will automatically set up awful to use a PostgreSQL database. Additionally, to access the db, you need to provide the credentials. You can provide the credentials by setting the {{db-credentials}} parameter. See the documentation for the eggs corresponding to the database type you are using ([[/egg/postgresql|postgresql]] for PostgreSQL and [[/egg/sqlite3|sqlite3]] or [[/egg/sql-de-lite|sql-de-lite]] for SQLite3.) To actually query the database, there's the {{$db}} procedure, which uses as arguments a string representing the query and, optionally, a default value (a keyword parameter) to be used in case the query doesn't return any result. {{$db}} returns a list of lists. Below is an usage example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful awful-postgresql)) (chicken-5 (import awful awful-postgresql))) (enable-db) (db-credentials '((dbname . "my-db") (user . "mario") (password . "secret") (host . "localhost"))) (define-page "db-example" (lambda () ($db "select full_name, phone from users"))) </enscript> ''Hint'': for SQLite3 databases, {{db-credentials}} should be the path to the database file. There's also the {{$db-row-obj}} procedure for when you want to access the results of a query by row name. {{$db-row-obj}} returns a procedure of two arguments: the name of the field and, optionally, a default value to be used in case the field value is {{#f}}. <enscript highlight=scheme> (define-page "db-example" (lambda () (let ((& ($db-row-obj "select full_name, phone from users where user_id=1"))) `((p "Full name: " ,(& 'full_name)) (p "Phone: " ,(& 'phone))))) </enscript> ''Warning'': currently {{$db-row-obj}} is only implemented for PostgreSQL. If you need more flexibility to query the database, you can always use the {{(db-connection}}) parameter to get the database connection object and use it with the procedures available from the your favorite database egg API. === Login pages and session Awful provides a very basic (awful?) support for creating authentication pages. The basic things you have to do is: 1. Enable the use of sessions: <enscript highlight=scheme> (enable-session #t) </enscript> 2. Set the password validation parameter. This parameter is a two argument procedure (user and password) which returns a value (usually {{#t}} or {{#f}}) indicating whether the password is valid for the given user. The default is a procedure which returns {{#f}}. Let's set it to a procedure which returns {{#t}} if the user and the password are the same: <enscript highlight=scheme> (valid-password? (lambda (user password) (equal? user password))) </enscript> 3. Define a login trampoline, which is an intermediate page accessed when redirecting from the login page to the main page. <enscript highlight=scheme> (define-login-trampoline "/login-trampoline") </enscript> 4. Create a login page. Awful provides a simple user/password login form ({{login-form}}), which we are going to use. Here's our full example so far (using the basic "Hello, world!" as main page). <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (enable-session #t) (define-login-trampoline "/login-trampoline") (valid-password? (lambda (user password) (equal? user password))) (define-page (main-page-path) (lambda () "Hello world!")) (define-page (login-page-path) (lambda () (login-form)) no-session: #t) </enscript> That's the very basic we need to set an auth page. If the password is valid for the given user, awful will perform a redirect to the main page ({{main-page-path}} parameter) passing the {{user}} variable and its value in the query string . If the password is not valid, awful will redirect to the login page ({{login-page-path}} parameter) and pass the following variables and values: ; {{reason}} : the reason why awful redirected to the login page. It may be {{invalid-password}}, for when the password is invalid for the given user; or {{invalid-session}} for when the session identifier is not valid (e.g., the session expired). ; {{attempted-page}} : the URL path to page the user tried to access, but couldn't because either he/she was not logged in or he/she provided an invalid session identifier. ; {{user}} : the user used for the form user field. Now we're gonna change our main page to store the user in the session and retrieve it to make the greetings message: <enscript highlight=scheme> (define-page (main-page-path) (lambda () ($session-set! 'user ($ 'user)) `("Hello " ,($session 'user "world") "!")) </enscript> Here we can see the two procedures to access the session: {{$session}} and {{$session-set!}}. {{$session-set!}} accepts two arguments: the first one is the name of the session variable and the second one is its value. {{$session}} takes the name of the session variable as argument and returns its session value. We can optionally use a second argument to specify a default value, in case the session variable is not bound or is {{#f}}. === Session inspector Awful provides a session inspector, so we can easily see the session contents for a given session identifier. By default, the session inspector is disabled. We can enabled it using the {{enable-session-inspector}} procedure, passing the session inspector URL path as argument: <enscript highlight=scheme> (enable-session-inspector "session-inspector") </enscript> Now, if you log in and try to access {{http://localhost:8080/session-inspector}}, you'll get ... an access denied page. Awful provides a way to control access to the session inspector ({{session-inspector-access-control}} parameter). The {{session-inspector-access-control}} parameter is a thunk which returns {{#f}} or some other value to indicate whether the access to the session inspector is allowed or not. By default, it blocks all access. Let's configure it so we can access the session inspector from the local machine (whose IP address is 127.0.0.1): <enscript highlight=scheme> (session-inspector-access-control (lambda () (member (remote-address) '("127.0.0.1")))) </enscript> Regarding to the access denied message, you can customize it by setting the {{session-inspector-access-denied-message}}. Now we can access {{http://localhost:8080/session-inspector}} and see the session contents. ''Note'': if {{enable-session-cookie}} is {{#f}}, you need to pass the session identifier in the query string (e.g., {{http://localhost:8080/session-inspector?sid=the-session-id-here}}). Here's a screenshot: [[image://parenteses.org/mario/img/awful/session-inspector.png|Awful session inspector]] When {{enable-session}} is {{#t}} and the {{--development-mode}} option is given to the awful application server, the session inspector is automatically enabled and is available from {{/session-inspector}}. === Web REPL For further run-time, server-side web hacking, awful provides a REPL that you can use via web browser. The web REPL can use either a plain HTML textarea for the input area or a more featureful editor (based on [[http://codemirror.net|codemirror]]). By default, the web REPL uses the ''fancy'' editor. The activation and control access are basically the same as for the session inspector. The relevant procedure and parameters are: * {{enable-web-repl}} * {{web-repl-access-control}} * {{web-repl-access-denied-message}} * {{enable-web-repl-fancy-editor}} * {{web-repl-fancy-editor-base-uri}} Here's a screenshot (using the fancy editor): [[image://parenteses.org/mario/img/awful/fancy-web-repl.png|Awful web REPL]] When the {{--development-mode}} option is given to the awful application server, the web REPL is automatically enabled and is available from {{/web-repl}}. The awful application server also accepts the {{--disable-web-repl-fancy-editor}} command line option to disable the web REPL fancy editor. === Pages access control To allow/deny access to pages, you can use the {{page-access-control}} parameter. It's a one-argument procedure (the page path) which can be set to determine if the access to the page is allowed or not. The example bellow shows a very silly access control to the main page: it only allows the access when the value of the request variable {{user}} is {{"mario"}}: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (enable-session #t) (define-login-trampoline "/login-trampoline") (valid-password? (lambda (user password) (equal? user password))) (page-access-control (lambda (path) (or (member path `(,(login-page-path) "/login-trampoline")) ;; allow access to login-related pages (and (equal? ($ 'user) "mario") (equal? path (main-page-path)))))) (define-page (main-page-path) (lambda () "Hello world")) (define-page (login-page-path) (lambda () (login-form)) no-session: #t) </enscript> You can customize the access denied message by setting the {{page-access-denied-message}} with a one-argument procedure (the page path). === Compiled pages Since Chicken is a compiler and our pages are Chicken code, we can compile them to have faster pages. We just need to compile our app and pass the generated {{.so}} to the {{awful}} application: $ csc -s hello-world.scm $ awful hello-world.so Notice that Chicken doesn't support reloading {{.so}} files, so, to reload your application, you'll have to restart awful. === Multiple applications support Parameters are thread-safe, but [[/egg/spiffy|Spiffy]] (the web server used by awful) doesn't guarantee that each request will be handled by a different thread, so awful has to provide a way to overcome that, otherwise hosting multiple applications under the same virtual host can be very painful, since it's not guaranteed that parameters will be reset at each request (awful does reset some critical and obviously per-request ones like {{http-request-variables}}, {{awful-response-headers}}, {{db-connection}} and {{sid}}). Awful provides the possibility of running hooks when handling requests, so you can set parameters for multiple applications in a way that they don't interfere with others. See the documentation for [[/egg/awful#add-request-handler-hook|{{add-request-handler-hook!}}]] for an example about how to do that. === The awful application server Awful consists of an application server and an extension module. The awful application server is a command line program whose usage is: $ awful --help Usage: awful [ -h | --help ] awful [ -v | --version ] awful [ <options> ] [ <app1> [ <app2> ... ] ] <options>: --development-mode Run awful in development mode. When in development mode, the web-repl, the session inspector and a special path for reload applications are automatically activated. They get bound to /web-repl, /session-inspector and /reload, respectively, and access to them is only permited from the local host. In this mode, error messages and call chains are printed to the client. Running awful with --development-mode is not recommended for applications in production. --privileged-code=<file1>[,<file2> ...] Files with code to be run with administrator privileges (e.g., setting port < 1024). --disable-web-repl-fancy-editor By default, the web-repl uses the "fancy" editor (Codemirror), with JavaScript to perform code highlight and other useful editor features. This option disables the "fancy" editor -- the web-repl will then provide a simple textarea for editing code. --ip-address=<ip address> Bind the web server to the given IP address. --port=<port number> Make the web server listen to the given port number. {{<app1> <app2> ...}} are files containing code to be loaded by the awful application server. {{--ip-address}} can be used to bind the web server to the given IP address. {{--port}} can be used to make the web server listen to the given port. {{--ip-address}} and {{--port}} take precedence over the Spiffy parameters to specify the server IP address ({{server-bind-address}}) and port ({{server-port}}). The {{--development-mode}} option is intended to be used during the development of your web application. It's not recommended to run awful with {{--development-mode}} in production. The development mode enables the web REPL and the session inspector (when {{enable-session}} is {{#t}}) for the localhost, prints error messages and backtraces to the client (e.g., web browser) and HTTP server debugging messages to the {{current-error-port}}. It also makes the {{/reload}} path available for reloading awful applications. When in development mode, the web REPL and the session inspector are available from the {{/web-repl}} and {{/session-inspector}} paths. If you enable the development mode you can still use {{enable-web-repl}} and {{enable-session-inspector}} to customize their respective paths and access control procedures (although the development mode always allows access to web REPL and session inspector for the localhost). The {{--disable-web-repl-fancy-editor}} command line option disables the web REPL fancy editor. The {{--privileged-code}} command line options specify code to be run in privileged mode, for the cases when you specify a user/group to run the server ({{spiffy-user}}/{{spiffy-group}}). In this case, you usually run awful as a user with administrator privileges (e.g., {{root}} in Unix[-like] systems) and spiffy switches to the specified user/group. To make things clearer, let's take a look at awful's workflow: | v load privileged code | v listen | v switch user/group | v load applications | v accept | v As you can see, the privileged code is loaded and evaluated '''before''' switching user/group. So, if you run awful with administrator privileges, the privileged code will the loaded with administrator privileges. Running code with administrator privileges is usually not recommended, but it is necessary if you want, for example, to specify the user and group to switch to before accepting connections. So, the {{--privileged-code}} command line option should be used strictly to specify code which must be run with administrator privileges (like specifying server user/group and [[/egg/awful-ssl|enabling SSL]], for example). The privileged code is '''not''' reloaded when you reload applications via a defined reload page. === Deploying Awful Here are some tips about how to deploy awful on your system. ==== On Unix[-like] systems Create a user for running awful. For example, on GNU/Linux systems, you can use the adduser program: # adduser --system --no-create-home --disabled-password --disabled-login --group awful This will create a user and a group called {{awful}}. Create a directory for the awful configuration file: # mkdir /etc/awful Create a directory for log files: # mkdir /var/log/awful Create a directory for static files: # mkdir /var/www/awful You may also want to create a directory for your awful applications: # mkdir /var/lib/awful Create configuration files for awful. In the privileged configuration file ({{privileged.conf}} below) you can set all the configuration parameters for awful and spiffy which require administrator privileges. In another file ({{awful.conf}} below) you can put whatever code which does not need administrator privileges. Here's some example configuration for awful listening on port 80 (strictly speaking, [[/egg/spiffy|spiffy]] -- the actual web server -- listens on this port): ===== privileged.conf <enscript highlight=scheme> ;; -*- scheme -*- (cond-expand (chicken-4 (use awful spiffy)) (chicken-5 (import awful spiffy))) (spiffy-user "awful") (server-port 80) (root-path "/var/www/awful") (debug-log "/var/log/awful/debug.log") (error-log "/var/log/awful/error.log") </enscript> ===== awful.conf <enscript highlight=scheme> (load "/var/lib/awful/my-app") ;; if you don't use file suffixes, Chicken will ;; pick .so files when you compile your apps </enscript> Set the permissions for the configuration, log, static files and awful applications directories: # chown -R awful:awful /etc/awful /var/log/awful /var/www/awful /var/lib/awful Set up an init script for your system. Basically, it should call /usr/bin/awful --privileged-code=/etc/awful/privileged.conf /etc/awful/awful.conf &> /var/log/awful/init.log & If awful doesn't start, take a look at {{/var/log/awful/init.log}} You can find an [[https://raw.github.com/mario-goulart/awful-init-scripts/master/debian|init script]] for [[http://www.debian.org|Debian]] systems (or derivatives, like [[http://www.ubuntulinux.com|Ubuntu]]) at the [[https://github.com/mario-goulart/awful-init-scripts|awful-init-scripts repository]]. Here are the steps to install it: # cd /etc/init.d # wget https://raw.github.com/mario-goulart/awful-init-scripts/master/debian -O awful # chmod 755 awful # update-rc.d awful defaults There's also an [[https://raw.github.com/mario-goulart/awful-init-scripts/master/gentoo|init script for Gentoo]] (by [[/users/david-krentzlin|David Krentzlin]]). === Awful & SSL To make awful serve pages using SSL, see the documentation for the [[/egg/awful-ssl|awful-ssl]] egg. === List of user configurable parameters ==== SXML (parameters) ===== {{enable-sxml}} <parameter>(enable-sxml [boolean])</parameter> Deprecated. Since version 1.0.0, this parameter has no effect. It is going to be removed in future versions. ===== {{sxml->html}} <parameter>(sxml->html [procedure])</parameter> The procedure used to transform SXML into HTML. The default value is: <enscript highlight=scheme> (let ((rules `((literal *preorder* . ,(lambda (t b) b)) . ,universal-conversion-rules*))) (lambda (sxml) (with-output-to-string (lambda () (SRV:send-reply (pre-post-order* sxml rules)))))) </enscript> This parameter has been introduced in awful 0.36. ==== Debugging (parameters) ===== {{debug-file}} <parameter>(debug-file [file path])</parameter> If {{#f}}, indicates that debugging should be disabled. When set to a string, it should be the path to the file where the debug messages go (when {{debug}} or {{debug-pp}} is used.) The default value is {{#f}}. ===== {{debug-db-query?}} <parameter>(debug-db-query? [boolean])</parameter> When not {{#f}}, all queries passed to {{$db}} and to {{$db-row-obj}} are printed to the debug file. The default value is {{#f}}. ===== {{debug-db-query-prefix}} <parameter>(debug-db-query-prefix [string])</parameter> Prefix to be used for queries debugging when {{debug-db-query}} is not {{#f}}. The default value is {{""}}. ===== {{debug-resources}} <parameter>(debug-resources [boolean])</parameter> When {{#t}}, enables debugging of awful's resources table (an alist mapping paths (or regexes) and vhost paths to their corresponding procedures to be executed on the server side upon request). The debugging data is sent to the file pointed by {{debug-file}}. The default value is {{#f}}. ==== Database (parameters) ===== {{db-credentials}} <parameter>(db-credentials [boolean or list])</parameter> Credentials to be used to access the database (see the documentation for the egg corresponding to the database backend you selected.) When {{#f}}, no database access is performed. The default value is {{#f}}. ==== Ajax (parameters) ===== {{ajax-library}} <parameter>(ajax-library [string])</parameter> URL or path to the ajax library (currently only [[http://jquery.com|jQuery]] is supported.) The default value is {{"http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"}} ===== {{enable-ajax}} <parameter>(enable-ajax [boolean])</parameter> When {{#t}}, makes {{define-page}} link the {{ajax-library}} to the generated page. Its effect is global, that is, once {{enable-ajax}} is set to {{#t}}, all pages defined via {{define-page}} will be linked to the ajax library, unless when the {{no-ajax}} keyword parameter is explicitly set. The default value is {{#f}}. ===== {{ajax-namespace}} <parameter>(ajax-namespace [string])</parameter> Name to be used as a namespace for ajax URL paths. The default value is {{"ajax"}}. ===== {{ajax-invalid-session-message}} <parameter>(ajax-invalid-session-message [string])</parameter> The message to be used when attempting the make an ajax call using an invalid session identifier. The default value is {{"Invalid session"}}. ==== Sessions (parameters) ===== {{enable-session}} <parameter>(enable-session [boolean])</parameter> When {{#t}}, session support is enabled. The default value is {{#f}}. ===== {{enable-session-cookie}} <parameter>(enable-session-cookie [boolean])</parameter> When {{#t}}, awful uses cookies to store the session identifier. Otherwise, the session identifier is passed as a value in the query string or in the request body. The default value is {{#t}}. ===== {{session-cookie-name}} <parameter>(session-cookie-name [string])</parameter> The name of the cookie for storing the session identifier. The default value is {{"awful-cookie"}}. ===== {{session-cookie-setter}} <parameter>session-cookie-setter</parameter> A one-argument procedure (the sid) that is called when creating/refreshing the session. The default value is the following procedure: <enscript highlight=scheme> (lambda (sid) (set-cookie! (session-cookie-name) sid)) </enscript> ==== Access control (parameters) ===== {{page-access-control}} <parameter>(page-access-control [procedure])</parameter> A one-argument (URL path of the current page) procedure which tells whether the access to the page is allowed or not. The default value is {{(lambda (path) #t)}}. ===== {{page-access-denied-message}} <parameter>(page-access-denied-message [procedure])</parameter> A one-argument (URL path of the current page) procedure which returns the access denied message in SXML. The default value is {{(lambda (path) '(h3 "Access denied."))}}. ===== {{valid-password?}} <parameter>(valid-password? [procedure])</parameter> A two-argument (user and password) procedure which indicates whether the given password is valid for the given user. The default value is {{(lambda (user password) #f)}}. ==== Pages (parameters) ===== {{page-doctype}} <parameter>(page-doctype [string])</parameter> The doctype (see the [[/egg/doctype|doctype]] egg) to be applied to all pages defined by {{define-page}}. It can be overwritten by {{define-page}}'s {{doctype}} keyword parameter. The default value is {{""}}. ===== {{page-css}} <parameter>(page-css [boolean or string])</parameter> The CSS file to be linked by all pages defined by {{define-page}}. It can be overwritten by {{define-page}}'s {{css}} keyword parameter. See [[/egg/html-utils|html-utils]]'s {{html-page}} procedure to know about the {{css}} keyword parameter syntax. The default value is {{#f}} (no CSS). ===== {{page-charset}} <parameter>(page-charset [boolean or string])</parameter> The page charset to be used by all pages defined by {{define-page}}. It can be overwritten by {{define-page}}'s {{charset}} keyword parameter. The default value is {{#f}} (no explicit charset). ===== {{page-template}} <parameter>(page-template [procedure])</parameter> A one-mandatory-argument procedure to be used by {{define-page}} (unless {{define-page}}'s {{no-template}} keyword parameter is set to {{#f}}) to generate HTML pages. Although the procedure yielded by this parameter takes only one mandatory argument, the following keyword arguments are passed: * css * title * doctype * headers * charset * literal-style? The default value is {{html-page}} (see the [[/egg/html-utils|html-utils]] egg documentation.) ===== {{page-exception-message}} <parameter>(page-exception-message [procedure])</parameter> A one-argument procedure to be used when an exception occurs while {{define-page}} tries to evaluate its contents. The default value is {{(lambda (exn) '(h3 "An error has occurred while processing your request."))}} ==== Page paths (parameters) ===== {{main-page-path}} <parameter>(main-page-path [string])</parameter> The URL path to the app main page. The default value is {{"/"}}. ===== {{app-root-path}} <parameter>(app-root-path [string])</parameter> The base path to be used by the application. All the pages defined by {{define-page}} will use {{app-root-path}} as the base directory. For example, if {{app-root-path}} is set to {{"/my-app"}} and {{"my-page"}} is used as first argument to {{define-page}}, the page would be available at {{http://<server>:<port>/my-app/my-page}}. The default value is {{"/"}}. '''Important''': this parameter is evaluated both at page definition time and page request handling time. ===== {{login-page-path}} <parameter>(login-page-path [string])</parameter> The URL path for the login page. When creating a login page, be sure to set the {{no-session}} keyword parameter for {{define-page}} to {{#t}}, otherwise you'll get an endless loop. The default value is {{"/login"}}. ==== Headers (parameters) ===== {{awful-response-headers}} <parameter>(awful-response-headers [alist])</parameter> An alist to specify the headers to be used in the response. If the {{content-length}} header is not provided, awful will calculate it automatically. Here's an example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (enable-sxml #t) (define (define-json path body) (define-page path (lambda () (awful-response-headers '((content-type "application/json"))) (body)) no-template: #t)) (define-json (main-page-path) (lambda () '(literal "{\"a\": 1}"))) </enscript> ==== Web REPL (parameters) ===== {{web-repl-access-control}} <parameter>(web-repl-access-control [procedure])</parameter> A no-argument procedure to control access to the web REPL. The default value is {{(lambda () #f)}}. ===== {{web-repl-access-denied-message}} <parameter>(web-repl-access-denied-message [SXML])</parameter> Message to be printed when the access to the web REPL is denied. The default value is {{'(h3 "Access denied.")}}. ===== {{enable-web-repl-fancy-editor}} <parameter>(enable-web-repl-fancy-editor [boolean])</parameter> Indicates whether the web REPL should use a fancier editor for the input area. The editor is based on [[http://codemirror.net|codemirror]]. Without the fancy editor, the input area is a simple HTML textarea. The default value for {{use-fancy-editor}} is {{#t}}. ===== {{web-repl-fancy-editor-base-uri}} <parameter>(web-repl-fancy-editor-base-uri [string])</parameter> The URI which indicates the fancy editor source files (JavaScript and CSS) location. The default value is {{http://parenteses.org/awful/codemirror}}. ==== Session inspector (parameters) ===== {{session-inspector-access-control}} <parameter>(session-inspector-access-control [procedure])</parameter> A no-argument procedure to control access to the session inspector. The default value is {{(lambda () #f)}}. ===== {{session-inspector-access-denied-message}} <parameter>(session-inspector-access-denied-message [SXML])</parameter> Message to be printed when the access to the session inspector is denied. The default value is {{'(h3 "Access denied.")}}. ==== JavaScript (parameters) ===== {{enable-javascript-compression}} <parameter>(enable-javascript-compression [boolean])</parameter> Enable JavaScript compression support. When enabled the compressor set by {{javascript-compressor}} is used. The default value is {{#f}}. ===== {{javascript-compressor}} <parameter>(javascript-compressor [procedure])</parameter> A one-argument procedure which receives JavaScript code and returns it compressed. Only used when {{enable-javascript-compression}} is not {{#f}}. The default value is the {{identity}} procedure. A possible value for {{javascript-compressor}} is {{jsmin-string}} (see the [[/egg/jsmin|jsmin]] egg.) ===== {{javascript-position}} <parameter>(javascript-position [symbol])</parameter> A symbol indicating the position of JavaScript code in the generated pages. Possible values are {{top}} (in the page headers) and {{bottom}} (right before {{</body>}}). The default value is {{top}}. ===== {{literal-script/style?}} <parameter>(literal-script/style? [boolean])</parameter> When {{#f}} (the default value), convert special HTML characters in JavaScript and in CSS code to their corresponding HTML entities. When non-{{#f}}, leave the special HTML characters untouched. This parameter was introduced in version 0.39. === List of read-only parameters available to users Note: these parameters should not be explicitly set and when their use is needed, it's a string sign you're doing something you shouldn't (except for {{db-connection}}, which can be used by procedures from the [[/egg/postgresql|postgresql]] egg API). ===== {{http-request-variables}} <parameter>(http-request-variables)</parameter> The per-request value returned by [[/egg/spiffy-request-vars|spiffy-request-vars]]'s {{request-vars}}. ===== {{db-connection}} <parameter>(db-connection)</parameter> A per-request database connection object. The connection is automatically opened and closed by awful in a per-request basis (unless databases are not being used or the {{no-db}} keyword parameter for {{define-page}} is {{#t}}.) ===== {{page-javascript}} <parameter>(page-javascript)</parameter> JavaScript code to be added to the pages defined by {{define-page}}. ===== {{sid}} <parameter>(sid)</parameter> The session identifier. ===== {{awful-apps}} <parameter>(awful-apps)</parameter> The list of awful applications, as given to the awful server when invoked from the command line. ===== {{development-mode?}} <parameter>(development-mode?)</parameter> Indicates whether awful is running in development mode (see the {{--development-mode}} command line option for the awful application server). === List of procedures and macros ==== Miscellaneous ===== {{awful-version}} <procedure>(awful-version)</procedure> Return the awful version (a string). ===== {{awful-resources-table}} Return the resources table used by awful. The resources table is represented by a hash table whose format is described below: ; keys: lists in the following format: {{(path-matcher vhost-root-path method)}}. {{path-matcher}} is the first argument given to {{define-page}} and can be either a string, a regular expression object or a procedure. {{vhost-root-path}} is the documents root path for the virtual host and is a string. {{method}}, a symbol, is the HTTP method bound to the resource. ; values: procedures representing the page handlers (the second argument given to {{define-page}}). ==== JavaScript ===== {{include-javascript}} <procedure>(include-javascript . files)</procedure> A shortcut to {{(<script> type: "text/javascript" src: file)}} for each file in {{files}}. ===== {{add-javascript}} <procedure>(add-javascript . code)</procedure> Add arbitrary JavaScript code to the pages defined by {{define-page}} and {{define-session-page}}. Note that the {{literal-script/style?}} parameter may affect special characters in JavaScript code. ==== CSS ===== {{add-css}} <procedure>(add-css . code)</procedure> Add arbitrary CSS code to the pages defined by {{define-page}} and {{define-session-page}}. Note that the {{literal-script/style?}} parameter may affect special characters in CSS code. This procedure was introduced in version 0.39. ==== Debugging ===== {{debug}} <procedure>(debug . args)</procedure> Print {{args}}, concatenated, to the file {{debug-file}}. ===== {{debug-pp}} <procedure>(debug-pp arg)</procedure> Pretty-print {{arg}} to the file {{debug-file}}. ==== Sessions and authentication ===== {{$session}} <procedure>($session var #!optional default)</procedure> Return the value of {{var}} in the session (or {{default}} if {{var}} does not exist or is {{#f}}). ===== {{$session-set!}} <procedure>($session-set! var #!optional val)</procedure> If {{var}} is a quoted symbol, set the value of {{var}} to {{val}}. If {{val}} is not provided, {{var}} will have its value set to {{#f}}. {{var}} can be an alist mapping session variable names to their corresponding values. Examples: <enscript highlight=scheme> ($session-set! 'foo "foo value") ($session-set! '((foo . "foo value") (bar . "bar value") (baz . "baz value"))) </enscript> ===== {{link}} <procedure>(link url text . rest)</procedure> Return a session-aware HTML code for a link, using the {{<a>}} procedure from [[/egg/html-tags|html-tags]]. The {{rest}} arguments are the same as the ones for the {{<a>}} procedure from [[/egg/html-tags|html-tags]], plus the following: ; {{no-session}} : a boolean. If {{#t}}, forces {{link}} to ignore the session even when {{enable-session}} is {{#t}}. ; {{arguments}} : an alist mapping variable names to their corresponding values, to be passed to uri-common's {{form-urlencode}} procedure. ; {{separator}} : the value to the {{separator}} keyword argument to be passed to to uri-common's {{form-urlencode}} procedure. When {{enable-session}} is {{#t}}, {{link}} automatically encodes the session identifier in the URI (unless {{no-session}} is {{#t}}). ===== {{form}} <procedure>(form contents . rest)</procedure> Return a session-aware HTML code for a form, using the {{<form>}} procedure from [[/egg/html-tags|html-tags]]. The {{rest}} arguments are the same as the ones for the {{<form>}} procedure from [[/egg/html-tags|html-tags]], plus {{no-session}}, a boolean. If {{no-session}} is {{#t}}, it forces {{form}} to ignore the session even when {{enable-session}} is {{#t}}. When {{enable-session}} is {{#t}}, {{form}} automatically generates a hidden input field to pass the session identifier (unless {{no-session}} is {{#t}}). ===== {{define-login-trampoline}} <procedure>(define-login-trampoline path #!key vhost-root-path hook)</procedure> Define a trampoline -- an intermediate page accessed when redirecting from the login page to the main page. {{vhost-root-path}} is the path to the virtual host the trampoline will be defined for. {{hook}} is a one-argument procedure which receives the given user. It is called right before the redirection to the attempted path (if any was attempted) or {{(main-page-path)}}. ===== {{login-form}} <procedure>(login-form #!key (user-label "User: ") (password-label "Password: ") (submit-label "Submit") (refill-user #t) (trampoline-path "/login-trampoline"))</procedure> Return a user/password login form (e.g., for using in authentication pages). When the {{refill-user}} is {{#t}}, the User field is refilled with the value from the {{user}} query string value when either the session or the password is invalid. The {{user-label}}, {{password-label}} and {{submit-label}} keyword parameters are labels to be used for the user, password and submit form widgets, respectively. {{trampoline-path}} is a string that specify the path the form will be submitted to. It must match the path given as argument to {{define-login-trampoline}}. ==== Request variables and values ===== {{$}} <procedure>($ var #!optional default/converter)</procedure> Return the HTTP request value for the given variable {{var}}. The variable is looked for in both the query string (GET method) and request body (e.g., POST method). See the documentation for the procedure returned by [[/eggref/4/spiffy-request-vars|spiffy-request-vars]]'s {{request-vars}} for further details. ===== {{with-request-variables}} <macro>(with-request-variables (var1 var2 ... varn) expression1 ...)</macro> A wrapper around [[/egg/spiffy-request-vars|spiffy-request-vars]]'s {{with-request-vars*}}. All the [[/egg/spiffy-request-vars|spiffy-request-vars]]'s converter procedures are exported, for convenience. ==== Database access ===== {{$db}} <procedure>($db q #!key (default '()) values)</procedure> Execute the given query ({{q}}) on the database and return the result as a list of lists or {{default}} if the result set is empty. The {{values}} keyword parameter (a list) is a list of values to replace the placeholders in the query. Example: <enscript highlight=scheme> ($db "insert into foo (bar, baz) values (?, ?)" values: '("bar-val" "baz-val")) </enscript> ''Note'': up to version 0.40.0, {{$db}} returned {{#f}} when queries returned empty set of results. Starting from version 0.41.0, {{$db}} returns {{'()}} queries return empty set of results. If you use the [[/egg/awful-ssql|awful-ssql]] egg, you can give {{$db}} the query as a SSQL expression. See the documentation for [[/egg/awful-ssql|awful-ssql]] for more details. Support for SSQL has been introduced in awful version 0.40.0. Note that {{$db}} is supposed to be used in the context of request handlers (i.e., in the body of a lambda bound to a uri path by {{define-page}}). If you want to use {{$db}} out of the context of request handlers, you'll have to manage database connections yourself. Basically, you have to parameterize {{db-connection}} with the actual connection object. Example: <enscript highlight=scheme> (parameterize ((db-connection (open-database (db-credentials)))) ($db the-query) (close-database (db-connection))) </enscript> ===== {{$db-row-obj}} <procedure>($db-row-obj q)</procedure> Execute the given query {{q}} on the database and return an one-argument procedure which takes as argument the name of the database field to get the value. Example: <enscript highlight=scheme> (let ((& ($db-row-obj "select full_name, phone from users where user_id=1"))) (<p> "Full name: " (& 'full_name)) (<p> "Phone: " (& 'phone))) </enscript> ''Warning'': currently {{$db-row-obj}} is only implemented for PostgreSQL databases. If you use the [[/egg/awful-ssql|awful-ssql]] egg, you can give {{$db}} the query as a SSQL expression. See the documentation for [[/egg/awful-ssql|awful-ssql]] for more details. Support for SSQL has been introduced in awful version 0.40.0. ==== Pages ===== {{define-page}} <procedure>(define-page path-matcher handler #!key css title doctype headers charset no-ajax use-ajax no-template no-session no-db no-javascript-compression method use-sxml vhost-root-path strict)</procedure> Define an awful page. {{path-matcher}} matches requested paths. It can be represented by three types: ; string: a literal path. When the path matcher is a string, the handler procedure must be a thunk. ; regex: a regular expression to match the requested path (regexes created by both regex egg and irregex unit are valid values). When the path matcher is a regular expression, the page handler procedure must be a one-argument procedure which will be given the requested path. ; procedure: a one-argument procedure to match the requested path (feature introduced in awful 0.35). If the procedure matches the requested path, it should return a list to be applied to the page handler. When the path matcher is a procedure, the arity of the page handler procedure must match the length of the list returned by the path matcher. {{#f}} indicates that there was no match. Values which are not {{#f}} or a list will cause an invalid argument type error. {{handler}} should return either a string, a no-argument procedure or a list (in case SXML mode is enabled -- SXML support has been introduced in awful 0.36). If it returns a string, it will be given as argument to {{(page-template)}}, unless {{no-template}} is not false. If {{handler}} returns a procedure (feature introduced in awful 0.35), awful won't do anything besides evaluating the returned procedure. It can be useful, for example, to serve static files out of the web server document directory (see the examples in this section). If {{handler}} produces a list, awful or the particular page in question must be operating in SXML mode, which can be set via the {{enable-sxml}} parameter or via de {{use-sxml}} keywork parameter for {{define-page}}. The list produced by the handler will be given as argument to the procedure yield by the {{sxml->html}} parameter. {{use-sxml}} (boolean): specifies whether awful should assume that the handler produces SXML code or strings. In case the handler produces SXML code, the parameter {{sxml->html}} yields the procedure used to generate HTML out of SXML. {{method}} (a symbol or a list) indicates the HTTP method to be used (e.g., {{GET}}, {{POST}}, {{PUT}}). {{method}} can also be a list of methods. In this case, awful will define a page for each method of the list. Methods are case-insensitive. Pages that use different methods can use the same path. The default value is {{(GET HEAD)}} (prior to version 0.39, the default value was just {{GET}}). The {{css}}, {{title}}, {{doctype}}, {{headers}} and {{charset}} keyword parameters have the same meaning as {{html-page}} (from the [[/egg/html-utils|html-utils]] egg). If {{no-ajax}} is {{#t}}, it means that the page won't use ajax, even if the {{enable-ajax}} parameter is {{#t}}. If {{use-ajax}} is {{#t}}, it means that the page will be linked to the ajax library, even if the {{enable-ajax}} parameter is {{#f}}. If {{no-template}} is {{#t}}, it means that no page template (see the {{page-template}} parameter) should be used. If {{no-session}} is {{#t}}, it means that the page should not use session. If {{no-db}} is {{#t}}, it means that the page should not use the database, even when database usage is activated by {{enable-db}} and {{db-credentials}} is not {{#f}}. If {{no-javascript-compression}} is {{#t}} the JavaScript code for the page is not compressed, even when {{enable-javascript-compression}} is not {{#f}}. {{vhost-root-path}} (a string or {{#f}}) is the root path of the virtual host the page definition is to be applied to. If {{vhost-root-path}} is set to a path, the page definition will only be valid for the virtual host whose root path is the given path. If {{strict}} is truthy, awful will only match the requested path if it strictly matches the defined matchers. For convenience, awful sloppily considered requests for files and directories to be equivalent. For example, if a page was defined with a {{"/foo"}} matcher, a request for {{"/foo/"}} would be handled by {{"/foo"}}'s handler. That's not always desired. For example, consider this case: (define-page (irregex "/[^/]*") identity) We want to specify that, e.g., requests for {{/foo}} should be handled, but requests for {{/foo/}} should ''not'' be handled. However, since awful tries to find a handler by stripping the trailing slash, a request for {{/foo/}} would actually be handled by the handler for {{(irregex "/[^/]*")}}, even if the regex specified that paths with a trailing slash should not match. Examples: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful srfi-1 ;; for filter-map srfi-13 ;; for string-prefix? irregex ;; for irregex spiffy)) ;; for send-static-file (chicken-5 (import (chicken irregex) ;; for irregex (chicken format) ;; for sprintf (chicken string)) ;; for string-split (import awful srfi-1 ;; for filter-map srfi-13 ;; for string-prefix? spiffy)) ;; for send-static-file ) ;; ;; Path matcher as a string ;; ;; http://host:port/foo => "bar" (define-page "/foo" (lambda () "bar")) ;; ;; Path matcher as a regular expression ;; ;; http://host:port/add/1/2/3 => 6 (define-page (irregex "/add/.*") (lambda (path) (let ((numbers (filter-map string->number (string-split path "/")))) `(,(apply + numbers))))) ;; ;; Path matchers as procedures ;; (define (ticket-id path) (and (string-prefix? "/ticket/" path) (and-let* ((tokens (string-split path "/")) (_ (not (null? (cdr tokens)))) (id (string->number (cadr tokens)))) (and id (list id))))) (define (ticket-reporter+severity path) (and (string-prefix? "/ticket/" path) (and-let* ((tokens (string-split path "/")) (_ (> (length tokens) 2))) (list (cadr tokens) (caddr tokens))))) ;; http://host:port/ticket/4 (define-page ticket-reporter+severity (lambda (reporter severity) `(,(sprintf "Reporter=~a, severity=~a" reporter severity)))) ;; http://host:port/ticket/4/5 (define-page ticket-id (lambda (id) `("This is ticket " ,id))) ;; ;; Page handler returning a procedure ;; ;; Serving a static file out of the web server documents directory (define-page "/my-dot-emacs" (lambda () (lambda () (parameterize ((root-path (get-environment-variable "HOME"))) (send-static-file ".emacs")))) no-template: #t) </enscript> ===== {{define-session-page}} <procedure>(define-session-page path-matcher handlerr . rest)</procedure> Define a session-aware page. When the page is accessed and a corresponding session does not exist, it is created. If the session already exists and is not valid, it is recreated. If the session already exists and is valid, then it is refreshed. The {{rest}} parameters are the same as for {{define-page}}. Here's an example (the [[http://www.paulgraham.com/arcchallenge.html|arc challenge]]): <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-session-page "said" (lambda () (with-request-variables (said) (cond (said ($session-set! 'said said) `(,(link "said" "click here"))) (($session 'said) => (lambda (said) `("You said: " ,said))) (else `(,(form '((input (@ (type "text") (name "said"))) (input (@ (type "submit")))) action: "said" method: 'post)))))) method: '(GET POST)) </enscript> ===== {{undefine-page}} <procedure>(undefine-page path #!optional vhost-root-path)</procedure> Undefine a page whose path is {{path}} (a string or a regular expression object). The optional parameter {{vhost-root-path}} is the path of virtual host where the page is to be undefined. If omitted, {{(root-path)}} is used. ===== {{set-page-title!}} <procedure>(set-page-title! title)</procedure> Set the title for the requested page. This procedure is useful to set the page title dynamically from the page handler (instead of statically, via {{define-page}}'s {{title}} keyword parameter). This procedure has been introduced in awful 0.35. Example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (set-page-title! "a title") "Hi!")) </enscript> ===== {{html-page}} <procedure>(html-page contents #!key css title doctype headers charset content-type literal-style? (html-attribs '()) (body-attribs '()))</procedure> Generates an SXML page containing {{contents}} (SXML). The following keywords arguments may be used to customize the page: * {{headers}}: an SXML form containing additional headers to be inserted in the section delimited by the {{<head>}} tag. Default = {{#f}}. * {{title}}: the title for the page (to be used in the {{<title>}} tag). Default = {{#f}}. * {{css}}: may be either a path to a Cascading Style Sheet file, to be linked from the generated page (the default value is {{#f}}, so no CSS is used) or a list of paths to CSS files. If a list of paths is used, the elements which are also lists are read and inlined into the generated page. Example: {{css: '("css1.css" ("css2.css"))}}. In the example, {{css1.css}} would be linked from the generated page (using the link tag) and {{css2.css}} would be inlined into the generated page (e.g., {{html-page}} would read the {{css2.css}} file and inline its contents in the HTML code). * {{doctype}}: specifies the document type of the generated page. The default value is {{#f}}. The possible values are the ones available from the [[doctype]] egg. * {{charset}}: specifies the default charset to be used in the corresponding meta tag of the document. The default value is {{"UTF-8"}} (only when {{content-type}} is provided). * {{literal-style?}}: if {{#f}}, convert special characters in style code (CSS) to theyr equivalent HTML entities. If non-{{#f}}, insert them verbatim. * {{content-type}} and {{charset}} are used together for the {{<meta http-equiv ...>}} specification.: {{charset}}'s default value is {{"UTF-8"}} and {{content-type}}'s default value is {{"application/xhtml+xml"}}. * {{html-attribs}}: attributes to the {{html}} tag. The format is a list of lists {{(<attribute> <value>)}} ({{<attribute>}} is a symbol). Example: {{(html-page "foo" html-attribs: '((lang "us")))}}. * {{body-attribs}}: attributes to the {{body}} tag. The format is a list of lists {{(<attribute> <value>)}} ({{<attribute>}} is a symbol). Example: {{(html-page "foo" body-attribs: '((bgcolor "red")))}}. ==== Ajax ===== {{ajax}} <procedure>(ajax path selector event proc #!key target (action 'html) (method 'POST) (arguments '()) success no-session no-db vhost-root-path live on prelude update-targets cache error-handler use-sxml strict)</procedure> Generate JavaScript code to be added to the page defined by {{define-page}}. Return the generated JavaScript code. Unless {{no-page-javascript}} is a truthy value, the JavaScript code will be added to the page in which case the returned JavaScript code is usually not useful and can be discarded. {{path}} is the URL path (a string) of the server side handler. This path is placed under the {{(app-root-path)/(ajax-namespace)}} path. So, if your {{app-root-path}} is {{"my-app"}}, your {{ajax-namespace}} is {{"ajax"}} and you use {{"do-something"}} as the first argument to {{ajax}}, the URL for the server side handler would be {{"/my-app/ajax/do-something"}}. {{selector}} is the selector for the DOM element to be observed. If it is a quoted symbol, awful generates a jQuery selector by DOM id (e.g., {{'my-selector}} generates {{"#my-selector"}}). If it is a string, awful uses it as-is to generate the jQuery selector (e.g., {{"input[name^=omg]"}} generates {{"input[name^=omg]"}}). {{event}} (a quoted symbol or a list) is the event(s) to be observed. If it is a quoted symbol (e.g., {{'click}}), only this event will be bound. If {{event}} is a list of events, all the events from the list will be bound. {{proc}} is a no-argument procedure to be executed on the server side. The {{target}} keyword parameter is the id of the DOM element to be affected by the result of {{proc}}. The {{method}} (a quoted symbol, usually {{'GET}} or {{'POST}}) keyword parameter is the HTTP method to be used by the ajax request. The {{arguments}} keyword parameter is an alist mapping request variables (symbols) to their values (strings). {{ajax}} uses these arguments to assembly the query string or the request body to send to the server when performing the ajax request. If {{use-sxml}} is {{#t}}, it specifies that the ajax handler produces SXML code instead of strings. Example: <enscript highlight=scheme> arguments: '((var1 . "$('#var1').val()") (var2 . "$('#var2').val()")) </enscript> If the {{no-session}} keyword parameter is {{#t}}, it means that no session should be considered ({{ajax}} implicit sends the session identifier when {{no-session}} is {{#f}}). If the {{no-db}} keyword parameter is {{#t}}, it means that the should be no attempt to connect the database, even when database usage is activated by {{enable-db}} and {{db-credentials}} is not {{#f}}. The {{vhost-root-path}} keyword parameter (a string) is the vhost root path. It is useful for explicitly separate pages defined using the same path (see {{define-page}}) but for different vhosts. The {{on}} keyword parameter (boolean) indicates whether ajax should use jQuery's {{on}} method (see [[http://api.jquery.com/on/]]). If {{on}} gets bound to {{#t}}, awful will generate code to start delegating events from the {{document}} DOM element. If {{on}} gets bound to a symbol, awful will generate code to start delegating events from DOM element whose id is the given symbol. If {{on}} gets bound to a string, awful will generate code to start delegating events from DOM element which matches the given string representing a selector. The {{on}} keyword parameter was introduced in awful 0.39.2. The {{live}} keyword parameter (boolean) indicates whether ajax should use jQuery's {{live}} method (see [[http://api.jquery.com/live/]]). Note that the {{live}} method was deprecated in jQuery 1.7 and removed in version 1.9. If you are using a more recent version of jQuery, see the {{on}} keyword parameter for {{ajax}} (introduced in awful 0.39.2). The {{prelude}} keyword parameter (string) is an arbitrary piece of JavaScript code to be placed right before the ajax request. The {{update-targets}} keyword parameter a boolean indicating whether multiple targets should be updated upon ajax response. When {{update-targets}} is used, the procedure {{proc}} used as argument to {{ajax}} should yield an alist as result. The alist maps DOM elements identifiers to their corresponding values. Here's an example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (ajax "foo" 'foo 'click (lambda () '((a . 1) (b . 2) (c . 3))) update-targets: #t) `(,(link "#" "Click me" id: "foo") (div (@ (id "a"))) (div (@ (id "b"))) (div (@ (id "c"))))) use-ajax: #t) </enscript> The {{success}} keyword parameter (string) can be any arbitrary JavaScript code to be executed on the successful ajax request. The JavaScript code can assume that a variable {{response}} is bound and contains the request resulting data. Here's an example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (ajax "foo" 'foo "click" (lambda () "hey") success: "$('#bar').html(response + ' you!')") `(,(link "#" "Click me" id: "foo") (div (@ (id "bar"))))) use-ajax: #t) </enscript> When {{success}} is used together with {{target}} or {{update-targets}}, the success code runs before the update code. This allows to update parts of the dom that cannot be identified by id (e.g., attributes), post-process the response before targets are updated, or skip the update altogether by performing a return from the callback function. Here's an example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (ajax "foo" 'foo 'click (lambda () '((lang . "en") (a . 1) (b . 2) (c . 3))) update-targets: #t success: "document.documentElement.lang = response['lang'];") `(,(link "#" "Click me" id: "foo") (span (@ (id "lang") (display "none"))) (div (@ (id "a"))) (div (@ (id "b"))) (div (@ (id "c"))))) use-ajax: #t) </enscript> The {{cache}} keyword parameter (boolean), if set to {{#f}}, it will force requested pages not to be cached by the browser. The default value is not set, leaving it to be set by jQuery. See [[http://api.jquery.com/jQuery.ajax/|jQuery's documentation]] for further details. The {{error-handler}} keyword parameter expects a JavaScript callback to be used as the error handler for the Ajax request. See the {{error}} attribute for the {{settings}} object given as argument to jQuery.ajax ([[http://api.jquery.com/jQuery.ajax/|http://api.jquery.com/jQuery.ajax/]]). The {{no-page-javascript}} keyword parameter is a boolean that determines whether the generated JavaScript code is automatically added to the page or not. Defaults to {{#f}}. This parameter may be useful if you want more control of where or when the generated JavaScript code gets added to the page or even if it gets added at all. The {{ajax}} procedure is session, HTTP request and database -aware. ===== {{periodical-ajax}} <procedure>(periodical-ajax path interval proc #!key target (action 'html) (method 'POST) (arguments '()) success no-session no-db vhost-root-path live on prelude update-targets error-handler use-sxml strict)</procedure> Periodically execute {{proc}} on the server side, using {{(app-root-path)/(ajax-namespace)/path}} as the URL path for the server side handler. {{interval}} (a number) is the interval between consecutive executions of {{proc}}, in milliseconds. The meaning of the keyword parameters is the same as for {{ajax}}'s. ===== {{ajax-link}} <procedure>(ajax-link path id text proc #!key target (action 'html) (method 'POST) (arguments '()) success no-session no-db (event 'click) vhost-root-path live on class hreflang type rel rev charset coords shape accesskey tabindex a-target prelude update-targets error-handler use-sxml strict)</procedure> A shortcut to <enscript highlight=scheme> (begin (ajax path id 'click proc ...) (<a> href: "#" [...other <a> keyword parameters...] id: id text)) </enscript> The meaning of the {{target}}, {{action}}, {{method}}, {{arguments}}, {{success}}, {{no-session}}, {{no-db}}, {{event}}, {{vhost-root-path}}, {{update-targets}} {{live}} and {{on}} keyword parameters is the same as for {{ajax}}'s. The meaning of the {{class}}, {{hreflang}}, {{type}}, {{rel}}, {{rev}}, {{charset}}, {{coords}}, {{shape}}, {{accesskey}}, {{tabindex}} and {{a-target}} are the same as for [[/egg/html-tags|html-tags]]' {{<a>}} procedure (except that {{a-target}} is {{<a>}}'s {{target}}, since {{ajax}} uses the {{target}} keyword parameter). The {{event}} keyword parameter syntax is the same for {{ajax}}'s {{event}} mandatory parameter. ==== Redirection ===== {{redirect-to}} <procedure>(redirect-to uri)</procedure> Perform an HTTP redirection (code 302) to the given {{uri}} (either a string or a [[/eggref/4/uri-common|uri-common]] URI object). To be used from {{define-page}} handler. Example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) ;; / -> /foo (define-page "/" (lambda () (redirect-to "/foo"))) (define-page "/foo" (lambda () "foo")) </enscript> The example above shows a redirection from {{/}} to {{/foo}}. Redirections can also be performed when the origin path is a regular expression: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) ;; /bar.* -> /foo (define-page (regexp "/bar.*") (lambda (_) (redirect-to "/foo"))) (define-page "/foo" (lambda () "foo")) </enscript> ==== Request handler hooks Awful provides the possibility of plugin hooks to the request handler, so that deploying multiple awful applications under the same virtual host is possible and easy. ===== {{add-request-handler-hook!}} <procedure>(add-request-handler-hook! hook-id proc)</procedure> Adds a hook identified by {{id}} (can be used to remove the hook if necessary). {{proc}} is a two-argument procedure which receives the requested path and its handler. Here's a simple usage example: <enscript highlight=scheme> (add-request-handler-hook! 'foo (lambda (path handler) (when (string-prefix? "/foo" path) (parameterize ((debug-file "/tmp/foo-debug") (enable-ajax #t)) (handler)))) </enscript> By using request handlers, you can parameterize parameter values in a way that they don't affect other applications. ===== {{remove-request-handler-hook!}} <procedure>(remove-request-handler-hook! hook-id)</procedure> Removes the request handler hook identified by {{id}}. ==== Web REPL ===== {{enable-web-repl}} <procedure>(enable-web-repl path #!key css title)</procedure> Enable the web REPL. {{path}} is the URL path to the web REPL. The keyword parameter {{css}} is the CSS to be used the the web REPL page (see the documentation for {{html-page}}, from the [[/egg/html-utils|html-utils]] egg, for the {{css}} keyword parameter.) The keyword parameter {{title}} (a string) is the title for the web REPL page (see the documentation for {{html-page}}, from the [[/egg/html-utils|html-utils]] egg, for the {{title}} keyword parameter.) The web REPL is automatically enabled by the awful application server when the {{--development-mode}} is provided (available from {{/web-repl}}). By default, the fancy editor is used, but can be disabled with the {{--disable-web-repl-fancy-editor}} command line option for the awful application server. ==== Session inspector ===== {{enable-session-inspector}} <procedure>(enable-session-inspector path #!key css title)</procedure> Enable the session inspector. {{path}} is the URL path to the session inspector. The keyword parameter {{css}} is the CSS to be used the the session inspector page (see the documentation for {{html-page}}, from the [[/egg/html-utils|html-utils]] egg, for the {{css}} keyword parameter.) The keyword parameter {{title}} (a string) is the title for the session inspector page (see the documentation for {{html-page}}, from the [[/egg/html-utils|html-utils]] egg, for the {{title}} keyword parameter.) The session inspector is automatically enabled by the awful application server when the {{--development-mode}} is provided (available from {{/session-inspector}}). ==== Applications ===== {{load-apps}} <procedure>(load-apps apps)</procedure> Load the given applications ({{apps}} - a list) (using [[/man/4/Unit eval#load|load]]). ===== {{reload-apps}} <procedure>(reload-apps apps)</procedure> The same as {{load-apps}} but also reseting the resources table (the thing that maps URIs to procedures) before loading applications. ==== The awful server ===== {{awful-start}} <procedure>(awful-start thunk #!key dev-mode? port ip-address (use-fancy-web-repl? #t) privileged-code))</procedure> Starts awful. This procedure is only useful for standalone applications which intent to embed awful. For example, the awful application server (the awful command line tool) uses it. This procedure does all the listening, switching user/group and entering the accept loop dance. {{thunk}} is a procedure to be executed upon starting awful. It can be Scheme code just like any other that can be loaded as an awful application. {{dev-mode?}} (boolean) indicates whether awful should run in development mode. {{port}} (integer) indicates to port to bind to. {{ip-address}} (string) indicates the IP address to bind to. {{use-fancy-web-repl?}} (boolean): indicates whether the web REPL should use the fancy editor. The default value is {{#t}} (since awful 0.40.0 -- before that it was {{#f}}) {{privileged-code}} (procedure): a thunk that is executed while awful is still running with privileged permissions (when run by the superuser). === List of macros ==== Applications definition ===== define-app <syntax>(define-app id matcher: matcher [ parameters: parameters | handler-hook: handler-hook ] body ...)</syntax> This macro can be used to define an awful application. It's a wrapper around {{add-request-handler-hook!}} and should be used when you have multiple awful applications running under the same server, so parameters set for one application don't interfere with other applications' parameters. The idea is that the application's matcher controls when the given parameters or handler-hook will be applied to take effect over the given body expressions. {{id}} (a symbol) is an identifier for the application. {{matcher}} is a Scheme object that is matched against requested paths. When there is a match, the given parameters or handler-hook are bound/evaluated during the evaluation of {{body}}. The following types are supported: ; lists of strings : lists of strings can be used to represent paths to be matched against requested paths. For example, if the matcher is {{'("/" "/foo")}}, it'll match requests for {{"/foo"}} and {{"/foo/"}}. ; one-argument procedures : if the matcher is a one-argument procedure, it will be given the requested path as argument. If it returns a non-{{#f}} value, it indicates a match. ; a regular expression object : if the matcher is a regular expression object (e.g., {{(regexp "/foo.*")}}), awful will try to match the requested path against the given regex. {{parameters}} and {{handler-hook}} are mutually exclusive and optional. {{parameters}} is a list of parameter bindings (same syntax as the list given as first argument to {{parameterize}}) that are bound when body is evaluated. {{handler-hook}} is a one-argument procedure which is given the request handler continuation. It is more flexible than {{parameters}}, since it can do anything before {{body}} is evaluated, including setting parameters (e.g., using {{parameterize}}). Notice that {{matcher}}, {{parameters}} and {{handler-hook}} are not keyword parameters, they are part of {{define-app}}'s syntax. {{body}} represents the expressions to be evaluated when matcher returns a truthy value. Notice that {{define-app}}'s matcher does not affect the '''definition''' of pages -- they are defined just as if they were defined outside {{define-app}}'s body. Here are some examples, assuming we have three applications, foo, bar and baz, running on the same awful server. The way we "separate" applications is by checking the path prefix, so foo is available from {{/foo}}, bar from {{/bar}} and baz from {{/baz}}. Other criteria can be used, but for simplicity, we'll just use path prefixes. <enscript highlight=scheme> (cond-expand (chicken-4 (use awful awful-sql-de-lite srfi-13)) (chicken-5 (import awful awful-sql-de-lite srfi-13))) ;;; ;;; App foo ;;; (define-app foo matcher: (lambda (path) (string-prefix? "/foo" path)) parameters: ((enable-ajax #t) (ajax-library "/js/foo.js")) (define-page "/foo/baz" (lambda () '(p "baz from foo"))) (define-page "/foo/quux" (lambda () '(p "quux from foo"))) ) ;;; ;;; App bar ;;; (define-app bar matcher: (lambda (path) (string-prefix? "/bar" path)) handler-hook: (lambda (handler) (switch-to-sql-de-lite-database) (parameterize ((enable-ajax #t) (ajax-library "/js/bar.js") (db-credentials "/var/bar/bar.db")) (handler))) (define-page "/bar/baz" (lambda () '(p "baz from bar"))) (define-page "/bar/quux" (lambda () '(p "quux from bar"))) ) ;;; ;;; App baz ;;; (define-app baz matcher: (lambda (path) (string-prefix? "/baz" path)) (define-page "/baz/foo" (lambda () '("foo from baz"))) (define-page "/baz/bar" (lambda () '("bar from baz"))) ) </enscript> === Tips and tricks ==== Reload applications' code on every request You can use {{add-request-handler-hook!}} to reload the applications' code every time a request is handled by awful. Here's a simple example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (add-request-handler-hook! 'reload-on-request (lambda (path handler) (reload-apps (awful-apps)) (handler))) (define-page (main-page-path) (lambda () '("1"))) </enscript> ==== Use {{link}} and {{form}} for links and forms Instead of using {{<a>}} and {{<form>}} for creating links and forms, respectively, consider using the {{link}} and {{form}} procedures instead. They are specially useful when using sessions, since they transparently handle the session identifier for you. Even if you don't use sessions, they may save you some work if one day you decide to use sessions (then you won't have do to anything regarding to links and forms). ==== Use {{with-request-variables}} when referencing the same request variable multiple times When you need to access the same request variable more than once, consider using {{with-request-variables}}. For example, instead of: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-session-page "save-and-show-user" (lambda () ($session-set! 'user ($ 'user)) `("Welcome " ,($ 'user "nobody") "!"))) </enscript> consider using something like: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-session-page "save-and-show-user" (lambda () (with-request-variables (user) ($session-set! 'user user) `("Welcome " ,(or user "nobody") "!")))) </enscript> ==== Use the web REPL and the session inspector for debugging You can simply use the {{--development-mode}} option for the awful application server to enable the web REPL and the session inspector (when {{enable-session}} is {{#t}}). The development mode allows access to them for the localhost. When in development mode, the web REPL and the session inspector are available at the {{/web-repl}} and {{/session-inspector}} paths. If you want further flexibility, you can customize the web REPL and the session inspector. Here's a simple recipe to allow access for your local machine to the web REPL ({{/repl}}) and to the session inspector ({{/session-inspector}}). <enscript highlight=scheme> (session-inspector-access-control (lambda () (member (remote-address) '("127.0.0.1")))) (enable-session-inspector "/session-inspector") (web-repl-access-control (lambda () (member (remote-address) '("127.0.0.1")))) (enable-web-repl "/repl") </enscript> To access them, just point your browser to {{http://localhost:<port>/repl}} and {{http://localhost:<port>/session-inspector}}, respectively. ==== Create custom page definers when {{page-template}} and/or plain {{define-page}} are not enough You can define your own page definers when {{page-template}} or the plain {{define-page}} is not enough for what you need. Here's an example: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define (define-custom-page path content) (define-page path (lambda () `(html (body ,(content)))) no-template: #t)) (define-custom-page (main-page-path) (lambda () '("Hey!"))) </enscript> If you access {{http://localhost:8080}} you'll get the following HTML code: <html> <body>Hey!</body></html> ==== Debugging: error messages on the browser window Error messages right on the browser window can be quite handy for debugging (although not for production environments). Here's a way to accomplish that: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import (chicken base) (chicken condition) (chicken port)) (import awful))) (page-exception-message (lambda (exn) `(pre ,(with-output-to-string (lambda () (print-call-chain) (print-error-message exn)))))) (define-page (main-page-path) (lambda () `("Boom!" ,(+ 1 "foo")))) </enscript> This feature is automatically enabled when the awful application server is used with the {{--development-mode}} option. ==== Run awful without arguments to quickly share a file When invoked without arguments, awful (the application) starts the web server using the current directory as root path and keeps listening on port 8080. So, if you want to quickly share a file (or some files), change to the directory containing the files and execute {{awful}}. The access {{http://<host>:8080/<the-file-you-want>}}. ==== Reloading awful from Emacs Here's a quick hack to reload awful apps from Emacs. It can be handy when you are developing using awful in development mode ({{--development-mode}}, or when you defined your own reload path). <enscript highlight=elisp> (defun awful-reload () (interactive) (shell-command "lynx -dump http://localhost:8080/reload")) (add-hook 'scheme-mode-hook #'(lambda () (local-set-key "\C-cR" 'awful-reload))) </enscript> The code above defines an {{awful-reload}} procedure, which requests the {{/reload}} path (automatically defined by awful when running in development mode) using {{lynx}}. You can use whatever command line HTTP client you want. {{lynx}} is handy because of the {{-dump}} option, which can be readly displayed by Emacs in the shell command output buffer. Here's a screenshot: [[image://parenteses.org/mario/img/awful/awful-reload-emacs.png|Reloading awful apps from Emacs]] ==== Performance tweaks * if you don't intend to use static index files, you may configure the [[/egg/spiffy|Spiffy]] {{index-files}} parameter to {{'()}}. That will disable the lookup for static index files (or you can configure it to contain only the static index files you use). * awful usually performs better with a larger stack size (1MB is a reasonable value). You can set the runtime stack size with the {{-:s}} command line option (see [[/manual/Using the compiler#runtime-options|the CHICKEN manual page]] for other options): $ awful -:s1m my-app.so ==== Binding URI paths as arguments to page handler procedures <enscript highlight=scheme> (cond-expand (chicken-4 (use awful irregex srfi-13)) (chicken-5 (import (chicken string)) (import awful irregex srfi-13))) (define (define-page* page-path content) (define-page (irregex (string-append page-path "/*.*")) (lambda (path) (let ((url-args (string-split ;; Removing `page-path' from the requested path (string-drop path (string-length page-path)) "/"))) (condition-case (apply content url-args) ((exn arity) "invalid arity") (exn () (signal exn))))))) ;;; ;;; /page1 ignores all arguments ;;; ;; http://<server>:<port>/page1 => "no args" ;; http://<server>:<port>/page1/1 => "no args" ;; http://<server>:<port>/page1/1/2 => "no args" (define-page* "/page1" (lambda _ '("no args"))) ;;; ;;; /page2 requires exactly one argument ;;; ;; http://<server>:<port>/page2 => "invalid arity" ;; http://<server>:<port>/page2/1 => "foo=1" ;; http://<server>:<port>/page1/1/2 => "invalid arity" (define-page* "/page2" (lambda (foo) '("foo=" foo))) ;;; ;;; /page3 requires one argument (the other is optional). Ignores the ;;; rest. ;;; ;; http://<server>:<port>/page3 => "invalid arity" ;; http://<server>:<port>/page3/1 => "foo=1, bar=#f" ;; http://<server>:<port>/page1/1/2 => "foo=1, bar=2" ;; http://<server>:<port>/page1/1/2/3 => "foo=1, bar=2" (define-page* "/page3" (lambda (foo #!optional bar) '("foo=" foo ", bar=" bar))) ;;; ;;; /page4: all arguments are optional (get bound if given). ;;; ;; http://<server>:<port>/page4 => "foo=#f, bar=#f" ;; http://<server>:<port>/page4/1 => "foo=1, bar=#f" ;; http://<server>:<port>/page4/1/2 => "foo=1, bar=2" ;; http://<server>:<port>/page4/1/2/3 => "foo=1, bar=2" (define-page* "/page4" (lambda (#!optional foo bar) '("foo=" foo ", bar=" bar))) </enscript> ==== Awful badge Here's a suggestion: [[image://parenteses.org/mario/img/thats-awful.png|That's awful!]] === Examples Here are some simple examples using assorted awful features. ==== Example with AngularJS Assuming you have [[https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js|angular.min.js]] in the {{root-path}} (the current directory, by default) and the code below is in a file {{angular-app.scm}}: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define-page (main-page-path) (lambda () (html-page `(div (@ (class "container")) "Name: " (input (@ (type "text") (ng-model "name"))) "{{ name }}") html-attribs: '((ng-app)) doctype: "<!DOCTYPE html>" headers: (include-javascript "/angular.min.js"))) no-template: #t) </enscript> Alternatively, you can set the {{page-template}} parameter: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) ;; Configure page-template to set ng-app as attribute for the html tag (page-template (lambda (content . args) (apply html-page (append (list content html-attribs: '((ng-app))) args)))) (define-page (main-page-path) (lambda () `((div (@ (class "container")) "Name: " (input (@ (type "text") (ng-model "name"))) "{{ name }}"))) doctype: "<!DOCTYPE html>" headers: (include-javascript "/angular.min.js")) </enscript> To run the example, execute: $ awful angular-app.scm and use your favorite web browser to access {{http://localhost:8080/}}. ==== Number guessing game A screencast showing this example is available at [[http://parenteses.org/mario/awful/awful-guess.ogv|http://parenteses.org/mario/awful/awful-guess.ogv]] This examples shows a simple guessing game. A random number is generated on the server side and the user tries to guess it. It shows some basic ajax features. <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define (prompt-guess) '(input (@ (type "text") (id "guess")))) (define-page (main-page-path) (lambda () (ajax "try-guess" 'guess 'change (lambda () (let ((guess ($ 'guess as-number)) (thinking (random 10))) `(p ,(if (and guess (= guess thinking)) "You guessed it right!" `("You guessed it wrong. I'm thinking " ,thinking "."))))) target: "verdict" arguments: '((guess . "$('#guess').val()"))) `((p "Guess the number I'm thinking:") ,(prompt-guess) (div (@ (id "verdict"))))) use-ajax: #t) </enscript> To run it, execute: $ awful number-guess.scm (considering you saved the code above in a file called {{number-guess.scm}}), then access {{http://localhost:8080}}. ==== Sandboxed Chicken Web REPL It shows how to implement a very simple web-based Chicken REPL using a sandbox environment (see the [[/egg/sandbox|sandbox egg]] documentation) for safe evaluation. The idea is to have a web page with an input box. Users type the forms they want to evaluate and submit them to the server. The server evaluates the given forms in a sandbox environment and return the results. Here's the commented code: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful sandbox)) (chicken-5 (import (chicken base) (chicken condition) (chicken port)) (import awful sandbox))) (enable-sxml #t) ;; Here we define the REPL page. It uses the session to store the ;; sandboxed environment. By default, the `main-page-path' parameter ;; value is "/". (define-session-page (main-page-path) (lambda () ;; Create the sandbox environment (if it does not exist yet) and ;; store it in the user session. (unless ($session 'sandbox-env) ($session-set! 'sandbox-env (make-safe-environment parent: default-safe-environment mutable: #t extendable: #t))) ;; Here we set an ajax handler for the REPL expressions ;; submission. When users change the REPL input widget (i.e., by ;; pressing ENTER), the contents of the text input field are ;; submitted and handled by the procedure given as the fourth ;; argument to `ajax'. (ajax "eval" 'repl-input 'change (lambda () ;; This binds the variable `repl-input' from the POST ;; method the the `repl-input' Scheme variable (let ((repl-input ($ 'repl-input))) ;; We'd better handle exceptions when trying to ;; evaluate the expressions given by users. (handle-exceptions exn ;; If something goes wrong, we print the error message ;; and the call chain. `(pre ,(with-output-to-string (lambda () (print-error-message exn) (print-call-chain)))) ;; Here we try to evaluate the given expression in the ;; sandboxed environment stored in the user session. ;; The `repl-output' page div is updated with the result. `(pre ,(safe-eval (with-input-from-string repl-input read) fuel: 100 allocation-limit: 100 environment: ($session 'sandbox-env)))))) ;; Here we pass the contents of the text input to the ajax ;; handler. The default HTTP method used by `ajax' is POST. arguments: `((repl-input . "$('#repl-input').val()")) ;; The output of the ajax handler updates the `repl-output' ;; page div. target: "repl-output") ;; Here's what is displayed to users `((h1 "Sandboxed Chicken web REPL") (input (@ (type "text") (id "repl-input"))) (div (@ (id "repl-output"))))) ;; This tells `define-session-page' to link the page to jQuery use-ajax: #t) </enscript> To run the code above you'll need to install awful and sandbox: $ chicken-install awful sandbox Then (considering you save the code above in a file called {{web-sandbox.scm}}), run: $ awful web-sandbox.scm and access {{http://localhost:8080}}. Here are some screenshots of the code above running on Firefox: [[image://gazette.call-cc.org/img/10/web-sandbox.png|Screenshot of the sandboxed web REPL running on Firefox]] If you try something nasty, the sandbox will abort the evaluation and you'll get an error message and the call chain: [[image://gazette.call-cc.org/img/10/web-sandbox-loop.png|Screenshot of the sandboxed web REPL running on Firefox]] We can also compile the web application: $ csc -s web-sandbox.scm $ awful web-sandbox.so ==== Color chooser Here's an example provided by [[/users/christian-kellermann|Christian Kellermann]] demonstrating an ajax-based color chooser: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful)) (chicken-5 (import awful))) (define color-table '("f63353" "fead76" "107279" "10fabc" "1181bf" "120902" "129105" "131848" "13a04b" "1427ee" "14a8b1" "1532d4" "15bcf7" "16671a" "16c13d" "175b60" "17d583" "186fa6" "18ecc9" "1973ec")) (define (color-picker counter color) `(div (@ (class "color-box") (style ,(string-append "background-color: #" color))) (input (@ (type "text") (id ,(conc "change-color-" counter)) (value ,color))))) (define (make-color-chooser counter c) (ajax "ajax" (conc "#change-color-" counter) 'change (lambda () (let ((color (or ($ 'color) c)) (counter ($ 'counter))) (color-picker counter color))) target: (conc "color-box-" counter) arguments: `((color . ,(conc "$('#change-color-" counter "').val()")) (counter . ,counter)) live: #t) `(div (@ (id ,(conc "color-box-" counter))) ,(color-picker counter c))) (define-page (main-page-path) (lambda () (map (let ((counter -1)) (lambda (c) (set! counter (add1 counter)) (make-color-chooser counter c))) color-table)) use-ajax: #t) </enscript> Here's a screenshot: [[image://parenteses.org/mario/img/awful/color-chooser.png|Awful color chooser]] To run this example (considering you save the code above in a file called {{color-chooser.scm}}): $ awful color-chooser.scm then access {{http://localhost:8080}}. You can change the colors by editing the input boxes then pressing enter. ==== Fortune server Note: this example assumes CHICKEN 4. This example is a fortune server in awful. It demonstrates some handy awful features like database access and ajax. You'll need the [[/egg/awful-sql-de-lite|awful-sql-de-lite]] egg and its dependencies (which should be automatically installed by {{chicken-install}}). Here are instructions to install and use it: 1. Install [[/egg/awful-sql-de-lite|awful-sql-de-lite]] $ chicken-install awful-sql-de-lite 2. Create the fortunes database (see the code below): $ csi -s create-database.scm 3. Run the fortune server (see the code below): $ awful fortune-server.scm Here's the code for {{create-database.scm}} which creates and populates the fortune database: <enscript highlight=scheme> (use sql-de-lite posix) (define fortunes '(("Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." "Brian Kernighan") ("In order to understand recursion, one must first understand recursion.") ("If debugging is the process of removing software bugs, then programming must be the process of putting them in." "Edsger Dijkstra") ("Controlling complexity is the essence of computer programming." "Brian Kernigan") ("The function of good software is to make the complex appear to be simple." "Grady Booch") ("That's the thing about people who think they hate computers. What they really hate is lousy programmers." "Larry Niven") ("First learn computer science and all the theory. Next develop a programming style. Then forget all that and just hack." "George Carrette") ("To iterate is human, to recurse divine." "L. Peter Deutsch") ("The best thing about a boolean is even if you are wrong, you are only off by a bit.") ("Optimism is an occupational hazard of programming; feedback is the treatment." "Kent Beck") ("Simplicity is prerequisite for reliability." "Edsger W. Dijkstra") ("Simplicity is the ultimate sophistication." "Leonardo da Vinci") ("The unavoidable price of reliability is simplicity." "C.A.R. Hoare") ("The ability to simplify means to eliminate the unnecessary so that the necessary may speak." "Hans Hoffmann") ("Simplicity is hard to build, easy to use, and hard to charge for. Complexity is easy to build, hard to use, and easy to charge for." "Chris Sacca"))) (delete-file* "fortunes.db") (let ((db (open-database "fortunes.db"))) (exec (sql db "create table fortunes(sentence text, author text)")) (for-each (lambda (fortune) (let* ((sentence (car fortune)) (author (cdr fortune)) (statement (string-append "insert into fortunes (sentence, author) values (?,?)"))) (exec (sql db statement) sentence (if (null? author) "" (car author))))) fortunes) (close-database db)) </enscript> Here's the code for the fortune server: <enscript highlight=scheme> (cond-expand (chicken-4 (use awful awful-sql-de-lite)) (chicken-5 (import awful awful-sql-de-lite))) (enable-sxml #t) (enable-db) (db-credentials "fortunes.db") (define (random-fortune) (car ($db "select sentence, author from fortunes order by random() limit 1"))) (define-page (main-page-path) (lambda () (ajax "new-fortune" 'new-fortune 'click (lambda () (let ((fortune (random-fortune))) `((sentence . ,(car fortune)) (author . ,(cadr fortune))))) update-targets: #t) `(div (@ (id "content")) (div (@ (id "sentence")) "Click the button below to get a new fortune") (div (@ (id "author"))) (button (@ (id "new-fortune")) "New fortune"))) css: "fortune.css" use-ajax: #t charset: "utf-8") </enscript> The contents of the {{fortune.css}} file are: <enscript highlight=css> body { font-family: arial, verdana, sans-serif; } #sentence { width: 20em; background-color: #DEE7EC; padding: 6px; min-height: 7em; } #author { width: 20em; min-height: 2em; padding: 6px; background-color: #eee; } </enscript> Here's a screenshot: [[image://parenteses.org/mario/img/awful/fortune-server.png|Awful fortune server]] ==== I-wonder I-wonder is a simple awful example application which uses SQLite as storage (with the [[/egg/sql-de-lite|sql-de-lite]] egg). You can find the code for that application on GitHub: [[https://github.com/mario-goulart/i-wonder|https://github.com/mario-goulart/i-wonder]] Here's a screenshot [[image://parenteses.org/mario/img/awful/i-wonder.png|I-wonder]] === The name Awful doesn't mean anything special. It's just awful. But folks on freenode's #chicken (IRC) have suggested some acronym expansions: * A Whole Freaking Universe of Lambdas * Authored Without Full Understanding of Logic * Another Web Framework Understating Logic * All Worthless Frameworks Unchain Laziness * Armed With Flimsy Utility Lisp * Awful Will Fed Up Lispers * Awful Wildly Finalizes Unfinished Loops * Another Widely Foolish Unknown Language * Ain't Work For Unpleasant Laywers * Aliens Would Find it Utterly Lame * Aren't We Funny and Uncaring Lurkers * Awful Will F*** Up Logic * Awful Will Fart Upon Leaving * Again Web Frameworks Underscore Lameness * Attention While Fully Utilizing Laziness * Another Webserver F***ing Up the Line * Again We Forget Unclosed Lambdas * All Web Features Understood Losslessly * Anything With Fully Universal Lambdas * Again We Fail Under Load * Apocalyptic Warthogs Find Undiscovered Lands * Accessible Web Framework Using Lisp * Another Weird Framework Using Lisp * All Waffles Fear Unicorn Landings * A Working Facility Underscoring Laziness * Another Webbot Flapping Under Lines * Anybody Will Fake Unrealistic Loveletters * Armadillos Would First Use Legs * Astonishing Whales Fill Up Lakes * Alternative Way to F*** Up Lisp * Another Way to Find Undressed Ladies * Amazing! Wonderful! Fantastic! Unbelievable! Lame. * All Wonders Feel Useless Later * Amazingly Wonderful Feeling, Using Lambdas * Alligators Will Fear Us, Lunatics * All Wussies Fear Ultimate Lambda * Animals Will Find Us Letal * Advanced Web Framework: Ultimate Lucubration * Awful Will Feed Urban Lethargy * Argument With Focus Upon Labelling * Another Word Faking Unpremeditated Label * Again We Find it Utterly Useless * Ain't Work For Unattractive Ladies * A Way For Using Lambdas * Awful Way For Using Lambdas * Apparently We Freaks Understand Lambdas * Again We Foolishly Use Lists * At Work Fools Use Lisp * Again We Foolishly Use Lisp * Another Wimp Fall Upon Lisp * Accepting Whatever Fools Undertake Lightly * Absurd Word For Unnatural Lingo * Alternative Word For Useless Loser * Acronym With Filled Up Letters * Acronym We Find Utterly Lame * Another Webserver Functioning Until Launched * Applications With Familiar, Understandable Language * (Awful Words Forming Useless List) * All Who Force Unusual Layout * Again We Fear Undescribable Lamenting * Awful Will Favour Ugly Layouts * Apes With Frequent Uncontrolled Lunacy ==== Acknowledgements (IRC nicknames on freenode's #chicken): * C-Keen * DerGuteMoritz * elderK * elf * florz * merlincorey * sjamaan === FAQ (aka Fakely Asked Questions) ==== How does awful bind URIs to files/directories on the filesystem and procedures? Explanation by example: When a procedure is bound to a path {{a}} (like {{(define-page "a" (lambda ...))}}) and the request is made to {{a/}}, the awful behavior is the following: * if the path {{a}} does not exist in the filesystem (under the {{root-path}}), the server replies with the result of the evaluation of the procedure bound to {{a}}. * if {{a}} is an existent file, the server replies with the file contents (or the result of processing the file, in case it is a special one like [[/egg/spiffy#web-scheme-handler|web-scheme]] or {{.ssp}} files) * if {{a}} is an existing directory and the directory contains one of the files in {{index-file}} (see documentation for [[/egg/spiffy|Spiffy]]), with the index file contents (or the result of processing the file, in case it is a special one like [[/egg/spiffy#web-scheme-handler|web-scheme]]'s or {{.ssp}}) * if {{a}} is an existing directory and does not contain index files, the server replies with the result of the evaluation of the procedure bound to {{a}}. === Known bugs and limitations * Awful currently doesn't detect if cookies are enabled on the client side. * Reloading of compiled applications is not supported (i.e., you have defined a page to reload your compiled applications). See [[http://bugs.call-cc.org/ticket/442|ticket #442]]. * When {{define-page}}'s {{no-template}} is not false, {{add-javascript}} and ajax-related features for {{define-page}} won't be effective for the page in question. * If you encounter bugs, please report them to the author. === License Copyright (c) 2010-2018, Mario Domenech Goulart All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. === Version history ===== version 1.0.3 (2023-04-10) * Automatically reject (i.e., respond 404) to dangerous paths. Dangerous paths are those that could cause a path traversal issue. * ajax: allow using success with target/update-targets (contributed by Pietro Cerutti) ===== version 1.0.2 (2020-08-03) * Fix typo in SXML attribute affecting JavaScript code in web-repl ===== version 1.0.1 * Fix handling of the {{--ip-address}} command line option (thanks to Vasilij Schneidermann for spotting the problem) ===== version 1.0.0 * CHICKEN 5 support * Drop support for page handlers that produce strings ("string mode"). Now only SXML is supported. * {{enable-sxml}} has been deprecated. It is still available, but doesn't do anything. * Drop dependency on the following eggs: regex, html-tags, html-utils * The following deprecated procedures have been removed: {{++}}, {{concat}} * New procedure: {{html-page}} ===== version 0.42.0 * Use SPIFFY_TEST_PORT environment variable's value (if provided) for tests * Make {{awful-start}} properly honor the {{dev-mode?}} keyword parameter. Before this change, {{development-mode-actions}} would only be called from {{load-apps}}. So, if an application uses {{awful-start}} with {{dev-mode?}} set to {{#t}}, but doesn't call {{load-apps}}, {{development-mode-actions}} would not be called. While at it, call {{development-mode-actions}} ''before'' evaluating the thunk for {{awful-start}}, so that the evaluated code can override the settings made by {{development-mode-actions}}. * Bug fix for {{form}} in SXML mode. Thanks to Thomas Hintz for spotting that issue. * {{debug-file}}: its value can now also be an output-port. {{debug}} and {{debug-pp}} will properly handle the {{debug-file}} value types: filenames (strings) or output port objects. * Add {{strict}} keyword parameter for define-page and ajax & friends For convenience, awful sloppily considered requests for files and directories to be equivalent. For example, if a page was defined with a {{"/foo"}} matcher, a request for {{"/foo/"}} would be handled by {{"/foo"}}'s handler. That's not always desired. For example, consider this case: (define-page (irregex "/[^/]*") identity) We want to specify that, e.g., requests for {{/foo}} should be handled, but requests for {{/foo/}} should ''not'' be handled. However, since awful tries to find a handler by stripping the trailing slash, a request for {{/foo/}} would actually be handled by the handler for {{(irregex "/[^/]*")}}, even if the regex specified that paths with a trailing slash should not match. To work around that inconsistency, a new keyword parameter has been added to {{define-page}}, {{ajax}}, {{periodical-ajax}} and {{ajax-link}}: {{strict}} (default: {{#f}}, for backward compatibility). If the {{strict}} keyword parameter is truthy, awful will not try to find a handler for the requested path by removing the trailing slash. With this change, the format of the resources table has changed too. Now the value part of hash table items is a pair whose car is the resource handler and the cdr is a boolean indicating whether the resource is "strict" or not. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=32298|version 0.41.0]] * The deprecated procedure {{sql-quote}} has been removed * Set the default value for {{$db}}'s {{default}} keyword param as {{'()}} To be consistent with the db-support eggs for awful. '''WARNING''': this change may break your code! In case of an empty result set from a query, {{$db}} will now return {{'()}} instead of {{#f}}. This change can be specially harmful in case you have something like <enscript highlight=scheme> (or ($db "some query") "something") </enscript> In this example, if the query returns an empty result set, that expression would produce: * in older awful versions: {{"something"}} * in awful versions with this change: {{'()}} To work around the incompatible behaviors, you can: * require awful 0.41.0 in you .meta file * always provide a value for {{$db}}'s {{default}} keyword parameter ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=30483|version 0.40.0]] * SSQL support for {{$db}} and {{$db-row-obj}} * Full support for static pages generation (via [[/egg/awful-static-pages|awful-static-pages]]) * {{--privileged-code}} now accepts multiple files (comma-separated) * Bug fixes in web-repl when in SXML mode * The default value for {{use-fancy-web-repl?}} is now {{#t}} in {{awful-start}} (to match the command line application's default behavior) * jQuery updated to 1.11.0 ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=30336|version 0.39.2]] * jQuery's {{on}} method support for {{ajax}} & friends * {{use-sxml}} keyword parameter for {{define-page}} and {{ajax}} & friends take precedence over {{enable-sxml}}. {{use-sxml}} can now be used to disable SXML mode for certain pages and {{ajax}} handlers when bound to {{#f}} (thanks to Thomas Hintz for reporting that issue) * Fix redirection to login page when in SXML mode (regression introduced by 03e4e6b9ca2378928fa35c5b8b68738561010aa2 in 0.39.1). Thanks to Thomas Hintz for reporting that and suggesting removing html-tags code ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=29489|version 0.39.1]] * Update jQuery to version 1.10.2 * Bug fix: {{page-path}} should return {{(app-root-path)}} when given {{"/"}} * Bug fix: check to make sure redirect-to-login-page always generates HTML (patch by Hugo Arregui) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=28751|version 0.39]] * The default methods for {{define-page}} are now HEAD and GET (thanks to Thomas Hintz) * Bug fix for {{link}} in SXML mode * Add {{define-app}} macro * Add {{literal-script/style?}} parameter (suggested by John J. Foerch) * Add {{add-css}} procedure (suggested by John J. Foerch) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=28237|version 0.38.2]] * Fix bug related to path matchers as procedures and sessions which caused errors, for example, on redirections from pages defined with path matchers as procedures to the login page ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=27797|version 0.38.1]] * Fix bug in handler for {{/reload}} when in development mode and in SXML mode * Versions now have a second dot for bugfix releases ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=27701|version 0.38]] * Fix bug in {{define-page}} when page handlers return procedures ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=27693|version 0.37]] * Fix {{page-exception-message}} when in development mode and using SXML (thanks to sECuRE for catching that) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=27510|version 0.36]] * SXML support. The use of SXML mode (i.e., {{(enable-sxml #t)}} is highly encouraged. All the documentation examples show SXML code. Page handlers that yield strings are deprecated and support for them will be removed in future versions. * {{++}} and {{concat}} have been deprecated (they are still available, but will be removed in future versions). * jQuery updated to version 1.8.2 ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=26831|version 0.35]] * Added {{awful-resources-table}} * Added {{set-page-title!}} (thanks to John J. Foerch for suggesting that) * {{define-page}}'s first argument (the path matcher) can now be a procedure (thanks to John J. Foerch for suggesting that) * The return value of the second argument to {{define-page}} can now be a procedure * {{add-request-handler-hook!}} overwrites hooks with the same name instead of adding multiple copies * {{define-page}}'s {{use-ajax}} keyword parameter can also be bound to a string indicating the path to the ajax library * Code moved to GitHub ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=25890|version 0.34]] * Don't check {{current-effective-user-id}} on windows, since it is not available on that platform. Reported by Karl_H on #chicken. * Use {{<label>}} instead of {{<span>}} for login form labels (patch by Arthur Maciel). Warning: this change may break your login form layout if you rely on the specific {{<span>}} tags for form labels. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=25689|version 0.33]] * jQuery updated to version 1.7.1 * Added {{session-cookie-setter}} (thanks to Thomas Hintz) * Added {{event}} argument to anonymous JavaScript function given as argument to {{bind}}/{{live}} ({{ajax}} and friends). Patch by Thomas Hintz. * The {{method}} keyword parameter for {{define-page}} and {{ajax}} & friends can be bound to a list of methods. Methods are now case insensitive. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=25073|version 0.32]] * Added {{method}} keyword parameter for {{define-page}}. Now the page router takes into account the HTTP method, so it is possible to have two different pages using the same path but different methods. * Added {{error-handler}} keyword parameter for {{ajax}}, {{ajax-link}} and {{periodical-ajax}} (suggested by Thomas Hintz) * {{ajax}} and friends send {{Content-Type: application/json}} when {{update-targets}} is non-#f * {{ajax}} bugfix: only opens db connection and refresh session when the page access is allowed * The default value for {{awful-backlog}} has been set to 100 * Updated jQuery from version 1.5.2 to 1.6.3 * Dropped {{-lambda-lift}} build option * Added the {{reload-apps}} procedure. {{load-apps}} no longer resets the resources table * {{awful-start}} requires a thunk as argument. With this, awful can be embedded into standalone applications ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=23515|version 0.31]] * Added a tiny wrapper around [[/egg/spiffy-request-vars|spiffy-request-vars]], adding the {{with-request-variables}} macro and exporting {{spiffy-request-vars}}'s converters. * Updated jQuery from version 1.5.1 to 1.5.2. * Better support for multiple applications under the same virtual host ({{add-request-handler-hook!}} and {{remove-request-handler-hook!}}) * {{$db}} checks if database access is enabled via {{(enable-db)}} and throws an error if it is not. * Fixed critical bug regarding to parameters and thread reuse by Spiffy * Fixed {{redirect-to}} but introduced in version 0.29 ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=23075|version 0.30]] * The {{(ajax-library)}} is always linked before any other scripts and JavaScript code in the headers (i.e., when {{(javascript-position)}} is {{top}}). * Ajax-related procedures simplifications and bugfix in the session awareness code when called from {{define-session-page}}. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=22907|version 0.29]] * Updated jQuery from version 1.5.0 to 1.5.1. * {{ajax}} and {{periodical-ajax}} bugfix (for situations when they are used simultaneously, or {{periodical-ajax}} and {{add-javascript}}). * The default URI for {{ajax-library}} is now protocol-relative (thanks to Peter Bex). * Added the {{javascript-position}} parameter. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=22639|version 0.28]] * Updated jQuery from version 1.4.3 to 1.5.0. * {{ajax}} and friends ({{periodical-ajax}}, {{ajax-link}}) are now session-aware when called from {{define-session-page}} (i.e., you don't need to explicitly pass the session identifier). * Added the {{cache}} keyword parameter for {{ajax}}, {{ajax-link}} and {{periodical-ajax}}. * The HTTP request variables are now parsed and made available on demand, when {{$}} is called for the first time for each request (thanks to David Krentzlin). * Applications are no longer loaded with administrator privileges when awful is run by administrator and configured to switch to a non privileged user. For code which needs administrator privileges (like binding to low ports, IP addresses, user/group settings, etc.), there's a new command line option for the awful application server: {{--privileged-code}}. '''Warning''': if you rely on your configuration performing actions with administrator privileges, this change may affect your code. * {{define-session-page}} bug fix (it was not properly obtaining the session identifier). ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=20689|version 0.27]] * Require the {{regex}} egg, for chickens >= 4.6.2 ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=19844|version 0.26]] * {{define-page}} allows page redefinitions * Added the {{undefine-page}} procedure * Fancy web REPL (enabled by default). The awful application server now accepts the {{--disable-web-repl-fancy-editor}} command line option to disable the web REPL fancy editor. * Added the following parameters, related to the fancy web REPL: {{enable-web-repl-fancy-editor}} and {{web-repl-fancy-editor-base-uri}}. * Bug fix for the development mode: requests to the {{/reload}} path killed {{/web-repl}} and {{/session-inspector}} ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=19496|version 0.25]] * The awful application server can now be used with the {{--development-mode}} command line option, which enables the web REPL, the session inspector (when {{enable-session}} is #t) for the localhost and on-browser error messages and backtraces. When running in development mode, the {{/reload}} path is defined to reload awful apps. * The awful application server now supports the {{--ip-address}} and {{--port}} command line options. * {{use-ajax}} keyword parameter for {{define-page}} (for when {{enable-ajax}} is {{#f}} and you need ajax for some specific pages only) * Added {{awful-response-headers}} and {{development-mode?}} parameters ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18903|version 0.24]] * initial support for storing session identifiers in cookies. Added the {{enable-session-cookie}} and {{session-cookie-name}} parameters. By default, cookies-sessions are enabled (relevant only when using sessions, of course). '''Warning''': if your code relies on the availability of session identifiers in the query string or in the request body for whatever reason, it will break. * initial support for HTTP redirection via {{redirect-to}}. * web-repl and session-inspector beautification * web-repl and session inspector provide a {{headers}} keyword parameter * fixes for {{define-page}} regarding to regex-based paths * {{ajax}} always prioritizes the {{success}} keyword parameter value (when it's given) to generate code for jQuery's {{success}} attribute, even when {{target}} and {{update-targets}} are given * awful (the application) handles the {{-v}} and {{--version}} command line options ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18559|version 0.23]] * added the {{update-targets}} keyword parameter for {{ajax}}, {{periodical-ajax}} and {{ajax-link}} procedures (multiple targets update support) * added the {{debug-resources}} parameter for debugging the resources table * the {{js}} keyword parameter for {{ajax}}, {{periodical-ajax}} and {{ajax-link}} has been renamed to {{success}} (to match jQuery's API naming for Ajax). '''Warning''': if your code uses the {{js}} keyword parameter for {{ajax}}, {{periodical-ajax}} or {{ajax-link}}, this change will break your code. * the JavaScript variable bound to the response data on successful ajax request has been renamed to {{response}} (it was {{h}} before). The same warning for the {{js}}->{{success}} renaming is valid for this change, since it is directly related to {{js}} (now {{success}}). * the default value for {{main-page-path}} is now {{"/"}} (it was {{"/main"}} before). There's no more main page redirection. '''Warning''': if your code relies on hardcoded {{"/main"}} paths (instead of using {{(main-page-path)}}) or if you rely on the main page automatic redirection, this change will break your code. * {{ajax}} bugfix regarding to session identifiers (could cause "Invalid session ID" errors in some cases, specially with {{define-session-page}}). * better handling of URI paths regarding to paths as directories (see FAQ's ''How does awful bind URIs to files/directories on the filesystem and procedures?'') ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18328|version 0.22]] * bug fix: fixed unintended shadowing of {{js}} in ajax * added the {{prelude}} keyword parameter for {{ajax}}, {{periodical-ajax}} and {{ajax-link}} * {{$db}} supports the {{values}} keyword parameter (patch by Stephen Eilert) * awful (the application) can now be invoked without arguments * awful (the application) handles {{-h}} and {{--help}} * dropped [[/eggref/4/jsmin|jsmin]] requirement ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18149|version 0.21]] * ajax and main page redirection issues fixes ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18051|version 0.20]] * {{page-access-control}} controls access to pages even when no session is in use * '''Warning''': the following parameters have been removed: {{enable-reload}}, {{reload-path}} and {{reload-message}}. Now the way to define reloaders is via {{define-page}}. * new parameter: {{awful-apps}} (a list of awful applications as passed to the awful server) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18040|version 0.19]] * bug fix for {{(reload-path)}} handler ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=18030|version 0.18]] * support for regex-based page paths (see {{define-page}}) * {{define-page}} checks whether the second arg is a procedure. * use {{-O3 -lambda-lift}} instead of {{-O2}} for compilation * {{main-page-path}} redirection made with code stolen from [[/egg/spiffy|spiffy]]'s {{send-response}} ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17510|version 0.17]] * .meta bug fix. postgresql is not required as a dependency (thanks to Stephen Pedrosa Eilert for pointing this issue). ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17393|version 0.16]] * added {{define-session-page}} * bug fix for {{link}} ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17383|version 0.15]] * jQuery updated to 1.4.2 ({{ajax-library}}). ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17350|version 0.14]] * {{link}}'s {{args}} keyword parameter renamed to {{arguments}} (the same as {{ajax}}'s). ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17346|version 0.13]] * Session-aware {{link}} and {{form}} procedures. Bug fix for {{ajax-link}} (was not passing the {{class}} keyword argument to <a>). ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17278|version 0.12]] * Containers for user and password fields ({{login-form}}) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17275|version 0.11]] * awful sets Spiffy's {{root-path}} to {{(current-directory)}} ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17186|version 0.10]] * Multiple database support. Currently PostgreSQL (via [[/egg/postgresql|postgresql]] egg) and SQLite3 (via [[/egg/sqlite3|sqlite3]] and [[/egg/sql-de-lite|sql-de-lite]] eggs) are supported. See [[/egg/awful-postgresql|awful-postgresql]], [[/egg/sqlite3|sqlite3]] and [[/egg/sql-de-lite|sql-de-lite]] eggs. * Removed requirement for postgresql * {{enable-db}} is now a procedure (not a parameter as before) and accepts no arguments ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17172|version 0.9]] * {{login-form}} gets the {{user}} refilled when the session or password is not valid. This feature may be disabled by setting the {{refill-user}} keyword parameter for {{login-form}} to {{#f}} (default is {{#t}}). Thanks to Arthur Maciel for suggesting this feature. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17149|version 0.8]] * jQuery updated to 1.4.1 ({{ajax-library}}). * support for jQuery's {{live}} method ({{live}} keyword parameter for {{ajax}}, {{ajax-link}} and {{periodical-ajax}}). See [[http://api.jquery.com/live/]] * bug fixes: {{periodical-ajax}} and {{ajax-link}} propagate {{vhost-root-path}} to {{ajax}} * added {{awful-start}} and dropped spiffy requirement for awful (the server) * dropped requirement for miscmacros * added more ajax tests ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17139|version 0.7]] * {{ajax-link}} accepts all {{<a>}}'s keyword arguments ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17134|version 0.6]] * Explicitly depends on [[/egg/http-session|http-session]] 2.0 ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17130|version 0.5]] * Reload handler register the reload path after reloading. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17127|version 0.4]] * {{disable-reload?}} renamed to {{enable-reload}}. {{enable-reload}} is {{#f}} by default. ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17122|version 0.3]] * {{awful}} (the server) allows applications to use Chicken syntax ({{use}}, {{include}}, etc.) ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17120|version 0.2]] * Added JavaScript compression support ===== [[http://wiki.call-cc.org/eggref/4/awful?action=show&rev=17109|version 0.1]] * Initial release
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you add 15 to 4?