remove out of date cont-responder docs
parent
a452f95788
commit
9f5845d09e
|
@ -193,7 +193,7 @@ M: promised-label set-label-color set-promised-label-color ;
|
|||
M: promised-label set-label-font set-promised-label-font ;
|
||||
|
||||
: fib ( n -- n )
|
||||
yield dup 2 < [ drop 1 ] [ dup 1 - fib swap 2 - fib + ] if ;
|
||||
1 sleep dup 2 < [ drop 1 ] [ dup 1 - fib swap 2 - fib + ] if ;
|
||||
|
||||
: test-promise-ui ( -- )
|
||||
<promise> dup <promised-label> gadget. [ 30 fib unparse swap fulfill ] cons spawn drop ;
|
||||
<promise> dup <promised-label> gadget. [ 15 fib unparse swap fulfill ] cons spawn drop ;
|
||||
|
|
|
@ -1,375 +0,0 @@
|
|||
cont-responder v0.3
|
||||
===================
|
||||
|
||||
NOTE: This documentation is slightly out of date with respect to the
|
||||
current code but contains the basic idea.
|
||||
|
||||
In a continuation based web application the current position within
|
||||
the web application is identified by a random URL. In this example the
|
||||
URL is generated as a string of random digits:
|
||||
|
||||
: get-random-id ( -- id )
|
||||
#! Generate a random id to use for continuation URL's
|
||||
<% 16 [ random-digit % ] times %> ;
|
||||
|
||||
Each URL is associated with a quotation or continuation. When that URL
|
||||
is accessed, that quotation is executed. The quotation will receive an
|
||||
alist on the top of the stack which holds any POST or query
|
||||
parameters. A global table is maintained that holds the association of
|
||||
quotations to id's.
|
||||
|
||||
: continuation-table ( -- <namespace> )
|
||||
#! Return the global table of continuations
|
||||
"cont" get ;
|
||||
|
||||
: reset-continuation-table ( -- )
|
||||
#! Create the initial global table
|
||||
<namespace> "cont" set ;
|
||||
|
||||
: register-continuation ( quot -- id )
|
||||
#! Store a continuation in the table and associate it with
|
||||
#! a random id.
|
||||
continuation-table [ get-random-id dup [ set ] dip ] bind ;
|
||||
|
||||
: get-registered-continuation ( id -- cont )
|
||||
#! Return the continuation associated with the given id.
|
||||
continuation-table [ get ] bind ;
|
||||
|
||||
: resume-continuation ( value id -- )
|
||||
#! Call the continuation associated with the given id,
|
||||
#! with 'value' on the top of the stack.
|
||||
get-registered-continuation call ;
|
||||
|
||||
When a an URL is accessed, the continuation for the specific URL is
|
||||
obtained and called. That continuation needs to exit back to the
|
||||
caller when it has some HTML that it needs to display. returning that
|
||||
HTML to the caller. To exit back to the caller it calls an 'exit
|
||||
continuation' that is stored in an "exit" variable:
|
||||
|
||||
: exit-continuation ( -- exit )
|
||||
#! Get the current exit continuation
|
||||
"exit" get ;
|
||||
|
||||
: call-exit-continuation ( value -- )
|
||||
#! Call the exit continuation, passing it the given value on the
|
||||
#! top of the stack.
|
||||
"exit" get call ;
|
||||
|
||||
: with-exit-continuation ( quot -- )
|
||||
#! Call the quotation with the variable "exit" bound such that when
|
||||
#! the exit continuation is called, computation will resume from the
|
||||
#! end of this 'with-exit-continuation' call, with the value passed
|
||||
#! to the exit continuation on the top of the stack.
|
||||
[ "exit" set call call-exit-continuation ] callcc1 nip ;
|
||||
|
||||
All this calling of continuations is hidden behind a single 'show'
|
||||
call. 'show' will take a quotation on the stack. That quotation should
|
||||
return an HTML string. 'show' will call it to generate the HTML and
|
||||
call the exit continuation with this string on the stack so it gets
|
||||
returned to the httpd responder. The quotation receives a 'url' on the
|
||||
top of the stack which is the 'id' of the continuation to resume when
|
||||
that URL is accessed.
|
||||
|
||||
The HTML page that 'show' displays can contain links to
|
||||
'callbacks'. These are links to other quotations that when called
|
||||
will perform some action and return back to the calling page. To
|
||||
return back to the calling page 'show' must capture and store the
|
||||
current continuation before 'show' does anything so it can be later
|
||||
return to by the callback. The following words store the current
|
||||
continuation and retrieve it:
|
||||
|
||||
: store-callback-cc ( -- )
|
||||
#! Store the current continuation in the variable 'callback-cc'
|
||||
#! so it can be returned to later by callbacks. Note that it
|
||||
#! recalls itself when the continuation is called to ensure that
|
||||
#! it resets it's value back to the most recent show call.
|
||||
[
|
||||
[ "callback-cc" set call ] callcc0 drop store-callback-cc
|
||||
] callcc0 ;
|
||||
|
||||
To generate the string of HTML I use 'with-string-stream' which calls
|
||||
a quotation and all output inside that call gets appended to a string:
|
||||
|
||||
: with-string-stream ( quot -- string )
|
||||
#! Call the quotation with standard output bound to a string output
|
||||
#! stream. Return the string on exit.
|
||||
<namespace> [
|
||||
"stdio" <string-output-stream> put call "stdio" get stream>str
|
||||
] bind ;
|
||||
|
||||
: show ( quot -- alist )
|
||||
#! Call the quotation with the URL associated with the current
|
||||
#! continuation. Return the HTML string generated by that code
|
||||
#! to the exit continuation. When the URL is later referenced then
|
||||
#! computation will resume from this 'show' call with a alist on
|
||||
#! the stack containing any query or post parameters.
|
||||
store-callback-cc
|
||||
[
|
||||
register-continuation swap with-string-stream
|
||||
call-exit-continuation
|
||||
] callcc1
|
||||
nip ;
|
||||
|
||||
An httpd get responder is used that takes the ID as an argument, retrieves
|
||||
the continuation associated with it and calls it. For the post
|
||||
responder the post data is converted into an alist and it is put on
|
||||
the top of the stack when calling the continuation.
|
||||
|
||||
: cont-get-responder ( id -- )
|
||||
#! httpd responder that retrieves a continuation and calls it.
|
||||
[ f swap resume-continuation ] with-exit-continuation
|
||||
serving-html print drop ;
|
||||
|
||||
: post-request>alist ( post-request -- alist )
|
||||
#! Return an alist containing name/value pairs from the
|
||||
#! post data.
|
||||
dup "&" swap str-contains [
|
||||
"(.+)&(.+)" groups [ "(.+)=(.+)" groups uncons car cons ] inject
|
||||
] [
|
||||
"(.+)=(.+)" groups uncons car cons unit
|
||||
] ifte ;
|
||||
|
||||
: cont-post-responder ( id -- )
|
||||
#! httpd responder that retrieves a continuation for the given
|
||||
#! id and calls it with the POST data as an alist on the top
|
||||
#! of the stack.
|
||||
[
|
||||
read-post-request post-request>alist swap resume-continuation
|
||||
] with-exit-continuation
|
||||
serving-html print drop ;
|
||||
|
||||
Some code to install the responder:
|
||||
|
||||
: install-cont-responder ( -- )
|
||||
#! Install the cont-responder in the table of httpd responders
|
||||
"httpd-responders" get [
|
||||
<responder> [
|
||||
[ cont-get-responder ] "get" set
|
||||
[ cont-post-responder ] "post" set
|
||||
reset-continuation-table
|
||||
] extend "cont" set
|
||||
] bind ;
|
||||
|
||||
Now to test it. Here's a function that displays some text on an HTML page:
|
||||
|
||||
: display-page ( title -- )
|
||||
#! Display a page with some text to test the cont-responder.
|
||||
#! The page has a link to the 'next' continuation.
|
||||
[
|
||||
swap [
|
||||
"<a href='" write write "'>Next</a>" write
|
||||
] html-document
|
||||
] show drop ;
|
||||
|
||||
Note that it contains an A HREF link to the URL that resumes the
|
||||
computation (The quotation passed to show has this URL passed to it by
|
||||
show).
|
||||
|
||||
An example of a POST request is a page that requests input of a
|
||||
name. The following function calls show to display it and returns the
|
||||
result of the 'name' field by retrieving it from the alist. Notice how
|
||||
the 'action' of the post request is set to the URL passed in to the
|
||||
quotation passed to show. So the 'next' that happens here is not an A
|
||||
HREF but the action field of the POST.
|
||||
|
||||
: display-get-name-page ( -- name )
|
||||
#! Display a page prompting for input of a name and return that name.
|
||||
[
|
||||
"Enter your name" [
|
||||
"<form method='post' action='" write write "'>" write
|
||||
"Name: <input type='text' name='name' size='20'>" write
|
||||
"<input type='submit' value='Ok'>" write
|
||||
"</form>" write
|
||||
] html-document
|
||||
] show
|
||||
"name" swap assoc ;
|
||||
|
||||
A word that displays a sequence of these pages would be:
|
||||
|
||||
: test-cont-responder ( alist - )
|
||||
#! Test the cont-responder responder by displaying a few pages in a row.
|
||||
drop
|
||||
"Page one" display-page
|
||||
"Hello " display-get-name-page cat2 display-page
|
||||
"Page three" display-page ;
|
||||
|
||||
This displays the first page, then a page prompting for the
|
||||
name. "Hello " is concatenated to the result of the page and a third
|
||||
page is displayed. A fourth page is available as well.
|
||||
|
||||
The following registers this word with the continuation system:
|
||||
|
||||
: register-test-cont-responder ( -- id )
|
||||
#! Register the test-cont-responder word so that accessing the
|
||||
#! URL with the returned ID will call it.
|
||||
"httpd-responders" get [
|
||||
"cont" get [
|
||||
[ test-cont-responder ] register-continuation
|
||||
] bind
|
||||
] bind ;
|
||||
|
||||
This returns an ID which can be used from the web server to resume
|
||||
it. Start the web server:
|
||||
|
||||
8888 httpd
|
||||
|
||||
Now access the URL with that id:
|
||||
|
||||
http://localhost:8888/cont/1234567890
|
||||
|
||||
(replace 1234567890 with the ID returned by register-continuation above)
|
||||
|
||||
You'll see the first page and a link. Click the link and you'll see
|
||||
the second page requesting a name. Enter the name and press 'Ok'. A
|
||||
third page will display a message using that name. You can book mark,
|
||||
refresh, go back, enter a new name, etc as expected.
|
||||
|
||||
You can do any form of computation inside the handlers. Here's an
|
||||
example of looping a set number of times:
|
||||
|
||||
: test-cont-responder2 ( alist - )
|
||||
#! Test the cont-responder responder by displaying a few pages in a loop.
|
||||
[ "one" "two" "three" "four" ] [ display-page ] each
|
||||
"Done!" display-page ;
|
||||
|
||||
: register-test-cont-responder2 ( -- id )
|
||||
#! Register the test-cont-responder2 word so that accessing the
|
||||
#! URL with the returned ID will call it.
|
||||
"httpd-responders" get [
|
||||
"cont" get [
|
||||
[ test-cont-responder2 ] register-continuation
|
||||
] bind
|
||||
] bind ;
|
||||
|
||||
There is currently a limited ability to do 'callbacks'. You can
|
||||
register a quotation as an HTML A HREF anchor thar when accessed by
|
||||
the browser will run the quotation and then return to the most recent
|
||||
'show' call. This has the effect of allowing 'subroutine' calls as
|
||||
page links that can do anything (including display other pages and
|
||||
complicated action) and will return back to the originating page. The
|
||||
word to generate this link is:
|
||||
|
||||
: quot-href ( text quot -- )
|
||||
#! Write to standard output an HTML HREF where the href,
|
||||
#! when referenced, will call the quotation and then return
|
||||
#! back to the most recent 'show' call (via the callback-cc).
|
||||
#! The text of the link will be the 'text' argument on the
|
||||
#! stack.
|
||||
"<a href='" write
|
||||
"callback-cc" get [ call ] cons append register-continuation write
|
||||
"'>" write
|
||||
write
|
||||
"</a>" write ;
|
||||
|
||||
An example of use is a simple menu page that displays links to the
|
||||
words written previously:
|
||||
|
||||
: test-cont-responder3 ( alist - )
|
||||
#! Test the quot-href word by displaying a menu of the current
|
||||
#! test words. Note that we drop the 'url' argument to the show
|
||||
#! quotation as we don't link to a 'next' page.
|
||||
drop
|
||||
[
|
||||
drop
|
||||
"Menu" [
|
||||
"<ol>" write
|
||||
"<li>" write
|
||||
"Test responder1" [ test-cont-responder ] quot-href
|
||||
"</li>" write
|
||||
"<li>" write
|
||||
"Test responder2" [ test-cont-responder2 ] quot-href
|
||||
"</li>" write
|
||||
"</ol>" write
|
||||
] html-document
|
||||
] show drop ;
|
||||
|
||||
: register-test-cont-responder3 ( -- id )
|
||||
#! Register the test-cont-responder3 word so that accessing the
|
||||
#! URL with the returned ID will call it.
|
||||
"httpd-responders" get [
|
||||
"cont" get [
|
||||
[ test-cont-responder3 ] register-continuation
|
||||
] bind
|
||||
] bind ;
|
||||
|
||||
You should now be able to click on the menu items, navigate through
|
||||
those pages and when the sequence of pages ends, return back to the
|
||||
original menu page.
|
||||
|
||||
Note that this is just a proof of concept. In a real framework the
|
||||
continuations need to be expired after time. It would also enable
|
||||
generating links to other pages, etc rather than just a sequence of
|
||||
pages. I plan to flesh this out over the next few days and present
|
||||
some more useful examples. My main point was to see if it was possible
|
||||
to do this type of thing in Factor.
|
||||
|
||||
The number of words required to get things going is amazingly small
|
||||
and it was very easy to develop this far.
|
||||
|
||||
The code is contained in cont-responder.factor. Here are the steps to
|
||||
run all the examples starting with a default Factor 0.60 install:
|
||||
|
||||
1) java -cp Factor.jar factor.FactorInterpreter
|
||||
-db:factor.db.FileStore:factor.db
|
||||
|
||||
At the prompt enter:
|
||||
|
||||
---8<---
|
||||
USE: httpd-responder
|
||||
default-responders
|
||||
exit
|
||||
---8<---
|
||||
|
||||
2) java -cp Factor.jar factor.FactorInterpreter
|
||||
-db:factor.db.FileStore:factor.db
|
||||
-no-compile
|
||||
|
||||
At the prompt enter:
|
||||
|
||||
---8<---
|
||||
USE: httpd
|
||||
"cont-responder.factor" run-file
|
||||
USE: cont-responder
|
||||
init-cont-responder
|
||||
register-test-cont-responder .
|
||||
---8<---
|
||||
|
||||
Make note of the number returned by the last line call this [1].
|
||||
|
||||
---8<---
|
||||
register-test-cont-responder2 .
|
||||
---8<---
|
||||
|
||||
Make note of the number returned by the last line call this [2].
|
||||
|
||||
---8<---
|
||||
register-test-cont-responder3 .
|
||||
---8<---
|
||||
|
||||
Make note of the number returned by the last line call this [3].
|
||||
|
||||
---8<---
|
||||
8888 httpd
|
||||
---8<---
|
||||
|
||||
For the first example go to http://localhost:8888/cont/[1]
|
||||
where you replace [1] with the number from [1] above. In my system
|
||||
it was:
|
||||
|
||||
http://localhost:8888/cont/0406763029866672
|
||||
|
||||
For the second example go to http://localhost:8888/cont/[2]
|
||||
where you replace [2] with the number from [2] above. In my system
|
||||
it was:
|
||||
|
||||
http://localhost:8888/cont/6018237533813007
|
||||
|
||||
For the menu example go to http://localhost:8888/cont/[3]
|
||||
where you replace [3] with the number from [3] above. In my system
|
||||
it was:
|
||||
|
||||
http://localhost:8888/cont/3568874223456634
|
||||
|
||||
3) You can use the inspector to look at the continuation table:
|
||||
|
||||
http://localhost:8888/inspect/global'httpd-responders'cont'cont
|
Loading…
Reference in New Issue