1 <?xml version="1.0" encoding="iso-8859-1"?> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 3 <html xmlns="http://www.w3.org/1999/xhtml"><head> 4 <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" /><title>Path Processing Strategies</title> 5 <link href="styles.css" rel="stylesheet" type="text/css" /></head> 6 <body><h1>Path Processing Strategies</h1> 7 <p>In the development of most Web applications, the structure of the 8 application - also known as the "site map" - needs to be defined 9 at a very early stage. We might decide that if a user requests a 10 certain path, a particular part of the application will be invoked, and 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 12 all of the action in WebStack applications takes place inside 13 resources, the challenge is to define resources in such a way which 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> 15 method performs most of the necessary work, it is useful to reconsider 16 such a resource as doing such work only for a particular narrow part of 17 a larger Web application. Moreover, resources are not restricted in 18 their usage of other objects to carry out their purpose, provided they 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 20 can apply this insight to the above path processing scenario. If we 21 first employ a resource to examine details of the path, and if that 22 resource then invokes other resources to produce certain pages, we can 23 separate the path processing from the rest of the application's 24 functionality. So, for the first site map strategy, we could define a 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 26 course, more elegant methods of mapping paths to resources could be 27 employed - a dictionary might be an acceptable solution, if defined in 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 29 the second site map strategy, we retain the basic parts of the above 30 strategy, focusing only on one level in the path as opposed to the 31 complete path. However, this means that our path processing resources 32 need to know exactly which part of the path they should be 33 considering, and perhaps which part of the path has already been 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>