[Gd-chatter] r11718 - in trunk/libraries/network/koala: sources/examples/koala-basics sources/koala www/demo

cgay at gwydiondylan.org cgay at gwydiondylan.org
Thu Feb 28 11:15:12 CET 2008


Author: cgay
Date: Thu Feb 28 11:15:09 2008
New Revision: 11718

Modified:
   trunk/libraries/network/koala/sources/examples/koala-basics/main.dylan
   trunk/libraries/network/koala/sources/koala/dsp.dylan
   trunk/libraries/network/koala/sources/koala/headers.dylan
   trunk/libraries/network/koala/sources/koala/library-unix.dylan
   trunk/libraries/network/koala/sources/koala/library.dylan
   trunk/libraries/network/koala/sources/koala/responders.dylan
   trunk/libraries/network/koala/sources/koala/response.dylan
   trunk/libraries/network/koala/sources/koala/server.dylan
   trunk/libraries/network/koala/www/demo/footer.dsp
   trunk/libraries/network/koala/www/demo/home.dsp
   trunk/libraries/network/koala/www/demo/login.dsp
Log:
job: koala
* invoke-action -> invoke-responder, which matches the terminology
  visible to the user already, and export invoke-responder so users
  can support different types of responders.
* Made invoke-handler pass correct arguments to responders.
* The entire url-tail match is now available to responders via the
  match: keyword parameter.
* koala-basics uses url-map-definer
* "output" is now a function, which makes it available everywhere,
  not just in tags.
* Got rid of page-definer and responder-definer.  They don't add
  much over url-map definer.
* In url-map-definer, allow action targets to be expressions and
  don't require (or allow) comma between the url and "action".
* Made page-template slot of <dylan-server-page> be per instance
  instead of each-subclass, so it's not necessary to create a new
  class for every top-level template you want to display.

Modified: trunk/libraries/network/koala/sources/examples/koala-basics/main.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/examples/koala-basics/main.dylan	(original)
+++ trunk/libraries/network/koala/sources/examples/koala-basics/main.dylan	Thu Feb 28 11:15:09 2008
@@ -4,28 +4,18 @@
 
 /*
 
-As distributed, the examples in this file should be available starting at
-/demo/home.dsp.  Just start koala-app.exe and this example should be loaded
-as a Koala module.
+To use this example web application (if I may glorify it with that name),
+compile this library and invoke the executable with --config <filename>.
+You will probably have to fix the config file to make the dsp-root setting
+point at the directory containing the "demo" directory that has all the
+.dsp template files in it.
 
 Each page demonstrates a feature of Dylan Server Pages.  You should be able
 to find the code corresponding to a particular URL by searching for that
 URL in this file.  Some XML-RPC methods are defined near the bottom.
 
-During development it's much easier to write your web application as an executable
-that uses the koala HTTP server project, since you can run and debug it directly 
-under Functional Developer.  To do this, you'll need to call start-server to get
-the HTTP server started up.  (See "UNCOMMENT THIS" below.)
-
-In production the idea is to run koala-app.exe, build your web app as a DLL, and
-put the DLL in the koala-app "modules" directory.  This way multiple web apps can
-be run on the same server.  Of course, you may not need multiple apps...
-
-Following are examples of various ways to define static and dynamic handlers
-for web URLs.
-
-Note that any URLs registered for dynamic pages hide URLs corresponding to files in
-the document root directory.  i.e., the dynamic URL takes precedence.
+Note that any URLs registered for dynamic pages hide URLs corresponding to
+files in the document root directory.  i.e., the dynamic URL takes precedence.
 
 */
 
@@ -39,62 +29,46 @@
 
 define constant $demo-base-url :: <byte-string> = "/demo";
 
