# HG changeset patch # User paulb # Date 1196984418 0 # Node ID e77c5130e4ed70a6a0b3ce2ca9edb15907f8c16f # Parent a9b44a61e215981e9a8face9d1d697e327317916 [project @ 2007-12-06 23:40:18 by paulb] Added notes on the StoreSelector, revising the integration-related documentation to mention the class, removing less pertinent notes about using packages to maintain global objects (which is an elementary Python technique). diff -r a9b44a61e215 -r e77c5130e4ed docs/integrating.html --- a/docs/integrating.html Thu Dec 06 23:38:37 2007 +0000 +++ b/docs/integrating.html Thu Dec 06 23:40:18 2007 +0000 @@ -8,33 +8,52 @@

Integrating with Other Systems

-

Most Web applications are not self-contained - instead of -providing information which is written into the application -code itself, they may often access data from other places or even -communicate with other systems. Since applications may be very -different in the way that they access external systems or the way -in which they obtain external information, WebStack does not mandate -rigid mechanisms for hooking into such systems or loading such -information. Instead, it is recommended that applications import -packages and modules which provide the functionality necessary to carry -out such integration.

+ +

Most Web applications are not self-contained - instead of providing +information which is written into the application code itself, they may often +access data from other places or even communicate with other systems. Since +applications may be very different in the way that they access external systems +or the way in which they obtain external information, WebStack does not mandate +a single mechanism for hooking into such systems or loading such information. +Instead, it is recommended that applications import packages and modules which +provide the functionality necessary to carry out such integration, using the +appropriate conveniences (such as selector +resources) to manage that functionality where appropriate.

+

Examples of Packages

-

Examples of packages and modules that might be used for integration -purposes include the following:

+ +

Examples of packages and modules that might be used for integration purposes +include the following:

+ -

Using External Packages

-

In the simplest of cases, the use of external packages is as -straightforward as importing a Python module (or package) and then -using that module's contents. This can often be done in the resource code; for example:

-
import urllib

class MyResource:
def respond(self, trans):
[Examine the transaction, decide what the user wants to do.]

f = urllib.urlopen("http://www.boddie.org.uk/rss/feed.xml")

[Produce some kind of response which tells the user what happened.]
+ +

Using External Packages in General

+ +

In the simplest of cases, the use of external packages is as straightforward +as importing a Python module (or package) and then using that module's +contents. This can often be done in the resource +code; for example:

+ +
+import urllib
+
+class MyResource:
+    def respond(self, trans):
+        [Examine the transaction, decide what the user wants to do.]
+
+        f = urllib.urlopen("http://www.boddie.org.uk/rss/feed.xml")
+
+        [Produce some kind of response which tells the user what happened.]
+
+

In the above example, here is what happens:

+ @@ -43,28 +62,68 @@ - - + + - - + +
Where it happens and how often
Import urllib -to gain access to functionality which we can then use to access a -remote service.This happens once - when -the above code is itself imported into Python.Import urllib to gain access to functionality which we + can then use to access a remote service.This happens once - when the above code is itself imported into + Python.
Use the urlopen -function of urllib to actually access a remote service.This happens in the -resource code each time the resource decides to access the service.Use the urlopen function of urllib to + actually access a remote service.This happens in the resource code each time the resource decides to + access the service.
-

In this case, the functionality is relatively easy to acquire and -does not require any initialisation. But what if we were connecting to -a database? There might be a need to specially initialise the database -module - only once, though - and then repeatedly use it. Consider this -highly artificial example:

-
import mydb

connection = mydb.connect("me", "myPassword")

class MyResource:
def respond(self, trans):
[Examine the transaction, decide what the user wants to do.]

results = connection.query("feed", owner="boddie.org.uk")

[Produce some kind of response which tells the user what happened.] + +

Using Database Connections

+ +

In the above case, the functionality is relatively easy to acquire and does not +require any initialisation. But what if we were connecting to a database? There +might be a need to specially initialise the database module - only once, though +- and then repeatedly use it. Moreover, we may want to ensure that any +transactions created as our resources run are terminated when the resources +have themselves completed their work. At this point, it is worth considering +the StoreSelector class, as described in the "Selectors - Components for Dispatching to +Resources" document. Consider this example:

+ +
+from WebStack.Resources.Selectors import StoreSelector
+
+class MyResource:
+    def respond(self, trans):
+        [Examine the transaction, decide what the user wants to do.]
+
+        connection = trans.get_attributes()["store"]
+        cursor = connection.cursor()
+        try:
+            cursor.execute("select url from feeds where owner=?", ["boddie.org.uk"])
+            results = cursor.fetchall()
+        finally:
+            cursor.close()
+
+        [Produce some kind of response which tells the user what happened.]
+
+def get_site_map(connection):
+    return StoreSelector(MyResource(), connection)
 
