1# -*- coding: utf-8 -*-
2"""
3    webapp2_extras.mako
4    ===================
5
6    Mako template support for webapp2.
7
8    Learn more about Mako: http://www.makotemplates.org/
9
10    :copyright: 2011 by tipfy.org.
11    :license: Apache Sotware License, see LICENSE for details.
12"""
13from __future__ import absolute_import
14
15from mako import lookup
16
17import webapp2
18
19#: Default configuration values for this module. Keys are:
20#:
21#: template_path
22#:     Directory for templates. Default is `templates`.
23default_config = {
24    'template_path': 'templates',
25}
26
27
28class Mako(object):
29    """Wrapper for configurable and cached Mako environment.
30
31    To used it, set it as a cached property in a base `RequestHandler`::
32
33        import webapp2
34
35        from webapp2_extras import mako
36
37        class BaseHandler(webapp2.RequestHandler):
38
39            @webapp2.cached_property
40            def mako(self):
41                # Returns a Mako renderer cached in the app registry.
42                return mako.get_mako(app=self.app)
43
44            def render_response(self, _template, **context):
45                # Renders a template and writes the result to the response.
46                rv = self.mako.render_template(_template, **context)
47                self.response.write(rv)
48
49    Then extended handlers can render templates directly::
50
51        class MyHandler(BaseHandler):
52            def get(self):
53                context = {'message': 'Hello, world!'}
54                self.render_response('my_template.html', **context)
55    """
56
57    #: Configuration key.
58    config_key = __name__
59
60    #: Loaded configuration.
61    config = None
62
63    def __init__(self, app, config=None):
64        self.config = config = app.config.load_config(self.config_key,
65            default_values=default_config, user_values=config,
66            required_keys=None)
67
68        directories = config.get('template_path')
69        if isinstance(directories, basestring):
70            directories = [directories]
71
72        self.environment = lookup.TemplateLookup(directories=directories,
73                                                 output_encoding='utf-8',
74                                                 encoding_errors='replace')
75
76    def render_template(self, _filename, **context):
77        """Renders a template and returns a response object.
78
79        :param _filename:
80            The template filename, related to the templates directory.
81        :param context:
82            Keyword arguments used as variables in the rendered template.
83            These will override values set in the request context.
84        :returns:
85            A rendered template.
86        """
87        template = self.environment.get_template(_filename)
88        return template.render_unicode(**context)
89
90
91# Factories -------------------------------------------------------------------
92
93
94#: Key used to store :class:`Mako` in the app registry.
95_registry_key = 'webapp2_extras.mako.Mako'
96
97
98def get_mako(factory=Mako, key=_registry_key, app=None):
99    """Returns an instance of :class:`Mako` from the app registry.
100
101    It'll try to get it from the current app registry, and if it is not
102    registered it'll be instantiated and registered. A second call to this
103    function will return the same instance.
104
105    :param factory:
106        The callable used to build and register the instance if it is not yet
107        registered. The default is the class :class:`Mako` itself.
108    :param key:
109        The key used to store the instance in the registry. A default is used
110        if it is not set.
111    :param app:
112        A :class:`webapp2.WSGIApplication` instance used to store the instance.
113        The active app is used if it is not set.
114    """
115    app = app or webapp2.get_app()
116    mako = app.registry.get(key)
117    if not mako:
118        mako = app.registry[key] = factory(app)
119
120    return mako
121
122
123def set_mako(mako, key=_registry_key, app=None):
124    """Sets an instance of :class:`Mako` in the app registry.
125
126    :param store:
127        An instance of :class:`Mako`.
128    :param key:
129        The key used to retrieve the instance from the registry. A default
130        is used if it is not set.
131    :param request:
132        A :class:`webapp2.WSGIApplication` instance used to retrieve the
133        instance. The active app is used if it is not set.
134    """
135    app = app or webapp2.get_app()
136    app.registry[key] = mako
137