=============== Getting Started =============== *Milla* aims to be lightweight and easy to use. As such, it provides only the tools you need to build your application the way you want, without imposing any restrictions on how to do it. .. contents:: Contents :local: Milla's Components ================== *Milla* provides a small set of components that help you build your web application in a simple, efficient manner: * WSGI Application wrapper * Two types of URL Dispatchers: * Traversal (like CherryPy or Pyramid) * Routing (like Django or Pylons) * Authorization framework * Utility functions *Milla* does not provide an HTTP server, so you'll have to use one of the many implementations already available, such as `Meinheld`_ or `Paste`_, or another application that understands `WSGI`_, like `Apache HTTPD`_ with the `mod_wsgi`_ module. ``Application`` Objects ======================= The core class in a *Milla*-based project is its :py:class:`~milla.app.Application` object. ``Application`` objects are used to set up the environment for the application and handle incoming requests. ``Application`` instances are *WSGI* callables, meaning they implement the standard ``application(environ, start_response)`` signature. To set up an ``Application``, you will need a :term:`URL dispatcher`, which is an object that maps request paths to :term:`controller` callables. Choosing a URL Dispatcher ========================= *Milla* provides two types of URL dispatchers by default, but you can create your own if neither of these suit your needs. The default dispatchers are modeled after the URL dispatchers of other popular web frameworks, but may have small differences. A *Milla* application can only have one URL dispatcher, so make sure you choose the one that will work for all of your application's needs. Traversal ********* Object traversal is the simplest form of URL dispatcher, and is the default for *Milla* applications. Object traversal works by looking for path segments as object attributes, beginning with a :term:`root object` until a :term:`controller` is found. For example, consider the URL ``http://example.org/myapp/hello``. Assuming the *Milla* application is available at ``/myapp`` (which is controlled by the HTTP server), then the ``/hello`` portion becomes the request path. It contains only one segment, ``hello``. Thus, an attribute called ``hello`` on the :term:`root object` must be the controller that will produce a response to that request. The following code snippet will produce just such an object. .. code-block:: python class Root(object): def hello(self, request): return 'Hello, world!' To use this class as the :term:`root object` for a *Milla* application, pass an instance of it to the :py:class:`~milla.app.Application` constructor: .. code-block:: python application = milla.Application(Root()) To create URL paths with multiple segments, such as ``/hello/world`` or ``/umbrella/corp/bio``, the root object will need to have other objects corresponding to path segments as its attributes. This example uses static methods and nested classes: .. code-block:: python class Root(object): class hello(object): @staticmethod def world(request): return 'Hello, world!' application = milla.Application(Root) This example uses instance methods to create the hierarchy at runtime: .. code-block:: python class Root(object): def __init__(self): self.umbrella = Umbrella() class Umbrella(object): def __init__(self): self.corp = Corp() class Corp(object): def bio(self, request): return 'T-Virus research facility' application = milla.Application(Root()) If an attribute with the name of the next path segment cannot be found, *Milla* will look for a ``default`` attribute. While the object traversal dispatch mechanism is simple, it is not very flexible. Because path segments correspond to Python object names, they must adhere to the same restrictions. This means they can only contain ASCII letters and numbers and the underscore (``_``) character. If you need more complex names, dynamic segments, or otherwise more control over the path mapping, you may need to use routing. Routing ******* Routing offers more control of how URL paths are mapped to :term:`controller` callables, but require more specific configuration. To use routing, you need to instantiate a :py:class:`~milla.dispatch.routing.Router` object and then populate its routing table with path-to-controller maps. This is done using the :py:meth:`~milla.dispatch.routing.Router.add_route` method. .. code-block:: python def hello(request): return 'Hello, world!' router = milla.dispatch.routing.Router() router.add_route('/hello', hello) Aft er you've set up a ``Router`` and populated its routing table, pass it to the :py:class:`~milla.app.Application` constructor to use it in a *Milla* application: .. code-block:: python application = milla.Application(router) Using routing allows paths to contain dynamic portions which will be passed to controller callables as keyword arguments. .. code-block:: python def hello(request, name): return 'Hello, {0}'.format(name) router = milla.dispatch.routing.Router() router.add_route('/hello/{name}', hello) application = milla.Application(router) In the above example, the path ``/hello/alice`` would map to the ``hello`` function, and would return the response ``Hello, alice`` when visited. ``Router`` instances can have any number of routes in their routing table. To add more routes, simply call ``add_route`` for each path and controller combination you want to expose. .. code-block:: python def hello(request): return 'Hello, world!' def tvirus(request): return 'Beware of zombies' router = milla.dispatch.routing.Router() router.add_route('/hello', hello) router.add_route('/hello-world', hello) router.add_route('/umbrellacorp/tvirus', tvirus) Controller Callables ==================== *Controller callables* are where most of your application's logic will take place. Based on the :abbr:`MVC (Model, View, Controller)` pattern, controllers handle the logic of interaction between the user interface (the *view*) and the data (the *model*). In the context of a *Milla*-based web application, controllers take input (the HTTP request, represented by a :py:class:`~milla.Request` object) and deliver output (the HTTP response, represented by a :py:class:`~milla.Response` object). Once you've decided which URL dispatcher you will use, it's time to write controller callables. These can be any type of Python callable, including functions, instance methods, classmethods, or partials. *Milla* will automatically determine the callable type and call it appropriately for each controller callable mapped to a request path. This example shows a controller callable as a function (using routing): .. code-block:: python def index(request): return 'this is the index page' def hello(request): return 'hello, world' router = milla.dispatch.routing.Router() router.add_route('/', index) router.add_route('/hello', hello) application = milla.Application(router) This example is equivalent to the first, but shows a controller callable as a class instance (using traversal): .. code-block:: python class Controller(object): def __call__(self, request): return 'this is the index page' def hello(self, request): return 'hello, world' application = milla.Application(Controller()) Controller callables must take at least one argument, which will be an instance of :py:class:`~milla.Request` representing the HTTP request that was made by the user. The ``Request`` instance wraps the *WSGI* environment and exposes all of the available information from the HTTP headers, including path, method name, query string variables, POST data, etc. If you are using `Routing`_ and have routes with dynamic path segments, these segments will be passed by name as keyword arguments, so make sure your controller callables accept the same keywords. .. _before-after-hooks: Before and After Hooks ********************** You can instruct *Milla* to perform additional operations before and after the controller callable is run. This could, for example, create a `SQLAlchemy`_ session before the controller is called and roll back any outstanding transactions after it completes. To define the before and after hooks, create an ``__before__`` and/or an ``__after__`` attribute on your controller callable. These attributes should be methods that take exactly one argument: the request. For example: .. code-block:: python def setup(request): request.user = 'Alice' def teardown(request): del request.user def controller(request): return 'Hello, {user}!'.format(user=request.user) controller.__before__ = setup controller.__after__ = teardown To simplify this, *Milla* handles instance methods specially, by looking for the ``__before__`` and ``__after__`` methods on the controller callable's class as well as itself. .. code-block:: python class Controller(object): def __before__(self, request): request.user = 'Alice' def __after__(self, request): del request.user def __call__(self, request): return 'Hello, {user}'.format(user=request.user) Returing a Response =================== Up until now, the examples have shown :term:`controller` callables returning a string. This is the simplest way to return a plain HTML response; *Milla* will automatically send the appropriate HTTP headers for you in this case. If, however, you need to send special headers, change the content type, or stream data instead of sending a single response, you will need to return a :py:class:`~milla.Response` object. This object contains all the properties necessary to instruct *Milla* on what headers to send, etc. for your response. To create a :py:class:`~milla.Response` instance, use the :py:attr:`~milla.Request.ResponseClass` attribute from the request: .. code-block:: python def controller(request): response = request.ResponseClass() response.content_type = 'text/plain' response.text = 'Hello, world!' return response .. _Meinheld: http://meinheld.org/ .. _Paste: http://pythonpaste.org/ .. _WSGI: http://www.python.org/dev/peps/pep-0333/ .. _Apache HTTPD: http://httpd.apache.org/ .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _SQLAlchemy: http://www.sqlalchemy.org/