Adding exampels to docs
svn: r12182
This commit is contained in:
parent
a6b7f4ba16
commit
4e7aa80828
|
@ -11,7 +11,8 @@ There are a number of ways to run the Web Server. They are given in order of sim
|
|||
@; ------------------------------------------------------------
|
||||
@section[#:tag "insta"]{Instant Servlets}
|
||||
@(require (for-label (only-in web-server/insta/insta
|
||||
no-web-browser static-files-path)))
|
||||
no-web-browser static-files-path)
|
||||
web-server/servlet-env))
|
||||
@defmodulelang[web-server/insta]
|
||||
|
||||
The fastest way to get a servlet running in the Web server is to use the
|
||||
|
@ -104,6 +105,7 @@ from a given path:
|
|||
@(require (for-label web-server/dispatchers/filesystem-map)
|
||||
(for-label web-server/web-config-unit)
|
||||
(for-label web-server/web-config-sig)
|
||||
(for-label web-server/configuration/configuration-table)
|
||||
(prefix-in files: (for-label web-server/dispatchers/dispatch-files)))
|
||||
|
||||
@schemeblock[
|
||||
|
@ -143,7 +145,13 @@ from a given path:
|
|||
(-> void)]{
|
||||
Starts the @web-server with the settings defined by the given @scheme[web-config^] unit.
|
||||
|
||||
It is very useful to combine this with @scheme[configuration-table->web-config@] and @scheme[configuration-table-sexpr->web-config@].
|
||||
It is very useful to combine this with @scheme[configuration-table->web-config@] and @scheme[configuration-table-sexpr->web-config@]:
|
||||
|
||||
@schemeblock[
|
||||
(serve/web-config@
|
||||
(configuration-table->web-config@
|
||||
default-configuration-table-path))
|
||||
(do-not-return)]
|
||||
}
|
||||
|
||||
@defproc[(do-not-return) void]{
|
||||
|
|
|
@ -45,25 +45,82 @@ A @defterm{servlet} is a module that provides the following:
|
|||
This function is called when an instance of this servlet is started.
|
||||
The argument is the HTTP request that initiated the instance.
|
||||
}
|
||||
|
||||
An example version 1 module:
|
||||
@schememod[
|
||||
scheme
|
||||
|
||||
(define interface-version 'v1)
|
||||
(define timeout (* 60 60 24))
|
||||
(define (start req)
|
||||
`(html (head (title "Hello World!"))
|
||||
(body (h1 "Hi Mom!"))))
|
||||
]
|
||||
|
||||
An example version 2 module:
|
||||
@(require (for-label web-server/managers/none))
|
||||
@schememod[
|
||||
scheme
|
||||
(require web-server/managers/none)
|
||||
|
||||
(define interface-version 'v2)
|
||||
(define manager
|
||||
(create-none-manager
|
||||
(lambda (req)
|
||||
`(html (head (title "No Continuations Here!"))
|
||||
(body (h1 "No Continuations Here!"))))))
|
||||
(define (start req)
|
||||
`(html (head (title "Hello World!"))
|
||||
(body (h1 "Hi Mom!"))))
|
||||
]
|
||||
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "servlet-structs.ss"]{Contracts}
|
||||
@(require (for-label web-server/servlet/servlet-structs))
|
||||
@(require (for-label web-server/servlet/servlet-structs
|
||||
web-server/servlet))
|
||||
|
||||
@defmodule[web-server/servlet/servlet-structs]
|
||||
|
||||
@filepath{servlet/servlet-structs.ss} provides a number of contracts
|
||||
for use in servlets.
|
||||
|
||||
@defthing[k-url? contract?]{Equivalent to @scheme[string?].}
|
||||
@defthing[k-url? contract?]{
|
||||
Equivalent to @scheme[string?].
|
||||
|
||||
Example: @scheme["http://localhost:8080/servlets;1*1*20131636/examples/add.ss"]}
|
||||
|
||||
@defthing[response-generator? contract?]{Equivalent to @scheme[(k-url? . -> . response?)].}
|
||||
@defthing[response-generator? contract?]{
|
||||
Equivalent to @scheme[(k-url? . -> . response?)].
|
||||
|
||||
Example: @schemeblock[(lambda (k-url)
|
||||
`(html
|
||||
(body
|
||||
(a ([href ,k-url])
|
||||
"Click Me to Invoke the Continuation!"))))]
|
||||
}
|
||||
|
||||
@defthing[url-transform? contract?]{Equivalent to @scheme[(k-url? . -> . k-url?)].}
|
||||
@defthing[url-transform? contract?]{
|
||||
Equivalent to @scheme[(k-url? . -> . k-url?)].
|
||||
|
||||
Example: @scheme[(lambda (k-url) (regexp-replace "^/" k-url "/servlets/"))]
|
||||
}
|
||||
|
||||
@defthing[expiration-handler/c contract?]{Equivalent to @scheme[(or/c false/c (request? . -> . response?))].}
|
||||
@defthing[expiration-handler/c contract?]{
|
||||
Equivalent to @scheme[(or/c false/c (request? . -> . response?))].
|
||||
|
||||
Example: @schemeblock[(lambda (req)
|
||||
`(html (head (title "Expired"))
|
||||
(body (h1 "Expired")
|
||||
(p "This URL has expired. "
|
||||
"Please return to the home page."))))]
|
||||
}
|
||||
|
||||
@defthing[embed/url/c contract?]{Equivalent to @scheme[(((request? . -> . any/c)) (expiration-handler/c) . opt-> . string?)].}
|
||||
@defthing[embed/url/c contract?]{
|
||||
Equivalent to @scheme[(((request? . -> . any/c)) (expiration-handler/c) . opt-> . string?)].
|
||||
|
||||
This is what @scheme[send/suspend/dispatch] gives to its function argument.
|
||||
}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "request-structs.ss"]{HTTP Requests}
|
||||
|
@ -89,6 +146,8 @@ related to HTTP request data structures.
|
|||
@defproc[(headers-assq* [id bytes?] [heads (listof header?)])
|
||||
(or/c false/c header?)]{
|
||||
Returns the header with a field case-insensitively equal to @scheme[id] from @scheme[heads] or @scheme[#f].
|
||||
|
||||
You almost @bold{always} want to use this, rather than @scheme[headers-assq] because Web browsers may send headers with arbitrary casing.
|
||||
}
|
||||
|
||||
@defstruct[binding ([id bytes?])]{Represents a binding of @scheme[id].}
|
||||
|
@ -121,7 +180,24 @@ related to HTTP request data structures.
|
|||
to the server at @scheme[host-ip]:@scheme[host-port] with @scheme[headers/raw]
|
||||
headers, @scheme[bindings/raw] GET and POST queries and @scheme[post-data/raw]
|
||||
POST data.
|
||||
|
||||
You are @bold{unlikely to need to construct} a request struct.
|
||||
}
|
||||
|
||||
Here is an example typical of what you will find in many applications:
|
||||
@schemeblock[
|
||||
(define (get-number req)
|
||||
(match
|
||||
(bindings-assq
|
||||
#"number"
|
||||
(request-bindings/raw req))
|
||||
[(? binding:form? b)
|
||||
(string->number
|
||||
(bytes->string/utf-8
|
||||
(binding:form-value b)))]
|
||||
[_
|
||||
(get-number (request-number))]))
|
||||
]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "bindings.ss"]{Request Bindings}
|
||||
|
@ -132,6 +208,15 @@ related to HTTP request data structures.
|
|||
@filepath{servlet/bindings.ss} provides a number of helper functions
|
||||
for accessing request bindings.
|
||||
|
||||
These functions, while convenient, could introduce subtle bugs into your
|
||||
application. Examples: that they are case-insensitive could introduce
|
||||
a bug; if the data submitted is not in UTF-8 format, then the conversion
|
||||
to a string will fail; if an attacker submits a form field as if it were
|
||||
a file, when it is not, then the @scheme[request-bindings] will hold a
|
||||
@scheme[bytes?] object and your program will error; and, for file uploads
|
||||
you lose the filename. @bold{Therefore, we recommend against their use, but
|
||||
they are provided for compatibility with old code.}
|
||||
|
||||
@defproc[(request-bindings [req request?])
|
||||
(listof (or/c (cons/c symbol? string?)
|
||||
(cons/c symbol? bytes?)))]{
|
||||
|
@ -167,14 +252,15 @@ for accessing request bindings.
|
|||
Returns @scheme[#t] if @scheme[binds] contains a binding for @scheme[id].
|
||||
Otherwise, @scheme[#f].
|
||||
}
|
||||
|
||||
These functions, while convenient, could introduce subtle bugs into your
|
||||
application. Examples: that they are case-insensitive could introduce
|
||||
a bug; if the data submitted is not in UTF-8 format, then the conversion
|
||||
to a string will fail; if an attacked submits a form field as if it were
|
||||
a file, when it is not, then the @scheme[request-bindings] will hold a
|
||||
@scheme[bytes?] object and your program will error; and, for file uploads
|
||||
you lose the filename.
|
||||
|
||||
Here is an example typical of what you will find in many applications:
|
||||
@schemeblock[
|
||||
(define (get-number req)
|
||||
(string->number
|
||||
(extract-binding/single
|
||||
'number
|
||||
(request-bindings req))))
|
||||
]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "response-structs.ss"]{HTTP Responses}
|
||||
|
@ -196,6 +282,15 @@ HTTP responses.
|
|||
@scheme[message] the message, @scheme[seconds] the generation time, @scheme[mime]
|
||||
the MIME type of the file, and @scheme[extras] are the extra headers, in addition
|
||||
to those produced by the server.
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(make-response/basic
|
||||
301 "Moved Permanently"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-header #"Location"
|
||||
#"http://www.plt-scheme.org/downloads")))
|
||||
]
|
||||
}
|
||||
|
||||
@; XXX Rename string? option
|
||||
|
@ -203,6 +298,20 @@ HTTP responses.
|
|||
([body (listof (or/c string? bytes?))])]{
|
||||
As with @scheme[response/basic], except with @scheme[body] as the response
|
||||
body.
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(make-response/full
|
||||
301 "Moved Permanently"
|
||||
(current-seconds) TEXT/HTML-MIME-TYPE
|
||||
(list (make-header #"Location"
|
||||
#"http://www.plt-scheme.org/downloads"))
|
||||
(list #"<html><body><p>"
|
||||
#"Please go to <a href=\""
|
||||
#"http://www.plt-scheme.org/downloads"
|
||||
#"\">here</a> instead."
|
||||
#"</p></body></html>"))
|
||||
]
|
||||
}
|
||||
|
||||
@defstruct[(response/incremental response/basic)
|
||||
|
@ -222,7 +331,7 @@ HTTP responses.
|
|||
(send/bytes #"Some content")
|
||||
(send/bytes)
|
||||
(send/bytes #"Even" #"more" #"content!")
|
||||
(send/bytes "No we're done")))
|
||||
(send/bytes "Now we're done")))
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -238,8 +347,8 @@ HTTP responses.
|
|||
|
||||
@defthing[TEXT/HTML-MIME-TYPE bytes?]{Equivalent to @scheme[#"text/html; charset=utf-8"].}
|
||||
|
||||
@warning{If you include a Content-Length header in a response that is inaccurate, there WILL be an error in
|
||||
transmission that the server will not catch.}
|
||||
@warning{If you include a Content-Length header in a response that is inaccurate, there @bold{will be an error} in
|
||||
transmission that the server @bold{will not catch}.}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "web.ss"]{Web}
|
||||
|
@ -251,12 +360,32 @@ functions of interest for the servlet developer.}
|
|||
|
||||
@defproc[(send/back [response response?])
|
||||
void?]{
|
||||
Sends @scheme[response] to the client.
|
||||
Sends @scheme[response] to the client. No continuation is captured, so the servlet is done.
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(send/back
|
||||
`(html
|
||||
(body
|
||||
(h1 "The sum is: "
|
||||
,(+ first-number
|
||||
second-number)))))
|
||||
]
|
||||
}
|
||||
|
||||
@defthing[current-servlet-continuation-expiration-handler (parameter/c expiration-handler/c)]{
|
||||
Holds the @scheme[expiration-handler/c] to be used when a continuation
|
||||
captured in this context is expired, then looked up.
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(parameterize
|
||||
([current-servlet-continuation-expiration-handler
|
||||
(lambda (req)
|
||||
`(html (head (title "Custom Expiration!"))))])
|
||||
(send/suspend
|
||||
...))
|
||||
]
|
||||
}
|
||||
|
||||
@defproc[(send/suspend [make-response response-generator?]
|
||||
|
@ -267,6 +396,110 @@ functions of interest for the servlet developer.}
|
|||
is expected to generate a @scheme[response?], which is sent to the client. If the
|
||||
continuation URL is invoked, the captured continuation is invoked and the request is
|
||||
returned from this call to @scheme[send/suspend].
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(send/suspend
|
||||
(lambda (k-url)
|
||||
`(html (head (title "Enter a number"))
|
||||
(body
|
||||
(form ([action ,k-url])
|
||||
"Enter a number: "
|
||||
(input ([name "number"]))
|
||||
(input ([type "submit"])))))))
|
||||
]
|
||||
|
||||
When this form is submitted by the browser, the request will be sent to the URL generated by @scheme[send/suspend].
|
||||
Thus, the request will be ``returned'' from @scheme[send/suspend] to the continuation of this call.
|
||||
}
|
||||
|
||||
@defproc[(send/suspend/dispatch [make-response (embed/url/c . -> . response?)])
|
||||
any/c]{
|
||||
Calls @scheme[make-response] with a function that, when called with a procedure from
|
||||
@scheme[request?] to @scheme[any/c] will generate a URL, that when invoked will call
|
||||
the function with the @scheme[request?] object and return the result to the caller of
|
||||
@scheme[send/suspend/dispatch].
|
||||
|
||||
Use @scheme[send/suspend/dispatch] when there are multiple `logical' continuations of a page.
|
||||
For example, we could either add to a number or subtract from it:
|
||||
@schemeblock[
|
||||
(define (count-dot-com i)
|
||||
(count-dot-com
|
||||
(send/suspend/dispatch
|
||||
(lambda (embed/url)
|
||||
`(html
|
||||
(head (title "Count!"))
|
||||
(body
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(sub1 i)))])
|
||||
"-"))
|
||||
(h1 ,(number->string i))
|
||||
(h2 (a ([href
|
||||
,(embed/url
|
||||
(lambda (req)
|
||||
(add1 i)))])
|
||||
"+"))))))))
|
||||
]
|
||||
It is very common that the return value of @scheme[send/suspend/dispatch] is irrevelant in
|
||||
your application and you may think of it as ``embedding'' value-less callbacks.
|
||||
}
|
||||
|
||||
@defproc[(clear-continuation-table!)
|
||||
void?]{
|
||||
Calls the servlet's manager's @scheme[clear-continuation-table!] function. Normally, this deletes all the previously
|
||||
captured continuations.
|
||||
}
|
||||
|
||||
@defproc[(send/forward [make-response response-generator?]
|
||||
[exp expiration-handler/c (current-servlet-continuation-expiration-handler)])
|
||||
request?]{
|
||||
Calls @scheme[clear-continuation-table!], then @scheme[send/suspend].
|
||||
|
||||
Use this if the user can logically go `forward' in your application, but cannot go backward.
|
||||
}
|
||||
|
||||
@defproc[(send/finish [response response?])
|
||||
void?]{
|
||||
Calls @scheme[clear-continuation-table!], then @scheme[send/back].
|
||||
|
||||
Use this if the user is truly `done' with your application. For example, it may be used to display the post-logout page:
|
||||
@schemeblock[
|
||||
(send/finish
|
||||
`(html (head (title "Logged out"))
|
||||
(body (p "Thank you for using the services "
|
||||
"of the Add Two Numbers, Inc."))))
|
||||
]
|
||||
}
|
||||
|
||||
@defproc[(redirect/get)
|
||||
request?]{
|
||||
Calls @scheme[send/suspend] with @scheme[redirect-to].
|
||||
|
||||
This implements the Post-Redirect-Get pattern.
|
||||
Use this to prevent the @onscreen["Refresh"] button from duplicating effects, such as adding items to a database.
|
||||
}
|
||||
|
||||
@defproc[(redirect/get/forget)
|
||||
request?]{
|
||||
Calls @scheme[send/forward] with @scheme[redirect-to].
|
||||
}
|
||||
|
||||
@; XXX Move
|
||||
@defproc[(adjust-timeout! [t number?])
|
||||
void?]{
|
||||
Calls the servlet's manager's @scheme[adjust-timeout!] function.
|
||||
|
||||
@warning{This is deprecated and will be removed in a future release.}
|
||||
}
|
||||
|
||||
@; XXX Remove
|
||||
@defthing[current-url-transform (parameter/c url-transform?)]{
|
||||
Holds a @scheme[url-transform?] function that is called by
|
||||
@scheme[send/suspend] to transform the URLs it generates.
|
||||
|
||||
@warning{This is deprecated and will be removed in a future release.}
|
||||
}
|
||||
|
||||
@defproc[(continuation-url? [u url?])
|
||||
|
@ -274,47 +507,7 @@ functions of interest for the servlet developer.}
|
|||
Checks if @scheme[u] is a URL that refers to a continuation, if so
|
||||
returns the instance id, continuation id, and nonce.
|
||||
}
|
||||
|
||||
@; XXX Move
|
||||
@defproc[(adjust-timeout! [t number?])
|
||||
void?]{
|
||||
Calls the servlet's manager's @scheme[adjust-timeout!] function.
|
||||
}
|
||||
|
||||
@defproc[(clear-continuation-table!)
|
||||
void?]{
|
||||
Calls the servlet's manager's @scheme[clear-continuation-table!] function.
|
||||
}
|
||||
|
||||
@defproc[(send/forward [make-response response-generator?]
|
||||
[exp expiration-handler/c (current-servlet-continuation-expiration-handler)])
|
||||
request?]{
|
||||
Calls @scheme[clear-continuation-table!], then @scheme[send/suspend].
|
||||
}
|
||||
|
||||
@defproc[(send/finish [response response?])
|
||||
void?]{
|
||||
Calls @scheme[clear-continuation-table!], then @scheme[send/back].
|
||||
}
|
||||
|
||||
@defproc[(send/suspend/dispatch [make-response (embed/url/c . -> . response?)])
|
||||
any/c]{
|
||||
Calls @scheme[make-response] with a function that, when called with a procedure from
|
||||
@scheme[request?] to @scheme[any/c] will generate a URL, that when invoked will call
|
||||
the function with the @scheme[request?] object and return the result to the caller of
|
||||
@scheme[send/suspend/dispatch].
|
||||
}
|
||||
|
||||
@defproc[(redirect/get)
|
||||
request?]{
|
||||
Calls @scheme[send/suspend] with @scheme[redirect-to].
|
||||
}
|
||||
|
||||
@defproc[(redirect/get/forget)
|
||||
request?]{
|
||||
Calls @scheme[send/forward] with @scheme[redirect-to].
|
||||
}
|
||||
|
||||
|
||||
@; XXX Remove
|
||||
@defproc[(embed-ids [ids (list/c number? number? number?)]
|
||||
[u url?])
|
||||
|
@ -322,12 +515,6 @@ functions of interest for the servlet developer.}
|
|||
Creates a @scheme[continuation-url?].
|
||||
}
|
||||
|
||||
@; XXX Remove
|
||||
@defthing[current-url-transform (parameter/c url-transform?)]{
|
||||
Holds a @scheme[url-transform?] function that is called by
|
||||
@scheme[send/suspend] to transform the URLs it generates.
|
||||
}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "helpers.ss"]{Helpers}
|
||||
@(require (for-label web-server/servlet/helpers))
|
||||
|
@ -344,6 +531,9 @@ functions of interest for the servlet developer.}
|
|||
response?]{
|
||||
Generates an HTTP response that redirects the browser to @scheme[uri],
|
||||
while including the @scheme[headers] in the response.
|
||||
|
||||
Example:
|
||||
@scheme[(redirect-to "http://www.add-three-numbers.com" permanently)]
|
||||
}
|
||||
|
||||
@defproc[(redirection-status? [v any/c])
|
||||
|
@ -362,6 +552,14 @@ functions of interest for the servlet developer.}
|
|||
any]{
|
||||
Calls @scheme[thunk] with an exception handler that generates an HTML error page
|
||||
and calls @scheme[send/finish-or-back].
|
||||
|
||||
Example:
|
||||
@schemeblock[
|
||||
(with-errors-to-browser
|
||||
send/back
|
||||
(lambda ()
|
||||
(/ 1 (get-number (request-number)))))
|
||||
]
|
||||
}
|
||||
|
||||
@; XXX Depreciate
|
||||
|
@ -380,7 +578,7 @@ They may eventually provided by another module.
|
|||
@defproc[(servlet-url->url-string/no-continuation [su servlet-url?])
|
||||
string?]{
|
||||
Returns a URL string without the continuation information in the URL
|
||||
that went into @scheme[su]
|
||||
that went into @scheme[su].
|
||||
}
|
||||
|
||||
@; XXX Support Digest
|
||||
|
@ -396,7 +594,10 @@ implementation of HTTP Basic Authentication.
|
|||
@defproc[(extract-user-pass [heads (listof header?)])
|
||||
(or/c false/c (cons/c bytes? bytes?))]{
|
||||
Returns a pair of the username and password from the authentication
|
||||
header in @scheme[heads] if they are present, or @scheme[#f]
|
||||
header in @scheme[heads] if they are present, or @scheme[#f].
|
||||
|
||||
Example:
|
||||
@scheme[(extract-user-pass (request-headers/raw req))] might return @scheme[(cons #"aladin" #"open sesame")].
|
||||
}
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
|
@ -405,14 +606,14 @@ implementation of HTTP Basic Authentication.
|
|||
|
||||
@defmodule[web-server/servlet/web-cells]{The
|
||||
@schememodname[web-server/servlet/web-cells] library provides the
|
||||
interface to web cells.}
|
||||
interface to Web cells.}
|
||||
|
||||
A web cell is a kind of state defined relative to the @defterm{frame tree}.
|
||||
A Web cell is a kind of state defined relative to the @defterm{frame tree}.
|
||||
The frame-tree is a mirror of the user's browsing session. Every time a
|
||||
continuation is invoked, a new frame (called the @defterm{current frame}) is
|
||||
created as a child of the current frame when the continuation was captured.
|
||||
|
||||
You should use web cells if you want an effect to be encapsulated in all
|
||||
You should use Web cells if you want an effect to be encapsulated in all
|
||||
interactions linked from (in a transitive sense) the HTTP response being
|
||||
generated. For more information on their semantics, consult the paper
|
||||
@href-link["http://www.cs.brown.edu/~sk/Publications/Papers/Published/mk-int-safe-state-web/"
|
||||
|
@ -440,3 +641,44 @@ generated. For more information on their semantics, consult the paper
|
|||
Binds @scheme[wc] to @scheme[v] in the current frame, shadowing any
|
||||
other bindings to @scheme[wc] in the current frame.
|
||||
}
|
||||
|
||||
Below is an extended example that demonstrates how Web cells allow
|
||||
the creation of reusable Web abstractions without requiring global
|
||||
transformations of the program into continuation or store passing style.
|
||||
@schememod[
|
||||
web-server/insta
|
||||
|
||||
(define (start initial-request)
|
||||
(define counter1 (make-counter))
|
||||
(define counter2 (make-counter))
|
||||
(define include1 (include-counter counter1))
|
||||
(define include2 (include-counter counter2))
|
||||
(send/suspend/dispatch
|
||||
(lambda (embed/url)
|
||||
`(html
|
||||
(body (h2 "Double Counters")
|
||||
(div (h3 "First")
|
||||
,(include1 embed/url))
|
||||
(div (h3 "Second")
|
||||
,(include2 embed/url)))))))
|
||||
|
||||
(define (make-counter)
|
||||
(make-web-cell 0))
|
||||
|
||||
(define (include-counter a-counter)
|
||||
(let/cc k
|
||||
(let loop ()
|
||||
(k
|
||||
(lambda (embed/url)
|
||||
`(div (h3 ,(number->string (web-cell-ref a-counter)))
|
||||
(a ([href
|
||||
,(embed/url
|
||||
(lambda _
|
||||
@code:comment{A new frame has been created}
|
||||
(define last (web-cell-ref a-counter))
|
||||
@code:comment{We can inspect the value at the parent}
|
||||
(web-cell-shadow a-counter (add1 last))
|
||||
@code:comment{The new frame has been modified}
|
||||
(loop)))])
|
||||
"+")))))))
|
||||
]
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
; XXX Format better
|
||||
(define (warning . x)
|
||||
(apply elem "Warning:" x))
|
||||
(apply elem (bold "Warning: ") x))
|
||||
|
||||
; XXX Actually display link
|
||||
(define (href-link url label)
|
||||
|
|
Loading…
Reference in New Issue
Block a user