+ +

The initialisation would be done elsewhere, perhaps in the same module:

+ +
+import mydb
+
+connection = mydb.connect("me", "myPassword")
+try:
+    site = get_site_map(connection)
+    deploy(site)
+finally:
+    connection.close()
+
+

In the above example, here is what happens:

+ @@ -73,54 +132,54 @@ - - + + + + + + + + + + - - + + - - + + + + + + + + + +
Where it happens and how often
Import the mydb -module to gain access to database access functionality.This happens once - when -the above code is itself imported into Python.Import the mydb module to gain access to database access functionality.This happens once - when the above code is itself imported into Python.
Initialise a database connection using the connect function from the mydb module.This also happens only once - when the above code is itself imported into Python.
Wrap the application in a StoreSelector which manages + the database connection.This happens in the get_site_map function which + prepares the application before deployment.
Initialise a database -connection using the connect function from the mydb -module.This also happens only -once - when the above code is itself imported into Python.Make the connection available in a transaction object.This happens automatically for every incoming transaction or request to access the + application - it is managed by the StoreSelector object.
Use the query -method on the connection object to access a database.This happens in the -resource code each time the resource decides to access the database.Obtain the connection from the attributes on the transaction object; + use the cursor method on the connection object to access + the database; use the methods on the cursor object.This happens in the resource code each time the resource decides to + access the database.
Terminate the active transaction on the connection object.This happens automatically for every completed transaction or request to access the + application - it is managed by the StoreSelector object.
Close the database connection using the close method on + the connection object.This also happens only once - when the application is + terminating.
-

The choice of initialising the connection may seem arbitrary - why -not just obtain a connection in the resource? Usually, such decisions -are made on the basis of efficiency and on constraints outside the -control of the application - some database systems limit the number of -connections, for example, and if a large number of resources suddenly -became active, some of them would fail to obtain connections if the -connection initialisation code were in the respond -method of the resource.

-

Configuring Packages Globally

-

Of course, the above resource might not be the only resource to use -database connections. It might then be tempting to initialise a -connection for each module whose resource needs (or, since as normal -Python classes we can put many resources in a single module, whose -resources need) to access a database. But it would surely be more -convenient to define a single, central place to hold such global -resources.

-

One approach is to define a module which can be accessed by all -modules, and thus by all resources. Let us create such a module in the -file Properties.py which will reside alongside MyApplication.py -(or whatever the application module is called). Inside the Properties -module we can write the following code:

-
import mydb

connection = mydb.connect("me", "myPassword")
-

Now, in each module containing resources which need to access the -database, all we need to do now is to import the Properties -module and to use the connection as defined in that module:

-
import Properties

class MyResource:
def respond(self, trans):
[Examine the transaction, decide what the user wants to do.]

results = Properties.connection.query("feed", owner="boddie.org.uk")

[Produce some kind of response which tells the user what happened.]
-

This is a very simple approach that is technically outclassed by the -mechanisms available in some frameworks. Currently, WebStack does not -provide access to those more sophisticated mechanisms, however.

+ +

The choice of initialising the connection may seem arbitrary - why not just +obtain a connection in the resource? Usually, such decisions are made on the +basis of efficiency and on constraints outside the control of the application - +some database systems limit the number of connections, for example, and if a +large number of resources suddenly became active, some of them would fail to +obtain connections if the connection initialisation code were in the +respond method of the resource. Moreover, by using the +StoreSelector, a certain amount of "boilerplate" code can be +hidden away and not written within each resource.

+ diff -r a9b44a61e215 -r e77c5130e4ed docs/selectors.html --- a/docs/selectors.html Thu Dec 06 23:38:37 2007 +0000 +++ b/docs/selectors.html Thu Dec 06 23:40:18 2007 +0000 @@ -5,60 +5,214 @@

Selectors - Components for Dispatching to Resources

-

The Selectors module provides classes (although -currently only one class is supplied) which act as standard WebStack -resources, but which attempt to "select" other resources, dispatch each -request to those resources, and to cause various side-effects, mostly -on the attributes stored on the transaction object. These "selector" -classes behave those in the ResourceMap module, but aspire to be more generally applicable.

Selecting Path Roots with PathSelector

In -non-trivial applications, it is often desirable to know the path or URL -to a particular resource, especially the "root" resource under which -the application or one of its components may reside. The PathSelector -class can be instantiated and be made to refer to a resource, recording -such path or URL details in an attribute for later inspection.

-

WebStack API - The PathSelector Class

+ +