-// This is used throughout this file to create absolute URLs.
-define function demo-url
-    (url :: <byte-string>) => (absolute-url :: <byte-string>)
-  concatenate($demo-base-url, url)
-end;
-
 
 
-//// Responders -- the lowest level API for responding to a URL
-
-// Responds to a single URL.
-define responder responder1 (demo-url("/responder1"))
-  select (request-method(current-request()))
-    #"get", #"post"
-      => format(output-stream(current-response()),
-                "<html><body>This is the output of a 'define responder' form."
-                "<p>Use your browser's Back button to return to the example."
-                "</body></html>");
-  end;
-end;
-
-// Responds to a single directory (i.e., prefix) URL.
-define directory responder dir1 ("/dir1")
-  let request = current-request();
-  select (request.request-method)
-    #"get", #"post"
-      => format(output-stream(current-response()),
-                "<html><body>This is a directory responder.  The part of the url after "
-                "the directory was %s."
-                "<p>Use your browser's Back button to return to the example."
-                "</body></html>",
-                request.request-url-tail);
-  end;
-end;
+// The lowest level API for responding to a URL is just a function
+// That does output to the response's output stream.  The simplest
+// way to do that is to use the "output" function.  The only thing
+// special about a responder function is that it must accept keyword
+// arguments.  The match: argument is always passed and is always a
+// <regex-match> object.  See the documentation for url-map for 
+// details.
+
+define function responder1 (#key)
+  output("<html><body>This is the output of a simple responder function."
+         "<p>Use your browser's Back button to return to the example."
+         "</body></html>");
+end function responder1;
+
+define function prefix1 (#key)
+  output("<html><body>This is a prefix responder.  The part of the url after "
+         "the prefix was %s."
+         "<p>Use your browser's Back button to return to the example.</p>"
+         "</body></html>",
+         // Note that we could also use "#key match" above and then get the
+         // entire match with match-group(match, 0).
+         current-request().request-tail-url);
+end function prefix1;
 
 
 
 //// Page abstraction
 
 // Slightly higher level than responders.  Gives you the convenience of not
-// having to figure out whether it's a GET, POST, HEAD, request, and the ability
+// having to figure out whether it's a GET, POST, etc, request, and the ability
 // to dispatch on your own page classes.  Just define methods for
-// respond-to that dispatch on your page class.
+// respond-to* that dispatch on your page class.
 
 // Note that you may override respond-to-get and respond-to-post instead of
 // overriding respond-to(== #"get") and respond-to(== #"post") as a convenience
 // since these are by far the most common request methods.
 
-// This defines a <hello-world-page> class which is a subclass of <page>, and a
-// variable called *hello-world-page* which is an instance of
-// <hello-world-page>.  It associates the URLs /hello-world and /hello with the
-// *hello-world-page* instance.
-//
-define page hello-world-page (<page>)
-    (url: demo-url("/hello-world"),
-     alias: demo-url("/hello"))
+define class <hello-world-page> (<page>)
 end;
 
 // Respond to a GET for <hello-world-page>.  Note the use of do-query-values to
@@ -104,45 +78,45 @@
 //
 define method respond-to-get
     (page :: <hello-world-page>)
-  let stream :: <stream> = output-stream(current-response());
-  format(stream, "<html>\n<head><title>Hello World</title></head>\n"
-                 "<body>Hello there.<p>");
-  format(stream, "%s<br>", if (count-query-values() > 0)
-                             "Query values are:"
-                           else
-                             "No query values were passed in the URL."
-                           end);
+  output("<html>\n<head><title>Hello World</title></head>\n"
+         "<body>Hello there.<p>");
+  output("%s<br>", if (count-query-values() > 0)
+                     "Query values are:"
+                   else
+                     "No query values were passed in the URL."
+                   end);
   do-query-values(method (key, val)
-                    format(stream, "key = %s, val = %s<br>\n", key, val);
+                    output("key = %s, val = %s<br>\n", key, val);
                   end);
-  format(stream,
-         "<p>Use your browser's Back button to return to the "
+  output("<p>Use your browser's Back button to return to the "
          "demo.</body></html>");
-end;
+end method respond-to-get;
 
 
 //// Dylan Server Pages
 
-// Dylan Server Pages are also usually defined with the "define page"
-// macro, but you must also specify the source: argument which is a
-// file that contains normal content plus DSP tags.
+// A DSP is basically just a page that is associated with a top-level
+// template.  When creating a <dylan-server-page> you must pass the
+// source: "foo/my.dsp" argument.
 
 // In general you will define respond-to-get/post methods for your
 // <dylan-server-page>s that do some processing based on the query
-// values in the request and then call next-method() to process the
-// template for the page or call process-template on some other page
-// to display its output instead (e.g., an error template).
-
-// Any plain content is output directly to the output stream, and
-// tags invoke the corresponding tag definition.
-
-// Note that the .dsp source file doesn't have to be under the
-// *document-root* directory.
+// values in the request and then call process-template(page) to
+// process the template for the page or call process-template on
+// some other page to display its output instead (e.g., an error
+// template).
+
+// Any plain content in the template is output directly to the output
+// stream, and tags invoke the corresponding tag definition.
+
+// Note that the .dsp source file doesn't have to be under Koala's
+// document root directory.  (Does it need to be under the DSP root?
+// Check this. --cgay)
 
 // This defines a class that will be used for all our example pages.
 // It must be a subclass of <dylan-server-page> so that all the
 // template parsing will happen.  If we define all our tags to be
-// specialized on this class they can be used in any example page.
+// specialized on this class they can be used in any demo page.
 //
 define class <demo-page> (<dylan-server-page>)
 end;
@@ -155,38 +129,24 @@
 define taglib demo ()
 end;
 
+// <demo:base-url/> tag, so templates don't have to hard-code absolute urls.
+//
 define tag base-url in demo
     (page :: <demo-page>)
     ()
-  format(output-stream(current-response()), $demo-base-url);
-end;
-
-define page home-page (<demo-page>)
-    (url: demo-url("/home.dsp"),
-     alias: "/demo/",
-     source: "demo/home.dsp")
-end;
-
-define page hello-page (<demo-page>)
-    (url: demo-url("/hello.dsp"),
-     source: "demo/hello.dsp")
+  // output(format-string, format-arg, ...) sends output directly to the
+  // response stream.
+  output($demo-base-url);
 end;
 
 // Defines a tag that looks like <demo:hello/> in the DSP source
-// file.  i.e., it has no body.  Note that the "output" function is
-// defined by the "define tag" macro and could be defined as
-// curry(format, output-stream(current-response))
+// file.  i.e., it has no body.
 define tag hello in demo
     (page :: <demo-page>)
     ()
   output("Hello, world!");
 end;
 
-define page args-page (<demo-page>)
-    (url: demo-url("/args.dsp"),
-     source: "demo/args.dsp")
-end;
-
 // This tag demonstrates the use of tag keyword arguments.  The tag call looks
 // like this:  <demo:show-keys arg1="100" arg2="foo"/>
 // Note that since arg1 is typed as an <integer> it is automatically parsed to
@@ -196,45 +156,43 @@
 define tag show-keys in demo
     (page :: <demo-page>)
     (arg1 :: <integer>, arg2)
-  format(output-stream(current-response()),
-         "The value of arg1 + 1 is %=.  The value of arg2 is %=.",
+  output("The value of arg1 + 1 is %=.  The value of arg2 is %=.",
          arg1 + 1, arg2);
 end;
 
-
+// Named methods can be used in various control flow tags defined in the
+// "dsp" taglib.  For example, <dsp:if test="logged-in?">...</dsp:if>
+//
 define named-method logged-in? in demo
     (page :: <demo-page>)
   let session = get-session(current-request());
   session & get-attribute(session, #"username");
 end;
 
-define page example-login-page (<demo-page>)
-    (url: demo-url("/login.dsp"),
-     source: "demo/login.dsp")
+define class <login-page> (<demo-page>)
 end;
 
-define page example-logout-page (<demo-page>)
-    (url: demo-url("/logout.dsp"),
-     source: "demo/logout.dsp")
+define constant *login-page* = make(<login-page>, source: "demo/login.dsp");
+
+define class <logout-page> (<demo-page>)
 end;
 
 define method respond-to-get
-    (page :: <example-logout-page>)
+    (page :: <logout-page>)
   let session = get-session(current-request());
   remove-attribute(session, #"username");
   remove-attribute(session, #"password");
-  next-method();  // Must call this if you want the DSP template to be processed.
-end;
+  // Process the template for this page...
+  next-method();
+end method respond-to-get;
 
 // The login page POSTs to the welcome page...
-define page example-welcome-page (<demo-page>)
-    (url: demo-url("/welcome.dsp"),
-     source: "demo/welcome.dsp")
+define class <welcome-page> (<demo-page>)
 end;
 
 // ...so handle the POST by storing the form values in the session.
 define method respond-to-post
-    (page :: <example-welcome-page>)
+    (page :: <welcome-page>)
   let username = get-query-value("username");
   let password = get-query-value("password");
   let username-supplied? = username & username ~= "";
@@ -243,15 +201,11 @@
     let session = get-session(current-request());
     set-attribute(session, #"username", username);
     set-attribute(session, #"password", password);
-    // process the DSP template for the welcome page.
+    // Process the template for this page...
     next-method();
   else
     note-form-error("You must supply <b>both</b> a username and password.");
-    // ---*** TODO: Calling respond-to(#"get", ...) probably isn't quite right.
-    // If we're redirecting to another page should the query/form values
-    // be cleared first?  Probably want to call process-page instead,
-    // but with the existing request?
-    respond-to-get(*example-login-page*);
+    respond-to-get(*login-page*);
   end;
 end method respond-to-post;
 
@@ -264,17 +218,12 @@
   let username
     = get-query-value("username")
       | get-attribute(get-session(get-request(response)), #"username");
-  username & write(output-stream(response), username);
+  username & output(username);
 end;
 
 
 //// iterator
 
-define page iterator-page (<demo-page>)
-    (url: demo-url("/iterator.dsp"),
-     source: "demo/iterator.dsp")
-end;
-
 define thread variable *repetition-number* = 0;
 
 // An iterating tag.  Note the use of the "body" modifier in "define body tag".
@@ -301,21 +250,16 @@
 define tag display-iteration-number in demo
     (page :: <demo-page>)
     ()
-  format(output-stream(current-response()), "%d", *repetition-number*);
+  output("%d", *repetition-number*);
 end;
 
 
 //// table generation
 
-define page table-page (<demo-page>)
-    (url: demo-url("/table.dsp"),
-     source: "demo/table.dsp")
-end;
-
 // This method is used as the row-generator function for a dsp:table call.
 // It must return a <sequence>.
 define named-method animal-generator in demo
-    (page :: <table-page>)
+    (page :: <demo-page>)
   #[#["dog", "perro", "gou3"],
     #["cat", "gato", "mao1"],
     #["cow", "vaca", "niu2"]]
@@ -323,7 +267,7 @@
 
 // The row-generator for the table with no rows.
 define named-method no-rows-generator in demo
-    (page :: <table-page>)
+    (page :: <demo-page>)
   #[]
 end;
 
@@ -331,29 +275,60 @@
     (page :: <demo-page>)
     ()
   let row = current-row();
-  format(output-stream(current-response()), "%s", row[0]);
+  output("%s", row[0]);
 end;
 
 define tag spanish-word in demo
     (page :: <demo-page>)
     ()
   let row = current-row();
-  format(output-stream(current-response()), "%s", row[1]);
+  output("%s", row[1]);
 end;
 
 define tag pinyin-word in demo
     (page :: <demo-page>)
     ()
   let row = current-row();
-  format(output-stream(current-response()), "%s", row[2]);
+  output("%s", row[2]);
 end;
 
 define tag row-bgcolor in demo
     (page :: <demo-page>)
     ()
-  write(output-stream(current-response()),
-        if(even?(current-row-number())) "#EEEEEE" else "#FFFFFF" end);
-end;
+  output(if(even?(current-row-number()))
+           "#EEEEEE"
+         else
+           "#FFFFFF"
+         end);
+end;
+
+define url-map
+  //prefix "/demo";    ...would be nice
+  url ("/demo", "/demo/home")
+    // Note that when a page just has a template but no special respond-to*
+    // method there's no need to define a new page subclass for it.  You
+    // just make a <dylan-server-page> (or subclass thereof) and specify
+    // the source: for the template.
+    action GET () => make(<demo-page>, source: "demo/home.dsp");
+  url "/demo/responder1"
+    action GET () => responder1;       // regex defaults to "^$"
+  url "/demo/prefix1"
+    action GET (".*") => prefix1;
+  url ("/demo/hello-world", "/demo/hello")
+    action GET () => make(<hello-world-page>);
+  url "/demo/args"
+    action GET () => make(<demo-page>, source: "demo/args.dsp");
+  url "/demo/login"
+    action GET () => *login-page*;
+  url "/demo/logout"
+    action (GET, POST) () => make(<logout-page>, source: "demo/logout.dsp");
+  url "/demo/welcome"
+    action POST () => make(<welcome-page>, source: "demo/welcome.dsp");
+  url "/demo/iterator"
+    action GET () => make(<demo-page>, source: "demo/iterator.dsp");
+  url "/demo/table"
+    action GET () => make(<demo-page>, source: "demo/table.dsp");
+end url-map;
 
 
 

Modified: trunk/libraries/network/koala/sources/koala/dsp.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/dsp.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/dsp.dylan	Thu Feb 28 11:15:09 2008
@@ -80,6 +80,16 @@
 define open primary class <page> (<object>)
 end;
 
+// Method on generic defined in server.dylan
+define method invoke-responder
+    (request :: <request>,
+     action :: <page>,
+     arguments :: <sequence>)
+ => ()
+  // todo -- Decide how to pass arguments to the page respond-to* methods?
+  process-page(action)
+end method invoke-responder;
+  
 // This is the method registered as the response function for all <page>s.
 // See register-page.
 define method process-page (page :: <page>)
@@ -153,32 +163,6 @@
   end block;
 end function url-to-page;
 
-// ---TODO: Test this and export it.
-// Register URLs for all files matching the given pathname spec as instances
-// of the given page class.
-define method register-pages-as
-    (path :: <locator>, page-class :: subclass(<file-page-mixin>),
-     #key descend? = #t, file-type)
-  // url-dir always ends in '/'
-  local method doer (url-dir, directory, name, type)
-          select (type)
-            #"file" =>
-              let file = merge-locators(as(<file-locator>, name),
-                                        as(<directory-locator>, directory));
-              register-page(name, make(page-class,
-                                       source: file,
-                                       url: concatenate(url-dir, name)));
-            #"directory" =>
-              let dir = subdirectory-locator(as(<directory-locator>, directory), name);
-              when (descend?)
-                do-directory(curry(doer, concatenate(url-dir, name, "/")),
-                             dir);
-              end;
-          end;
-        end;
-  do-directory(curry(doer, "/"), path);
-end;
-
 //
 // Page mixin classes and related methods
 //
@@ -684,65 +668,22 @@
 // Dylan Server Pages
 //
 
+// The only required init arg for this class is source:, which should
+// be the location of the top-level template.
+//
 define open primary class <dylan-server-page> (<file-page-mixin>, <page>)
   // A sequence of strings and functions.  Strings are output directly
   // to the network stream.  The functions are created by 'define tag'.
-  each-subclass slot page-template :: false-or(<dsp-template>) = #f;
-end;
-
-// define page my-dsp (<dylan-server-page>) (url: "/hello", source: make-locator(...), ...)
-//   slot foo :: <integer> = bar;
-//   ...
-// end;
-define macro page-definer
-    { define page ?:name (?superclasses:*) (?make-args:*)
-        ?slot-specs:*
-      end }
- => { page-aux(?name; ?superclasses; ?make-args; ?slot-specs);
-      if (has-url?(?make-args))
-        register-page-urls("*" ## ?name ## "*", ?make-args)
-      end
-    }
-
-    { define directory page ?:name (?superclasses:*) (?make-args:*)
-        ?slot-specs:*
-      end }
- => { page-aux(?name; ?superclasses; ?make-args; ?slot-specs);
-      if (has-url?(?make-args))
-        register-page-urls("*" ## ?name ## "*", ?make-args, prefix?: #t)
-      end
-    }
-
-end;
-
-define macro page-aux
-  { page-aux(?:name; ?superclasses:*; ?make-args:*; ?slot-specs:*) }
-   => { define class "<" ## ?name ## ">" (?superclasses) ?slot-specs end;
-        define variable "*" ## ?name ## "*" = make("<" ## ?name ## ">", ?make-args) }
+  slot page-template :: false-or(<dsp-template>) = #f;
 end;
 
-define function has-url? (#key url :: false-or(<string>), #all-keys)
- => (url-provided? :: <boolean>);
-  if (url)
-    #t
-  end;
-end;
-
-define function register-page-urls
-    (page :: <page>, #key url :: <string>, alias, #all-keys)
- => (responder :: <function>)
-  let responder = register-page(url, page);
-  when (alias)
-    for (alias in iff(instance?(alias, <string>),
-                      list(alias),
-                      alias))
-      add-responder(alias, responder);
-    end;
-  end;
-  responder
+// For convenience in responders, tags, named-methods, etc.
+//
+define function output
+    (format-string, #rest format-args)
+  apply(format, output-stream(current-response()), format-string, format-args)
 end;
 
-
 // define tag foo in tlib (page) () do-stuff end
 // define body tag foo in tlib (page, do-body) (foo, bar :: <integer>) do-stuff end
 //
@@ -758,10 +699,7 @@
     end }
   => { define tag-aux #f ?tag ?taglib-spec
            (?page, _do-body) (?tag-parameters)
-         begin
-           let ?=output = curry(format, current-response().output-stream);
-           ?body;       // semicolon is needed even when ?body ends in semicolon.
-         end;
+         ?body;       // semicolon is needed even when ?body ends in semicolon.
          _do-body();  // process the tag body
        end
      }
@@ -771,10 +709,7 @@
     end }
   => { define tag-aux #t ?tag ?taglib-spec
            (?page, ?do-body) (?tag-parameters)
-         begin
-           let ?=output = curry(format, current-response().output-stream);
-           ?body;
-         end;
+         ?body
        end
      }
 
@@ -876,7 +811,8 @@
   display-template(page.page-template, page);
 end;
 
-define method display-template (tmplt :: <dsp-template>, page :: <dylan-server-page>)
+define method display-template
+    (tmplt :: <dsp-template>, page :: <dylan-server-page>)
   log-debug("Displaying template %s", tmplt.source | "not set");
   let stream = current-response().output-stream;
   for (item in tmplt.entries)

Modified: trunk/libraries/network/koala/sources/koala/headers.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/headers.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/headers.dylan	Thu Feb 28 11:15:09 2008
@@ -47,13 +47,13 @@
      #key if-exists? :: <symbol> = #"append")
   let old = element(headers, key, default: #f);
   // typically there is only one header for given key, so favor that.
-  if (~old | if-exists? == #"replace")
+  if (~old | if-exists? = #"replace")
     headers[key] := data;
-  elseif (if-exists? == #"append")
+  elseif (if-exists? = #"append")
     headers[key] := iff(instance?(old, <pair>),
                         concatenate!(old, list(data)),
                         list(old, data));
-  elseif (if-exists? == #"error")
+  elseif (if-exists? = #"error")
     error("Attempt to add header %= which has already been added", key);
   end;
 end add-header;

Modified: trunk/libraries/network/koala/sources/koala/library-unix.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/library-unix.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/library-unix.dylan	Thu Feb 28 11:15:09 2008
@@ -158,6 +158,7 @@
     add-responder,
     remove-responder,
     find-responder,
+    invoke-responder,
     <request>,
     *request*,                   // Holds the active request, per thread.
     current-request,             // Returns the active request of the thread.
@@ -193,6 +194,7 @@
     <response>,
     output-stream,
     clear-output,
+    output,
     set-content-type,
     add-header,
     add-cookie,
@@ -354,10 +356,9 @@
     page-template,
     page-template-setter,
 
-    <dylan-server-page>,         // Subclass this using the "define page" macro
-    page-definer,                // Defines a new page class
-    process-template,            // Call this (or next-method()) from respond-to-get/post if
-                                 //   you decide you want the DSP template to be processed.
+    <dylan-server-page>,
+    process-template,
+    process-page,
     <taglib>,
     taglib-definer,
     tag-definer,            // Defines a new DSP tag function and registers it with a page

Modified: trunk/libraries/network/koala/sources/koala/library.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/library.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/library.dylan	Thu Feb 28 11:15:09 2008
@@ -158,11 +158,11 @@
     add-responder,
     remove-responder,
     find-responder,
+    invoke-responder,
     <request>,
     *request*,                   // Holds the active request, per thread.
     current-request,             // Returns the active request of the thread.
     current-response,            // Returns the active response of the thread.
-    request-query-string,
     request-query-values,        // get the keys/vals from the current GET or POST request
     request-method,              // Returns #"get", #"post", etc
     request-host,
@@ -193,6 +193,7 @@
     <response>,
     output-stream,
     clear-output,
+    output,
     set-content-type,
     add-header,
     add-cookie,
@@ -271,10 +272,6 @@
     request-url,
     request-tail-url;
 
-  // Debugging
-  create
-    print-object;
-
   // Files
   create
     static-file-responder;
@@ -354,10 +351,9 @@
     page-template,
     page-template-setter,
 
-    <dylan-server-page>,         // Subclass this using the "define page" macro
-    page-definer,                // Defines a new page class
-    process-template,            // Call this (or next-method()) from respond-to-get/post if
-                                 //   you decide you want the DSP template to be processed.
+    <dylan-server-page>,
+    process-template,
+    process-page,
     <taglib>,
     taglib-definer,
     tag-definer,            // Defines a new DSP tag function and registers it with a page

Modified: trunk/libraries/network/koala/sources/koala/responders.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/responders.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/responders.dylan	Thu Feb 28 11:15:09 2008
@@ -123,10 +123,19 @@
 /* Example usage
 define url-map
   url "/wiki",
-    action GET () => show-page,
-    action GET () => show-page;
+    action get () => show-page,
+    action get () => show-page;
+  url "/wiki/login"
+    action post ("/(?<name>:\\w+") => login;
+  url
 end;
 */
+// It might be nice to add a prefix clause to this.  e.g.,
+//    prefix: "/demo"
+// so that all urls are prefixed with that string.  But that might
+// be better handled by "define web-application", which perhaps this
+// macro can be expanded to at some point.
+//
 define macro url-map-definer
   { define url-map
       ?urls
@@ -138,13 +147,13 @@
     { ?url ; ... } => { ?url ; ... }
 
   url:
-    { url ?location:expression , ?definitions }
+    { url ?location:expression ?definitions }
      => { begin
             let responder = make(<responder>);
             ?definitions ;
             ?location ;
           end }
-    { url ( ?locations ) , ?definitions }
+    { url ( ?locations ) ?definitions }
       => { begin
             let responder = make(<responder>);
             ?definitions ;
@@ -162,14 +171,16 @@
     { } => { }
     { ?definition , ... } => { ?definition ; ... }
 
+  // I'd like to get rid of the parens around ?request-methods.
+  // Not quite sure how yet though.  --cgay
   definition:
-    { action ( ?request-methods ) ( ?regex ) => ?action:name }
+    { action ( ?request-methods ) ( ?regex ) => ?action:expression }
       => { begin
              let regex = compile-regex(?regex, use-cache: #t);
              let actions = list(?action);
              ?request-methods
            end }
-    { action ?request-method:name ( ?regex ) => ?action:name }
+    { action ?request-method:name ( ?regex ) => ?action:expression }
       => { begin
              let regex = compile-regex(?regex);
              let actions = list(?action);
@@ -177,7 +188,7 @@
            end }
     { action ( ?request-methods ) ( ?regex ) => ( ?action-sequence:* ) }
       => { begin
-             let regex = compile-regex(?regex);
+             let regex = compile-regex(?regex, use-cache: #t);
              let actions = list(?action-sequence);
              ?request-methods
            end }
@@ -187,7 +198,6 @@
              let actions = list(?action-sequence);
              ?request-method
            end }
-
   request-methods:
     { } => { }
     { ?request-method , ...  } => { ?request-method ; ... }

Modified: trunk/libraries/network/koala/sources/koala/response.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/response.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/response.dylan	Thu Feb 28 11:15:09 2008
@@ -66,7 +66,7 @@
       response.%output-stream := make(<string-stream>, direction: #"output");
     else
       signal(make(<koala-error>,
-                  format-string: "unbuffered responses aren't supported yet."));
+                  format-string: "Unbuffered responses aren't supported yet."));
     end
 end;
 
@@ -81,11 +81,13 @@
 // Exported
 //
 define method set-content-type
-    (response :: <response>, content-type :: <object>, #key if-exists? = #"replace")
+    (response :: <response>, content-type :: <object>,
+     #key if-exists? = #"replace")
   let out = response.%output-stream;
   if (out & stream-size(out) ~= 0)
     raise(<koala-api-error>,
-          "Attempt to set the Content-Type header after reply has begun to be sent.");
+          "Attempt to set the Content-Type header after some content "
+          "has already been generated.");
   else
     add-header(response.response-headers, "Content-Type", content-type,
                if-exists?: if-exists?);

Modified: trunk/libraries/network/koala/sources/koala/server.dylan
==============================================================================
--- trunk/libraries/network/koala/sources/koala/server.dylan	(original)
+++ trunk/libraries/network/koala/sources/koala/server.dylan	Thu Feb 28 11:15:09 2008
@@ -147,23 +147,20 @@
     required-init-keyword: client:;
 end;
 
-define generic read-request (request :: <basic-request>);
-define generic invoke-handler (request :: <basic-request>);
-
-define generic request-keep-alive? (request :: <basic-request>) => (well? :: <boolean>);
-
 define method initialize (request :: <basic-request>, #key, #all-keys)
   next-method();
   request.request-client.client-request := request;
 end;
 
-define inline function request-socket (request :: <basic-request>)
-    => (socket :: <tcp-socket>)
+define inline function request-socket
+    (request :: <basic-request>)
+ => (socket :: <tcp-socket>)
   request.request-client.client-socket
 end;
 
-define inline function request-server (request :: <basic-request>)
-    => (server :: <server>)
+define inline function request-server
+    (request :: <basic-request>)
+ => (server :: <server>)
   request.request-client.client-server
 end;
 
@@ -872,11 +869,14 @@
      if (actions)
        // Invoke each action function with keyword arguments matching the names
        // of the named groups in the first regular expression that matches the
-       // tail of the url, if any.
+       // tail of the url, if any.  Also pass the entire match as the match:
+       // argument so unnamed groups and the entire match can be accessed.
        let arguments = #[];
        if (match)
-         arguments := make(<simple-object-vector>,
-                           size: 2 * match.groups-by-name.size);
+         arguments := make(<stretchy-vector>,
+                           size: 2 * (match.groups-by-name.size + 1));
+         add!(arguments, #"match");
+         add!(arguments, match);
          for (group keyed-by name in match.groups-by-name)
            if (group)
              add!(arguments, as(<symbol>, name));
@@ -885,7 +885,7 @@
          end for;
        end if;
        for (action in actions)
-         invoke-action(request, action, arguments)
+         invoke-responder(request, action, arguments)
        end;
      else
        resource-not-found-error(url: url);
@@ -901,7 +901,7 @@
 
 define inline function find-actions
     (request :: <request>)
- => (actions, match :: false-or(<regex-match>))
+ => (actions, match)
   let rmap = request.request-responder.responder-map;
   let responders = element(rmap, request.request-method, default: #f);
   if (responders)
@@ -911,6 +911,7 @@
       for (actions keyed-by regex in responders)
         log-debug("regex -> actions:  %= -> %=", regex.regex-pattern, actions);
         let match = regex-search(regex, url-tail);
+        log-debug("find-actions: match: %=", match);
         if (match)
           return(actions, match)
         end if;
@@ -919,13 +920,15 @@
   end if;
 end function find-actions;
 
-define generic invoke-action
+// Clients can override this to create other types of responders.
+// 
+define open generic invoke-responder
     (request :: <request>,
      action :: <object>,
      arguments :: <sequence>)
  => ();
 
-define method invoke-action
+define method invoke-responder
     (request :: <request>,
      action :: <object>,
      arguments :: <sequence>)
@@ -933,15 +936,7 @@
   log-warning("Unknown action %= in action sequence.", action);
 end;
 
-define method invoke-action
-    (request :: <request>,
-     action :: <dylan-server-page>,
-     arguments :: <sequence>)
- => ()
-  respond-to(request.request-method, action);
-end;
-  
-define method invoke-action
+define method invoke-responder
     (request :: <request>,
      action :: <function>,
      arguments :: <sequence>)

Modified: trunk/libraries/network/koala/www/demo/footer.dsp
==============================================================================
--- trunk/libraries/network/koala/www/demo/footer.dsp	(original)
+++ trunk/libraries/network/koala/www/demo/footer.dsp	Thu Feb 28 11:15:09 2008
@@ -13,7 +13,7 @@
       </dsp:if>
     </td>
     <td width="50%" align="right">
-      <a href="<demo:base-url/>/home.dsp">Go back to demo home</a>
+      <a href="<demo:base-url/>/home">Go back to demo home</a>
     </td>
   </tr>
 </table>

Modified: trunk/libraries/network/koala/www/demo/home.dsp
==============================================================================
--- trunk/libraries/network/koala/www/demo/home.dsp	(original)
+++ trunk/libraries/network/koala/www/demo/home.dsp	Thu Feb 28 11:15:09 2008
@@ -21,22 +21,24 @@
   <h3>Contents</h3>
 
   <ol>
-    <li><a href="<demo:base-url/>/hello.dsp">Hello World</a></li>
-    <li><a href="<demo:base-url/>/args.dsp">A tag with arguments</a></li>
-    <li><a href="<demo:base-url/>/login.dsp">Login (demonstrates sessions)</a></li>
-    <li><a href="<demo:base-url/>/logout.dsp">Logout (demonstrates sessions)</a></li>
-    <li><a href="<demo:base-url/>/iterator.dsp?n=3">Iterator (demonstrates query
+    <li><a href="<demo:base-url/>/hello">Hello World</a></li>
+    <li><a href="<demo:base-url/>/args">A tag with arguments</a></li>
+    <li><a href="<demo:base-url/>/login">Login (demonstrates sessions)</a></li>
+    <li><a href="<demo:base-url/>/logout">Logout (demonstrates sessions)</a></li>
+    <li><a href="<demo:base-url/>/iterator?n=3">Iterator (demonstrates query
              values and body tags)</a></li>
-    <li><a href="<demo:base-url/>/table.dsp">Table Generation</a></li>
+    <li><a href="<demo:base-url/>/table">Table Generation</a></li>
   </ol>
 
   <p>
   <h3>Low-level Koala API</h3>
-  
+
   <ol>
     <li><a href="<demo:base-url/>/responder1">
 	  A responder (the most basic way to respond to a URL)
 	</a></li>
+    <li><a href="<demo:base-url/>/prefix1/one/two/three">A prefix responder.
+        Same as above, but matches any url starting with /prefix1.</a></li>
     <li><a href="<demo:base-url/>/hello?a=1&b=2">Hello World (a non-DSP page)</a></li>
   </ol>
 

Modified: trunk/libraries/network/koala/www/demo/login.dsp
==============================================================================
--- trunk/libraries/network/koala/www/demo/login.dsp	(original)
+++ trunk/libraries/network/koala/www/demo/login.dsp	Thu Feb 28 11:15:09 2008
@@ -12,7 +12,7 @@
 
   <dsp:show-form-notes/>
 
-  <form action="<demo:base-url/>/welcome.dsp"
+  <form action="<demo:base-url/>/welcome"
         method="post"
         enctype="application/x-www-form-urlencoded">
 



More information about the chatter mailing list