== Paths and URLs ==

The primary system of organizing URLs in modu is based around a class called `URLNode`. `URLNode` provides a mechanism for creating a tree of nodes, where each level of nodes has its own name. A node may contain a value or not, and the entire tree may be queried with a particular slash-separated path (e.g., a URI).

Let's look at a sample representation of a very basic `URLNode` tree. To begin with, we'll populate the tree with some objects at various paths:

{{{
#!python
tree = url.URLNode()
tree.register('/one', 1)
tree.register('/one/two', 2)
tree.register('/three', 3)
tree.register('/four', 4)
tree.register('/one/five/six', 6)
}}}

This results in a tree where the nodes have been laid out as follows:

{{{
<root>
+-----/one
+---------/two
+---------/five
+--------------/six
+-----/three
+-----/four
}}}

Requesting any of the paths specified as part of those calls to register() return obvious results, but what happens if we request '/', or '/one/five'?

In this example, there was no object stored at '/', so attempting to parse a URI of simply '/' would return `None` (and in practice would generate a 404 status).

For the second URI, '/one/five', the tree will be searched using a mock-depth-first algorithm. The deepest registered object that satisfies this path will be returned, so in this case, the number 1 would be returned.

What happens to the rest of the path? In this case, the resulting tree analysis also establishes the `pre-` and `postpath` variables. The `prepath` variable becomes `['one']`, while the `postpath` variable becomes `['five']`.

Fetching one last URI of '/one/five/six', however, returns the value 6 and sets `prepath` to `['one', 'five', 'six']`, and `postpath` to the empty list.

=== Host Trees ===

As an implementation detail, modu uses the `URLNode` class to construct a second tree to organize the different modu sites/applications that might exist on a particular machine. Since the discovery process for finding site configs will load any site found in sys.path, a single server can host all of the simultaneously, provided they all use different domains or base paths.

The host tree is basically a dictionary of `URLNode` trees. The keys are  hostnames with appended port numbers, while multiple apps on the same domain are stored under their base_path config attribute. The end result looks something like this pseudocode:

{{{
host_tree = {
    'localhost:8888'    : URLNode(None)[
                            /modu : <class ModuSite>,
                            /trac : <class TracPassthroughSite>,
                        ],
    'example.com:80'    : URLNode(None)[
                            /     : <class ExampleSite>,
                        ],
}
}}}

=== Handling Complex URL Schemes ===

The default implementation of modu paths can be limited in certain respects when you have a pre-existing URL scheme that you must implement. For example, let's say you have a scheme that requires the following URLs:

    * /node/1234
    * /node/1234/edit
    * /node/1234/delete

where 1234 is the node ID. The easiest way to implement this is to register a `Resource` for /node, and allow it to handle all the URLs starting with /node. However, in complex applications this may not be suitable, either from a code organization standpoint or because of a even more complex URL structure.

Another more powerful option is available through the use of Resource Delegates. If you create a `Resource` that implements the `IResourceDelegate` interface, and supply a corresponding `get_delegate()` method, you can write code that examines the request, and then returns some `Resource` (this can be `self`, or any other `Resource`). Here's an example:

{{{
#!python
class Resource(resource.CheetahTemplateResource):
    implements(resource.IResourceDelegate)
    
    def get_delegate(self, req):
        if(req.postpath[1] == 'player'):
            return player.Resource()
        else:
            return self
}}}

In this case, the `Resource` represents an album detail page, at a URL like '/albums/1234'. However, I wanted to be able to popup a media player window through JavaScript, and the easiest way was just to append '/player' to the URL.

There's one small catch with this method. This happens early in the resource lookup process, and you aren't limited to making delegate choices based solely on the URL scheme, so there's no way for modu to automatically update the `pre-` and `postpath` variables. In the example above, my `prepath` is still `['albums']` and my `postpath` is still `['1234', 'player']`, but if that matters to you, you can adjust those variables before returning the delegate.

=== modu Paths ===

A modu application manages a `URLNode` tree of `Resource` objects. It provides an extremely simple mechanism for looking up those resources based on the URL path used in the HTTP request.

Every modu application is setup at some path from the root of the server. The particular path is specified in the site config using the `base_path` attribute. In the default scenario, the base_path is set to '/'.

Once a `Resource` is found for a particular URI, the Request object has its path, prepath, and postpath attributes set for additional processing. This system is specifically aimed towards creating search-friendly 'clean' URLs; adding additional path elements to a linked URI is a great way to 'cleanly' supply GET arguments without using the query string syntax.

The goal when using modu paths is to allow the same codebase to be run over several domains (say, between production and staging, or development sites) without needing to replace config files or doing tricks with symlinks. To further this goal, users are encouraged to use `Request::get_path()` to generate URLs in their templates and resource code.

`get_path()` takes an arbitrary number of path elements, concatenates them together -- removing duplicate slashes, etc -- and creates an absolute URL containing the hostname, port (if necessary), and `base_path` for the particular running site configuration. It's also able to deal with certain Apache proxying situations where the WSGI environment dictionary contains '''HTTP_X_FORWARDED_SERVER'''.