The Selectors module provides classes (although currently only +one class is supplied) which act as standard WebStack resources, but which +attempt to "select" other resources, dispatch each request to those resources, +and to cause various side-effects, mostly on the attributes stored on the +transaction object. These "selector" classes behave those in the +ResourceMap module, but aspire to be more generally +applicable.

+ +

Selecting Path Roots with PathSelector

+ +

In non-trivial applications, it is often desirable to know the path or URL +to a particular resource, especially the "root" resource under which the +application or one of its components may reside. The PathSelector +class can be instantiated and be made to refer to a resource, recording such +path or URL details in an attribute for later inspection.

+ +
+

WebStack API - The PathSelector Class

+ +

The PathSelector class (found in the + WebStack.Resources.Selectors module) wraps resource objects whose + location (as indicated by a path or URL) should be recorded as an attribute on + the current transaction. The most common use of the class is to record the + "root" resource's location, and this would be done as follows:

+ +
+def get_site_map():
+    return PathSelector(MyResource())
+
+ +

Here, the get_site_map function (as described in the "Deploying a WebStack Application" document) would + provide a PathSelector object instead of an instance of the + MyResource class. However, the side-effect of combining these two + resources would be the availability of an attribute named root on + the transaction object. Thus, the "root" MyResource object and, + indeed, all resource objects visited after this side-effect has occurred will + have access to the "root" path or URL information.

+ +

Further Reading

+ +

The API + documentation for the PathSelector class provides additional + information.

+ +
+ +

Defining Encodings using EncodingSelector

+ +

One frequently bothersome aspect of writing Web applications involves the +processing and production of text in different encodings. Whilst the WebStack +API lets applications explicitly state the character encoding for various +operations, one often wants to be able to ignore such details since they start +to clutter up application code. The EncodingSelector class offers +a basic solution which is compatible with the mechanisms in transaction +objects: by wrapping WebStack resources with instances of +EncodingSelector, an application-wide default encoding (or +character set) will be defined; this will result in request information being +processed according to the stated encoding and response information being +produced according to that encoding (see below for more details of the latter +activity).

+ +
+

WebStack API - The EncodingSelector Class

+ +

The EncodingSelector class (found in the + WebStack.Resources.Selectors module) wraps resource + objects whilst changing the default encoding of the current transaction + object. The class can be applied like this:

+ +
+def get_site_map():
+    return EncodingSelector(MyResource(), "utf-8")
+
+ +

Here, the get_site_map function (as described in the "Deploying a WebStack Application" document) would + provide a EncodingSelector object instead of an instance of the + MyResource class. However, the EncodingSelector will + forward requests on to MyResource (since it "selects" that + resource), whilst setting the default encoding to be "utf-8".

+ +

Request Streams and Encodings

+ +

Although a default encoding affects the processing of request parameters, + direct access to the request stream using the get_request_stream + method will only produce plain strings. This behaviour is justified by the + observation that, if conversion from an encoding to Unicode were to take place, + the resulting character values may be unsuitable substitutes for the original + byte values in applications intending to make use of the raw incoming (possibly + binary) data. A recipe for making a Unicode stream is provided in the "Character Encodings" document.

+ +

Response Encodings and Content Types

+ +

Although EncodingSelector sets a default encoding, responses + will generally be written in that encoding, at least if textual data is written + to the response stream as Unicode objects. However, the content type must + typically be set either to match the encoding in use or to not specify an + encoding. It may become necessary to find out what the response stream encoding + is when creating a ContentType object. For this and other + purposes, the get_response_stream_encoding method is available on + the transaction object:

-

The PathSelector -class (found in the -WebStack.Resources.Selectors module) wraps resource -objects whose location (as indicated by a path or URL) should be -recorded as an attribute on the current transaction. The most common -use of the class is to record the "root" resource's location, -and this would be done as follows:

def get_site_map():
return PathSelector(MyResource())

Here, the get_site_map -function (as described in the "Deploying a WebStack Application" document) would provide a PathSelector object instead of an instance of the MyResource class. However, the side-effect of combining these two resources would be the availability of an attribute named root on the transaction object. Thus, the "root" MyResource -object and, indeed, all resource objects visited after this side-effect -has occurred will have access to the "root" path or URL information.

Further Reading

The API documentation for the PathSelector -class provides additional information.

Defining Encodings using EncodingSelector

One -frequently bothersome aspect of writing Web applications involves the -processing and production of text in different encodings. Whilst the -WebStack API lets applications explicitly state the character encoding -for various operations, one often wants to be able to ignore such -details since they start to clutter up application code. The EncodingSelector class offers a basic solution which is compatible with the mechanisms in transaction objects: by wrapping WebStack resources with instances of EncodingSelector, -an application-wide default encoding (or character set) will be -defined; this will result in request information being processed -according to the stated encoding and response information being -produced according to that encoding (see below for more details of the -latter activity).

