Added more details to tutorial of cont-responder
parent
24e8d2fc2a
commit
7c8e1f62b6
|
@ -45,8 +45,8 @@ browser and display a page is 'show'. Think of it as 'show a page to
|
|||
the client'. 'show' takes a single item on the stack and that is a
|
||||
'page generation' quotation.
|
||||
|
||||
A 'page generation' quotation is a quotation with stack effect (
|
||||
string -- ). For now we'll ignore the string it receives on the
|
||||
A 'page generation' quotation is a quotation with stack effect
|
||||
( string -- ). For now we'll ignore the string it receives on the
|
||||
stack. Its purpose will be explained later.
|
||||
|
||||
Hello World 1
|
||||
|
@ -70,11 +70,12 @@ The responder is installed using:
|
|||
|
||||
"helloworld1" [ hello-world1 ] install-cont-responder
|
||||
|
||||
The 'install-cont-responder' word has stack effect ( name quot --
|
||||
). It installs a responder with the given name. When the URL for that
|
||||
responder is accessed the 'quot' quotation is run. In this case it is
|
||||
'hello-world1' which displays the single HTML page described
|
||||
previously.
|
||||
The 'install-cont-responder' word has stack effect
|
||||
( name quot -- ). It installs a responder with the given name.
|
||||
|
||||
When the URL for that responder is accessed the 'quot' quotation is
|
||||
run. In this case it is 'hello-world1' which displays the single HTML
|
||||
page described previously.
|
||||
|
||||
Accessing the above responder from a web browser is via an URL like:
|
||||
|
||||
|
@ -89,12 +90,12 @@ chore. Especially when the content is dynamic requiring concatenation
|
|||
of many pieces of data.
|
||||
|
||||
The 'cont-responder' system uses 'html', a library that allows writing
|
||||
HTML looking output directly in factor. This system developed for
|
||||
'cont-responder' has recently been made part of the standard 'html'
|
||||
HTML looking output directly in factor. This system, developed for
|
||||
'cont-responder', has recently been made part of the standard 'html'
|
||||
library of Factor.
|
||||
|
||||
'html' basically allows you to write HTML-like output in a factor word
|
||||
and it will be automatically output. It can be tested at the console
|
||||
and it will be output as correct HTML. It can be tested at the console
|
||||
very easily:
|
||||
|
||||
USE: html
|
||||
|
@ -121,8 +122,8 @@ You can use any factor code at any point:
|
|||
</p>
|
||||
=> <p style='text-align: red'>Using style text-align: red</p>
|
||||
|
||||
Tags that are not normally closed are written using an XML style
|
||||
(ie. with a trailing slash):
|
||||
Tags that are not normally closed are written using XML style closed
|
||||
tag (ie. with a trailing slash):
|
||||
|
||||
"One" write <br/> "Two" write <br/> <input type= "text" input/>
|
||||
=> One<br>Two<br><input type='text'>
|
||||
|
@ -151,8 +152,10 @@ Dynamic Data
|
|||
============
|
||||
|
||||
Adding dynamic data to the page is relatively easy. This example pulls
|
||||
a value from the 'room' word which displays the amount of available
|
||||
and free memory in the system.
|
||||
a information from the 'room' word which displays memory details about
|
||||
the running Factor system. It also uses 'room.' which outputs these
|
||||
details to standard output and this is wrapped in a <pre> tag so it is
|
||||
formatted correctly.
|
||||
|
||||
: memory-stats1 ( -- )
|
||||
[
|
||||
|
@ -227,13 +230,13 @@ the third.
|
|||
|
||||
When a 'show' call is executed the page generated by the quotation is
|
||||
sent to the client. The computation of the responder is then
|
||||
'suspended'. When the client accesses a special URL computiation is
|
||||
'suspended'. When the client accesses a special URL, computation is
|
||||
resumed at the point of the end of the 'show' call. In this way
|
||||
procedural flow is maintained.
|
||||
|
||||
This brings us to the 'URL' stack item that is available to the 'page
|
||||
generation' quotation passed to 'show'. This URL is a string that
|
||||
contains a URL that can be embedded in the page. When the user access
|
||||
contains an URL that can be embedded in the page. When the user access
|
||||
that URL computation is resumed from the point of the end of the
|
||||
'show' call as described above:
|
||||
|
||||
|
@ -277,9 +280,9 @@ When you display this example in the browser you'll be able to click
|
|||
the URL to navigate. You can use the back button to retry the URL's,
|
||||
you can clone the browser window and navigate them independantly, etc.
|
||||
|
||||
The similarity of the functions above could so with some
|
||||
refactoring. The pages are almost exactly the same so we seperate this
|
||||
into a seperate word:
|
||||
The similarity of the functions above shows that some refactoring
|
||||
would be useful. The pages are almost exactly the same so we seperate
|
||||
this into a seperate word:
|
||||
|
||||
: show-flow-page ( n bool -- )
|
||||
#! Show a page in the flow, using 'n' as the page number
|
||||
|
@ -414,3 +417,268 @@ Either way works. Notice that in the 'post-example2' we had to do a
|
|||
additional 'name' that is on the stack. This wasn't needed in
|
||||
'post-example1' because the 'name' was not on the stack at the time of
|
||||
the 'show' call.
|
||||
|
||||
Associating URL's with words
|
||||
============================
|
||||
A web page can contain URL's that when clicked perform some
|
||||
action. This may be to display other pages, or to affect some server
|
||||
state.
|
||||
|
||||
The 'cont-responder' system enables an URL to be associated with any
|
||||
Factor quotation. This quotation will be run when the URL is
|
||||
clicked. When that quotation exits control is returned to the page
|
||||
that contained the call.
|
||||
|
||||
The word that enables this is 'quot-href'. It takes two items on the
|
||||
stack. One is the text to display for the link. The other is the
|
||||
quotation to run when the link is clicked. This quotation should have
|
||||
stack effect ( -- ).
|
||||
|
||||
This example displays a number which can be incremented or
|
||||
decremented.
|
||||
|
||||
0 "counter" set
|
||||
|
||||
: counter-example1 ( - )
|
||||
#! Display a global counter which can be incremented or decremented
|
||||
#! using anchors.
|
||||
#!
|
||||
#! We don't need the 'url' argument
|
||||
[
|
||||
drop
|
||||
<html>
|
||||
<head>
|
||||
<title> "Counter: " write "counter" get unparse dup write </title>
|
||||
</head>
|
||||
<body>
|
||||
<h2> "Counter: " write write </h2>
|
||||
<p> "++" [ "counter" get succ "counter" set ] quot-href
|
||||
"--" [ "counter" get pred "counter" set ] quot-href
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show
|
||||
drop ;
|
||||
|
||||
"counter-example1" [ counter-example1 ] install-cont-responder
|
||||
|
||||
Accessing this example from the web browser will display a count of
|
||||
zero. Clicking '++' or '--' will increment or decrement the count
|
||||
respectively. This is done by calling a quotation that either
|
||||
increments or decrements the count when the URL's are clicked.
|
||||
|
||||
Because the count is 'global' in this example, if you clone the
|
||||
browser window with the count set to a specific value and increment
|
||||
it, and then refresh the original browser window you will see the most
|
||||
recent incremented state. This gives you 'shopping cart' like state
|
||||
whereby using the back button or cloning windows shows a view of a
|
||||
single global value that can be modified by all browser
|
||||
instances. ie. The state is not backtracked when the back button is
|
||||
used.
|
||||
|
||||
You'll notice that when you visit the root URL for the responder that
|
||||
the count is reset back to zero. This is because when the responder
|
||||
was installed the value of zero was in the namespace stack. This stack
|
||||
is copied when the responder is installed resulting in initial
|
||||
accesses to the URL having the starting value. This gives you 'server
|
||||
side session data' for free.
|
||||
|
||||
Local State
|
||||
===========
|
||||
You can also have a counter value with 'local' state. That is, cloning
|
||||
the browser window will give you a new independant state value that
|
||||
can be incremented. Going to the original browser window and
|
||||
refreshing will show the original value which can be incremented or
|
||||
decremented seperately from that value in the cloned window. With this
|
||||
type of state, using the back button results in 'backtracking' the
|
||||
state value.
|
||||
|
||||
A way to get 'local' state is to store values on the stack itself
|
||||
rather than a namespace:
|
||||
|
||||
: counter-example2 ( count - )
|
||||
[ ( count URL -- )
|
||||
drop
|
||||
<html>
|
||||
<head>
|
||||
<title> "Counter: " write dup unparse write </title>
|
||||
</head>
|
||||
<body>
|
||||
<h2> "Counter: " write dup unparse write </h2>
|
||||
<p> "++" over [ succ counter-example2 ] cons quot-href
|
||||
"--" swap [ pred counter-example2 ] cons quot-href
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show
|
||||
drop ;
|
||||
|
||||
"counter-example2" [ 0 counter-example2 ] install-cont-responder
|
||||
|
||||
This example works by taking the value of the counter and consing it
|
||||
to a code quotation that will increment or decrement it then call the
|
||||
responder again. So if the counter value is '5' the two 'quot-href'
|
||||
calls become the equivalent of:
|
||||
|
||||
"++" [ 5 succ counter-example2 ] cons quot-href
|
||||
"--" [ 5 pred counter-example2 ] cons quot-href
|
||||
|
||||
Because it calls itself with the new count value the state is
|
||||
remembered for that page only. This means that each page has an
|
||||
independant count value. You can clone or use the back button and all
|
||||
browser windows have an independant value.
|
||||
|
||||
Calling 'Subroutines'
|
||||
=====================
|
||||
Being able to call other page display functions from 'quot-href' gives
|
||||
you subroutine like functionality in your web pages. A simple menu
|
||||
that displays a sequence of pages and returns back to the main page is
|
||||
very easy:
|
||||
|
||||
: show-page ( n -- )
|
||||
#! Show a page in the flow, using 'n' as the page number
|
||||
#! to display.
|
||||
[ ( n url -- )
|
||||
<html>
|
||||
<head> <title> "Page " write over unparse write </title> </head>
|
||||
<body>
|
||||
<p> "Page " write swap unparse write </p>
|
||||
<p> <a href= a> "Press to continue" write </a> </p>
|
||||
</body>
|
||||
</html>
|
||||
] show 2drop ;
|
||||
|
||||
: show-some-pages ( n -- )
|
||||
#! Display the given number of pages in a row.
|
||||
[ succ show-page ] times* ;
|
||||
|
||||
: subroutine-example1 ( -- )
|
||||
[
|
||||
<html>
|
||||
<head> <title> "Subroutine Example 1" write </title> </head>
|
||||
<body>
|
||||
<p> "Please select:" write
|
||||
<ol>
|
||||
<li> "Flow1" [ 1 show-some-pages ] quot-href </li>
|
||||
<li> "Flow2" [ 2 show-some-pages ] quot-href </li>
|
||||
<li> "Flow3" [ 3 show-some-pages ] quot-href </li>
|
||||
</ol>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
] show drop ;
|
||||
|
||||
"subroutine-example1" [ subroutine-example1 ] install-cont-responder
|
||||
|
||||
Each item in the ordered list is an anchor. When pressed they will
|
||||
call a quotation that displays a certain number of pages in a
|
||||
row. When that quotation finishes via dropping off the end the main
|
||||
menu page is displayed again.
|
||||
|
||||
Simple Testing
|
||||
==============
|
||||
Sometimes it is useful to test the responder words from the console
|
||||
instead of accessing it via a web browser. This enables you to step
|
||||
through or quickly check to see if a word is generating HTML
|
||||
correctly.
|
||||
|
||||
Because the responders require some state associated with them to keep
|
||||
track of continuation id's and other things you can't usually just run
|
||||
them and expect them to work. The 'show' call for example will fail as
|
||||
it expects some continuations to in the continuation table for that
|
||||
responder.
|
||||
|
||||
The 'cont-testing.factor' file contains some simple words that
|
||||
maintains this state for you in such a way that you can test the words
|
||||
from the console:
|
||||
|
||||
"cont-testing.factor" run-file
|
||||
|
||||
For this example we'll call the 'subroutine-example1' responder from
|
||||
above. First we need to put a 'testing state' object on the stack. All
|
||||
the testing functions expect this on the stack and return it after
|
||||
they have been called. We then put a quotation on the stack which
|
||||
calls the code we want to test and call the 'test-cont-function' word:
|
||||
|
||||
<cont-test-state> [ subroutine-example1 ] test-cont-function
|
||||
=>
|
||||
HTTP/1.1 302 Document Moved
|
||||
Location: 8209741119458310
|
||||
Content-Length: 0
|
||||
Content-Type: text/plain
|
||||
|
||||
The first request is often a 'Document Moved' as above. This is
|
||||
because by default the 'cont-responder' system does the
|
||||
'Post-Refresh-Get' pattern which results in a redirect after each
|
||||
request. This can be disabled but we'll work through the example with
|
||||
it enabled.
|
||||
|
||||
We can see the continuation id where we are 'moved' to in the
|
||||
'Location' header. To access this we use the 'test-cont-click'
|
||||
function. Think of this as manually clicking the
|
||||
URL. 'test-cont-click' has stack effect
|
||||
( state url post -- state). 'post' is a hashtable of post data to pass
|
||||
along with the request. We use 'f' here because we have no post
|
||||
data. Remember that our previous 'test-cont-function' call left the
|
||||
state on the stack:
|
||||
|
||||
"8209741119458310" f test-cont-click
|
||||
=>
|
||||
HTTP/1.0 200 Document follows
|
||||
Content-Type: text/html
|
||||
<html><head><title>Subroutine Example 1</title></head>
|
||||
<body><p>Please select:
|
||||
<ol><li><a href='7687398605200513'>Flow1</a></li>
|
||||
<li><a href='7856272029924613'>Flow2</a></li>
|
||||
<li><a href='4909116160485714'>Flow3</a></li>
|
||||
</ol>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
We can continue to drill down using 'test-cont-click' using the URL's
|
||||
above to see the HTML for each 'click'.
|
||||
|
||||
Here's an example using post data. We'll test the 'post-example1' word
|
||||
written previously:
|
||||
|
||||
<cont-test-state> [ post-example1 ] test-cont-function
|
||||
=>
|
||||
HTTP/1.1 302 Document Moved
|
||||
Location: 5829759941409535
|
||||
Content-Length: 0
|
||||
Content-Type: text/plain
|
||||
|
||||
Again we skip past the forward:
|
||||
|
||||
"5829759941409535" f test-cont-click
|
||||
=>
|
||||
HTTP/1.0 200 Document follows
|
||||
Content-Type: text/html
|
||||
|
||||
<html><head><title>Please enter your name</title></head>
|
||||
<body>
|
||||
<form action='5456539333180428' method='post'>
|
||||
<p>Please enter your name:
|
||||
<input type='text'size='20'name='username'>
|
||||
<input type='submit'value='Ok'>
|
||||
</p>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Now we submit the post data along to the 'action' url:
|
||||
|
||||
"5456539333180428" [ [ "username" | "Chris" ] ] alist>hash test-cont-click
|
||||
=>
|
||||
HTTP/1.0 200 Document follows
|
||||
Content-Type: text/html
|
||||
|
||||
<html>
|
||||
<head><title>Hello!</title></head>
|
||||
<body>
|
||||
<p>Chris, Good to see you!</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
As you can see the post data was sent correctly.
|
Loading…
Reference in New Issue