Thursday, August 13, 2009

Adding "Global" (sortof) Variables...

Let's say we want to create a database connection and we want the database connection available during the whole script execution phase, but we do not want to have to touch all of the scripts to either initialize or pass the variable, what do we do?

Because the variable will be available to the top level scripts as well, it means that the variable needs to be passed through parameters rather than passing through procedure arguments, and because the way parameterize works, we'll need a wrapper function outside of the script. In short, we need a filter.

A filter is a procedure the form of:

(-> handler? response/c)
(define (filter inner) ...)

And basically, we'll pass the handler's function into the filter, and then let the filter do its job, then call the handler, possibly do some post processing, and finally return the result.

In the case of setting the global parameters, the filter will call parameterize and then call inner within the parameterize block.

It is simple to develop such a filter, since our scripts already takes in arguments:

;; somefilter.shp
(:args inner)
(parameterize ((foo (some-value)))
(inner))

And since the script takes an argument - it cannot be invoked directly via the browser. Now we just need to modify the handler to support such a filter. Let's tackle the support for this specific case (of having a top level filter for all scripts).
The first thing we do is the same as adding required module: add an attribute for shp-handler:

(define-struct shp-handler
(path default not-found required topfilter) ...)

Then we want to call the toplevel filterif it is available with something like the following:

(define (envoke-topfilter-if-available inner)
(if (shp-handler-topfilter ($server))
(include! (shp-handler-topfilter ($server)) inner)
(inner)))

(define-struct shp-handler (path default not-found required topfilter)
#:property prop:procedure
(lambda ($struct request)
...
(make-response (if (thunk? proc)
(envoke-topfilter-if-available proc)
(include! (shp-handler-not-found ($server)))))))))

We then also need to update the wrapper *make-shp-handler procedure with the additional parameter. Given we now have four optional parameters and it can get cumbersome to specify them, we'll change them to keyword parameters:

(define (*make-shp-handler path
#:default (default "index.shp")
#:not-found (not-found "notfound.shp")
#:required (required "required.shp")
#:topfilter (topfilter #f))
(make-shp-handler path default not-found (init-script required path) topfilter))


Now you can parameterize at topfilter and know that you'll get the correct value in all of the scripts! Since the topfilter gets run after the required script, make sure you have the parmeter defined and exported from your required modules, and things will work accordingly.

No comments:

Post a Comment