paulb@654 | 1 | <?xml version="1.0" encoding="iso-8859-1"?> |
paulb@621 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
paulb@654 | 3 | <html xmlns="http://www.w3.org/1999/xhtml"><head> |
paulb@654 | 4 | <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" /><title>Path Processing Strategies</title> |
paulb@654 | 5 | <link href="styles.css" rel="stylesheet" type="text/css" /></head> |
paulb@621 | 6 | <body><h1>Path Processing Strategies</h1> |
paulb@621 | 7 | <p>In the development of most Web applications, the structure of the |
paulb@654 | 8 | application - also known as the "site map" - needs to be defined |
paulb@621 | 9 | at a very early stage. We might decide that if a user requests a |
paulb@621 | 10 | certain path, a particular part of the application will be invoked, and |
paulb@654 | 11 | we might define the site map in a number of different ways:</p><p>As a set of acceptable paths...</p><ul><li><code>/</code> is the main page</li><li><code>/services/finance/salaries/</code> is the salary report page</li><li><code>/services/customer/complaints/</code> is the complaints page</li></ul><p>As a tree of resources...</p><ul><li><code>/</code> is the main page</li><ul><li><code>.../services/</code> refers to services (which may not be defined as anything viewable)</li><ul><li><code>.../finance/</code> is the finance department</li><ul><li><code>.../salaries/</code> is the salary report page</li></ul><li><code>.../customer/</code> is the customer service department</li><ul><li><code>.../complaints/</code> is the complaints page</li></ul></ul></ul></ul><p>Since |
paulb@621 | 12 | all of the action in WebStack applications takes place inside |
paulb@621 | 13 | resources, the challenge is to define resources in such a way which |
paulb@654 | 14 | makes processing paths relatively easy.</p><h2>Chaining Resources</h2><p>Whilst the classic resource, as described in <a href="resources.html">"Applications and Resources"</a>, might resemble a simple class whose <code>respond</code> |
paulb@621 | 15 | method performs most of the necessary work, it is useful to reconsider |
paulb@621 | 16 | such a resource as doing such work only for a particular narrow part of |
paulb@621 | 17 | a larger Web application. Moreover, resources are not restricted in |
paulb@621 | 18 | their usage of other objects to carry out their purpose, provided they |
paulb@621 | 19 | 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 <code>respond</code> method on another resource in order to get that resource to continue with the act of processing.</p><p>We |
paulb@621 | 20 | can apply this insight to the above path processing scenario. If we |
paulb@621 | 21 | first employ a resource to examine details of the path, and if that |
paulb@621 | 22 | resource then invokes other resources to produce certain pages, we can |
paulb@621 | 23 | separate the path processing from the rest of the application's |
paulb@621 | 24 | functionality. So, for the first site map strategy, we could define a |
paulb@654 | 25 | path processing resource as follows:</p><pre>from WebStack.Generic import EndOfResponse<br /><br />class PathProcessor:<br /><br /> "A path processing resource."<br /><br /> def __init__(self, main_page, salary_report_page, complaints_page):<br /><br /> # Supplied resources are chained to this resource.<br /><br /> self.main_page = main_page<br /> self.salary_report_page = salary_report_page<br /> self.complaints_page = complaints_page<br /><br /> def respond(self, trans):<br /><br /> # This is where the resources are invoked...<br /><br /> path = trans.get_path_without_query() # should really use an encoding here<br /> if path == "/":<br /> self.main_page.respond(trans)<br /> elif path == "/services/finance/salaries/":<br /> self.main_page.respond(trans)<br /> elif path == "/services/finance/salaries/":<br /> self.main_page.respond(trans)<br /><br /> # Administrative details...<br /><br /> else:<br /> trans.set_response_code(404) # not found!<br /> raise EndOfResponse</pre><p>Of |
paulb@621 | 26 | course, more elegant methods of mapping paths to resources could be |
paulb@621 | 27 | employed - a dictionary might be an acceptable solution, if defined in |
paulb@621 | 28 | the initialiser method. The above class might be initialised as follows:</p><pre>main_page = MainResource()<br />salary_report_page = SalaryReportResource()<br />complaints_page = ComplaintsResource()<br />path_processor = PathProcessor(main_page, salary_report_page, complaints_page)</pre><p>For |
paulb@621 | 29 | the second site map strategy, we retain the basic parts of the above |
paulb@621 | 30 | strategy, focusing only on one level in the path as opposed to the |
paulb@621 | 31 | complete path. However, this means that our path processing resources |
paulb@654 | 32 | need to know exactly which part of the path they should be |
paulb@621 | 33 | considering, and perhaps which part of the path has already been |
paulb@654 | 34 | processed.</p><p>As described in <a href="path-info.html">"Paths To and Within Applications"</a>, special path information of the nature required is provided by two methods: <code>get_virtual_path_info</code> and <code>get_processed_virtual_path_info</code>. Such information can thus be obtained and updated at each level in the processing chain.</p><pre>class MainPathProcessor:<br /><br /> "A path processor for the top of the application."<br /><br /> def __init__(self, main_page, services_processor):<br /> self.main_page = main_page<br /> self.services_processor = services_processor<br /><br /> def respond(self, trans):<br /><br /> # This is where the resources are invoked...<br /><br /> path = trans.get_virtual_path_info() # should really use an encoding here<br /> if path == "/":<br /> self.main_page.respond(trans)<br /> elif path.startswith("/services/"):<br /> trans.set_virtual_path_info(path[len("/services"):]) # cut off "/services"<br /> self.services_processor.respond(trans)<br /><br /> # Administrative details...<br /><br /> else:<br /> trans.set_response_code(404) # not found!<br /> raise EndOfResponse</pre><p>A suite of similar classes can be defined and might be initialised as follows:</p><pre>main_page = MainResource()<br />salary_report_page = SalaryReportResource()<br />complaints_page = ComplaintsResource()<br />finance_processor = FinancePathProcessor(salary_report_page)<br />customer_processor = CustomerPathProcessor(complaints_page)<br />services_processor = ServicesPathProcessor(finance_processor, customer_processor)<br />main_processor = MainPathProcessor(main_page, services_processor)</pre><p>Fortunately, this latter strategy is supported by WebStack in the form of the <code>ResourceMap</code> module. See <a href="paths-filesystem.html">"Treating the Path Like a Filesystem"</a> and <a href="resource-map.html">"ResourceMap - Simple Mappings from Names to Resources"</a> for details.</p></body></html> |