-

WebStack API - The EncodingSelector Class

+
+from WebStack.Generic import ContentType
+
+def respond(self, trans):
+
+    # Some code...
+
+    trans.set_content_type(ContentType("text/html", trans.get_response_stream_encoding()))
+    out = trans.get_response_stream()
+    out.write(some_unicode_object)
+
+ +

However, it is completely acceptable to omit the encoding information where + a default encoding has been set:

+ +
+    trans.set_content_type(ContentType("text/html")) # no encoding/charset specified
+
+ +

Reading the Default Directly

+ +

For some activities, such as making a Unicode stream from the request + stream, it is desirable to find out the encoding set by the selector. This + information is made available on transaction objects as the + default_charset attribute.

+ +

Further Reading

+ +

The API + documentation for the EncodingSelector class provides + additional information, along with the "Responses and + Presentation" document.

+ +
+ +

Managing Database Resources

+ +

Web applications must often employ external resources such as database +systems, leading developers to consider effective mechanisms to manage such +resources. Although an application can initially open a database connection +and make it available to resources, it is essential that such resources access +the connection in a way that does not cause problems over time. The +StoreSelector class offers an elementary solution for resources +employing database connections by storing a connection object in an attribute +on the transaction object, such that resources may obtain the connection +merely by accessing the appropriate attribute. Moreover, the +StoreSelector also ensures that database transactions are +terminated in a timely fashion by calling the rollback method on +the connection when a resource (or potentially many resources) have completed +their work.

-

The EncodingSelector -class (found in the -WebStack.Resources.Selectors module) wraps resource -objects whilst changing the default encoding of the current transaction object. The class can be applied like this:

def get_site_map():
return EncodingSelector(MyResource(), "utf-8")

Here, the get_site_map -function (as described in the "Deploying a WebStack Application" document) would provide a EncodingSelector object instead of an instance of the MyResource class. However, the EncodingSelector will forward requests on to MyResource (since it "selects" that resource), whilst setting the default encoding to be "utf-8".

Request Streams and Encodings

Although a default encoding affects the processing of request parameters, direct access to the request stream using the get_request_stream -method will only produce plain strings. This behaviour is justified by -the observation that, if conversion from an encoding to Unicode were to -take place, the resulting character values may be unsuitable -substitutes for the original byte values in applications intending to -make use of the raw incoming (possibly binary) data. A recipe for -making a Unicode stream is provided in the "Character Encodings" document.

Response Encodings and Content Types

Although EncodingSelector sets -a default encoding, responses will generally be written in that -encoding, at least if textual data is written to the response stream as -Unicode objects. However, the content type must typically be set either -to match the encoding in use or to not specify an encoding. It may -become necessary to find out what the response stream encoding is when -creating a ContentType object. For this and other purposes, the get_response_stream_encoding method is available on the transaction object:

from WebStack.Generic import ContentType

def respond(self, trans):

# Some code...

trans.set_content_type(ContentType("text/html", trans.get_response_stream_encoding()))
out = trans.get_response_stream()
out.write(some_unicode_object)

However, it is completely acceptable to omit the encoding information where a default encoding has been set:

    trans.set_content_type(ContentType("text/html")) # no encoding/charset specified

Reading the Default Directly

For -some activities, such as making a Unicode stream from the request -stream, it is desirable to find out the encoding set by the selector. -This information is made available on transaction objects as the default_charset attribute.

Further Reading

The API documentation for the EncodingSelector -class provides additional information, along with the "Responses and Presentation" document.


+
+

WebStack API - The StoreSelector Class

+ +

The StoreSelector class (found in the + WebStack.Resources.Selectors module) wraps resource + objects whilst maintaining a reference to a database connection or store + object as an attribute on a WebStack transaction object. The class can be + applied like this:

+ +
+def get_site_map(connection):
+    return StoreSelector(MyResource(), connection)
+
+ +

Here, the get_site_map function (as described in the "Deploying a WebStack Application" document) would + provide a StoreSelector object instead of an instance of the + MyResource class. However, the StoreSelector will + forward requests on to MyResource (since it "selects" that + resource), whilst maintaining a reference to the connection + provided.

+ +

Resources wishing to access the database connection would use code like + the following:

+ +
+def respond(self, trans):
+
+    # Some code...
+
+    connection = trans.get_attributes()["store"]
+
+ +

Should an alternative attribute name be required, such a name may be + provided as an additional argument when initialising the + StoreSelector; for example:

+ +
+def get_site_map(connection):
+    return StoreSelector(MyResource(), connection, "connection")
+
+ +

Further Reading

+ +

The API + documentation for the StoreSelector class provides + additional information, and the "Integrating with + Other Systems" document describes related topics.

+ +
+ +