The following are path related procedures:
normalize-path
: convert a path to the underlying system path, depends onpath->segments
andshp-handler-path
url->path-segments
: converts the url into path segments; overlapping partly withpath->segments
normalize-partial-path
: takes the segments and convert into an underlying path; depends onshp-handler-path
segments->path
: converts the segments back to the underling path; callsnormalize-partial-path
url->shp-path
: wraps aroundurl-path->segments
andsegments->path
eval-script-if-changed
: evaluate the script if it is changed; takes in script structshp-handler
: evaluate the script from top level, takes in nothingenvoke-topfilter-if-available
: call the top filter if available - takes in a procedureinclude!
- calls the code, takes in a path
First -
url-path->segments
can be refactored to:
;; url->path-segments - returns an underlying path based on input url (duplicate with normalize-path)
(define (url->path-segments url (default "index.shp"))
(filter (lambda (path)
(not (equal? path "")))
(map path/param-path (url-path url))))
But now
url->path-segments
looks a lot like path->segments
, so the two can be refactored:
(define (path->segments path)
(filter (lambda (path)
(not (equal? path "")))
(if (url? path)
(map path/param-path (url-path path))
(regexp-split #px"\\/" path))))
(define (url->shp-path url)
(segments->path (path->segments url) #t))
Change the name of
normalize-partial-path
to segments->partial-path
to more correctly state its purpose.The difference between
include!
, shp-handler
, eval-script-if-changed
, and envoke-topfilter-if-available
are basically their signatures, one takes in a path, the other takes a request, then one takes a script structure, and the last takes a procedure. It would be great to "normalize" them.Note
shp-handler
and envoke-top-filter-if-available
depend on include!
, so include!
should be the base of our refactoring. Let's see if we can push redundant features into include!
.Since
include!
takes a path into path->segments
, which now also takes an url, we can reduce the code in shp-handler
if we can fold envoke-topfilter-if-available
in as well.
(define (include! path #:topfilter (topfilter #f) . args)
(define (helper topfilter)
(let ((proc (evaluate-script (segments->path (path->segments path) #f))))
(if topfilter
(topfilter (lambda () (apply proc args)))
(apply proc args))))
(helper (if topfilter
(evaluate-script (segments->path (path->segments topfilter) #f))
topfilter)))
So we can now get rid of
envoke-topfilter-if-available
, and modify shp-handler
as such:
(define (handle-request server request)
(parameterize (($pathinfo ($pathinfo))
($server server)
($request request))
(eval-script-if-changed! (shp-handler-required server))
(make-response (include! (request-uri request)
#:topfilter (shp-handler-topfilter server)
#:partial? #t))))
handle-request
is extracted out of the struct, which now becomes:
(define-struct shp-handler (path default not-found required topfilter)
#:property prop:procedure
(lambda ($struct request)
(handle-request $struct request)))
You'll notice that
handler-request
calls include!
with an extra partial?
parameter, which will allow a partial path match, so include!
now looks like:
(define (include! path
#:topfilter (topfilter #f)
#:partial? (partial? #f)
. args)
(define (helper topfilter)
(let ((proc (evaluate-script (segments->path (path->segments path) #f))))
(if topfilter
(topfilter (lambda () (apply proc args)))
(apply proc args))))
(helper (if topfilter
(evaluate-script (segments->path (path->segments topfilter) partial?))
topfilter)))
Notice that
partial?
is only applied to the script and not to the toplevel filter, which needs a full path match.At this time only
eval-script-if-changed
has not been combined. A simple way of handling the issue is to allow include!
to take in script struct as well:
(define (include! path
#:topfilter (topfilter #f)
#:partial? (partial? #f)
. args)
(define (helper topfilter)
(let ((proc (evaluate-script
(if (script? path)
(script-path path)
(segments->path (path->segments path) partial?)))))
(if topfilter
(topfilter (lambda () (apply proc args)))
(apply proc args))))
(helper (if topfilter
(evaluate-script (segments->path (path->segments topfilter) #f))
topfilter)))
(define (eval-script-if-changed! script)
(unless (not (file-exists? (script-path script)))
(let ((timestamp (file-or-directory-modify-seconds (script-path script))))
(when (> timestamp (script-timestamp script))
(set-script-timestamp! script timestamp)
(include! script)))))
Finally - we can abstract
(segments->path (path->segments ...))
into its own procedure:
(define (resolve-path path (partial? #t))
(segments->path (path->segments path) partial?))
(define (include! path
#:topfilter (topfilter #f)
#:partial? (partial? #f)
. args)
(define (make-script path partial?)
(evaluate-script (if (script? path)
(script-path path)
(resolve-path path partial?))))
(define (helper topfilter)
(let ((proc (make-script path partial?)))
(if topfilter
(topfilter (lambda () (apply proc args)))
(apply proc args))))
(helper (if topfilter (make-script topfilter #f) topfilter)))
Allright! Now the code looks a lot better! We can move onto adding more features!
No comments:
Post a Comment