]> gitweb.factorcode.org Git - factor.git/commitdiff
Added more details to tutorial of cont-responder
authorChris Double <chris.double@double.co.nz>
Wed, 24 Nov 2004 00:58:19 +0000 (00:58 +0000)
committerChris Double <chris.double@double.co.nz>
Wed, 24 Nov 2004 00:58:19 +0000 (00:58 +0000)
contrib/cont-responder/tutorial.txt

index d9c18adace5b6f27964767ea688c9724add20f96..dae16542f708fe7ffaee7032bf0a6a448cadfa54 100644 (file)
@@ -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
@@ -413,4 +416,269 @@ Either way works. Notice that in the 'post-example2' we had to do a
 '2drop' instead of a 'drop' at the end of the show to remove the
 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.
\ No newline at end of file
+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.
\ No newline at end of file