# HG changeset patch # User paulb # Date 1169334982 0 # Node ID 90993d7be1237d6cb815fd0ccf8183b674246ea4 # Parent c091bb3733be12ecdefbf9ca2058330b3f441180 [project @ 2007-01-20 23:16:22 by paulb] Added a path strategies document. Improved the introduction to resources. Updated the documentation index. Updated release information. diff -r c091bb3733be -r 90993d7be123 docs/developing.html --- a/docs/developing.html Sat Jan 20 23:15:29 2007 +0000 +++ b/docs/developing.html Sat Jan 20 23:16:22 2007 +0000 @@ -17,20 +17,20 @@
DemoApp
example, Calendar
example)DemoApp
example, Calendar
example)First of all, let us assume that the WebStack distribution has been
-unpacked and now sits in the WebStack-1.2.1
directory.
WebStack-1.2.2
directory.
Before we begin, we must make sure that the WebStack package is
available
to Python. The easiest way to do this is to change into the
-WebStack-1.2.1
directory and to run the setup.py
+WebStack-1.2.2
directory and to run the setup.py
script provided with the version of Python you are going to be using
(possibly as a privileged user like root
):
cd WebStack-1.2.1+
python setup.py install
cd WebStack-1.2.2
python setup.py install
If you don't want to install WebStack in this way, or if you can't
do so
because you don't have root
privileges, you can just make
sure
-that the WebStack-1.2.1
directory sits on your
+that the WebStack-1.2.2
directory sits on your
PYTHONPATH
.
With the help of Python's built-in standard library, WebStack can run without any additional software, but @@ -30,7 +30,7 @@ run WebStack applications in other environments.
The API documentation for use in conjunction with this
guide can be found inside the apidocs
-directory within the WebStack-1.2.1
directory. Of course,
+directory within the WebStack-1.2.2
directory. Of course,
it is always possible to view WebStack's API documentation
within Python by importing modules (such as WebStack.Generic
)
and using Python's built-in help
function.
In the development of most Web applications, the structure of the +application - also known as the "site map" - needs to be defined +at a very early stage. We might decide that if a user requests a +certain path, a particular part of the application will be invoked, and +we might define the site map in a number of different ways:
As a set of acceptable paths...
/
is the main page/services/finance/salaries/
is the salary report page/services/customer/complaints/
is the complaints pageAs a tree of resources...
/
is the main page.../services/
refers to services (which may not be defined as anything viewable).../finance/
is the finance department.../salaries/
is the salary report page.../customer/
is the customer service department.../complaints/
is the complaints pageSince +all of the action in WebStack applications takes place inside +resources, the challenge is to define resources in such a way which +makes processing paths relatively easy.
Whilst the classic resource, as described in "Applications and Resources", might resemble a simple class whose respond
+method performs most of the necessary work, it is useful to reconsider
+such a resource as doing such work only for a particular narrow part of
+a larger Web application. Moreover, resources are not restricted in
+their usage of other objects to carry out their purpose, provided they
+are initialised with references to those objects.Consequently, it makes sense to consider defining a resource which, if it alone cannot process a request, invokes the respond
method on another resource in order to get that resource to continue with the act of processing.
We +can apply this insight to the above path processing scenario. If we +first employ a resource to examine details of the path, and if that +resource then invokes other resources to produce certain pages, we can +separate the path processing from the rest of the application's +functionality. So, for the first site map strategy, we could define a +path processing resource as follows:
from WebStack.Generic import EndOfResponse
class PathProcessor:
"A path processing resource."
def __init__(self, main_page, salary_report_page, complaints_page):
# Supplied resources are chained to this resource.
self.main_page = main_page
self.salary_report_page = salary_report_page
self.complaints_page = complaints_page
def respond(self, trans):
# This is where the resources are invoked...
path = trans.get_path_without_query() # should really use an encoding here
if path == "/":
self.main_page.respond(trans)
elif path == "/services/finance/salaries/":
self.main_page.respond(trans)
elif path == "/services/finance/salaries/":
self.main_page.respond(trans)
# Administrative details...
else:
trans.set_response_code(404) # not found!
raise EndOfResponse
Of +course, more elegant methods of mapping paths to resources could be +employed - a dictionary might be an acceptable solution, if defined in +the initialiser method. The above class might be initialised as follows:
main_page = MainResource()
salary_report_page = SalaryReportResource()
complaints_page = ComplaintsResource()
path_processor = PathProcessor(main_page, salary_report_page, complaints_page)
For +the second site map strategy, we retain the basic parts of the above +strategy, focusing only on one level in the path as opposed to the +complete path. However, this means that our path processing resources +need to know exactly which part of the path they should be +considering, and perhaps which part of the path has already been +processed.
As described in "Paths To and Within Applications", special path information of the nature required is provided by two methods: get_virtual_path_info
and get_processed_virtual_path_info
. Such information can thus be obtained and updated at each level in the processing chain.
class MainPathProcessor:
"A path processor for the top of the application."
def __init__(self, main_page, services_processor):
self.main_page = main_page
self.services_processor = services_processor
def respond(self, trans):
# This is where the resources are invoked...
path = trans.get_virtual_path_info() # should really use an encoding here
if path == "/":
self.main_page.respond(trans)
elif path.startswith("/services/"):
trans.set_virtual_path_info(path[len("/services"):]) # cut off "/services"
self.services_processor.respond(trans)
# Administrative details...
else:
trans.set_response_code(404) # not found!
raise EndOfResponse
A suite of similar classes can be defined and might be initialised as follows:
main_page = MainResource()
salary_report_page = SalaryReportResource()
complaints_page = ComplaintsResource()
finance_processor = FinancePathProcessor(salary_report_page)
customer_processor = CustomerPathProcessor(complaints_page)
services_processor = ServicesPathProcessor(finance_processor, customer_processor)
main_processor = MainPathProcessor(main_page, services_processor)
Fortunately, this latter strategy is supported by WebStack in the form of the ResourceMap
module. See "Treating the Path Like a Filesystem" and "ResourceMap - Simple Mappings from Names to Resources" for details.
If we were to just decode the query string and then extract the
parameters/fields, the result would be two empty parameters with the
names a
and b
, as opposed to the correct interpretation of the query string as describing a single parameter a
with the value &b
.
Regardless of all this, all inspection of path parameters should be done using the appropriate methods (see "Request Parameters and Uploads"), and direct access to the query string should only occur in situations diff -r c091bb3733be -r 90993d7be123 docs/resources.html --- a/docs/resources.html Sat Jan 20 23:15:29 2007 +0000 +++ b/docs/resources.html Sat Jan 20 23:16:22 2007 +0000 @@ -4,7 +4,6 @@
To test this resource we need to deploy it, and to do that we need an -adapter. Here is a quick way of writing an adapter and testing this +adapter to connect it to the outside world. Here is a quick way of writing an adapter and testing this code:
MyAdapter.py
- you
can choose another name if you want - this will be where the adapter
code lives.MyAdapter.py
.MyApplication.py
+ from WebStack.Adapters.BaseHTTPRequestHandler import deploy # import the support for the server environment
from MyApplication import MyResource # import the main resource class
print "Serving..." # just for testing - we might want to remove this later
deploy(MyResource()) # connect a resource object to the server environment
Now, with two files in your directory, MyApplication.py
and MyAdapter.py
, you may run MyAdapter.py
-as follows:
+as follows:
python MyAdapter.py
This should start the adapter program and print the following @@ -68,5 +66,4 @@
Serving...
You should now be able to visit http://localhost:8080
in your
-browser and see the message printed by your application:
Hello world.