liberating jingo from zamboni
This commit is contained in:
Родитель
fa330b13e4
Коммит
6e048e3801
|
@ -1,59 +0,0 @@
|
|||
.. _jingo:
|
||||
.. module:: jingo
|
||||
.. highlight:: jinja
|
||||
|
||||
A page about Jingo.
|
||||
===================
|
||||
|
||||
Jingo is an adapter for using
|
||||
`Jinja2 <http://jinja.pocoo.org/2/documentation/>`_ templates within Django.
|
||||
Why are we already replacing the templates? AMO's current PHP templates let you
|
||||
go hog-wild with logic in the templates, while Django is extremely restrictive.
|
||||
Jinja loosens those restrictions somewhat, providing a more powerful engine with
|
||||
the beauty of Django's templates. The tipping point for me was the verbosity of
|
||||
doing L10n in Django templates.
|
||||
|
||||
|
||||
Localization
|
||||
------------
|
||||
|
||||
Since we all love L10n, let's see what it looks like in Jinja templates::
|
||||
|
||||
<h2>{{ _('Reviews for {0}')|f(addon.name) }}</h2>
|
||||
|
||||
The simple way is to use the familiar underscore and string within a ``{{ }}``
|
||||
moustache block. ``f`` is an interpolation filter documented below. Sphinx
|
||||
could create a link if I knew how to do that.
|
||||
|
||||
The other method uses Jinja's ``trans`` tag::
|
||||
|
||||
{% trans user=review.user|user_link, date=review.created|datetime %}
|
||||
by {{ user }} on {{ date }}
|
||||
{% endtrans %}
|
||||
|
||||
``trans`` is nice when you have a lot of text or want to inject some variables
|
||||
directly. Both methods are useful, pick the one that makes you happy.
|
||||
|
||||
|
||||
Rendering
|
||||
---------
|
||||
|
||||
At this point, Jingo only provides two shortcuts for rendering templates.
|
||||
|
||||
.. autofunction:: jingo.render
|
||||
|
||||
The basic usage is to pass an ``HttpRequest`` and a template name. All the
|
||||
processors in ``settings.CONTEXT_PROCESSORS`` will be applied to the
|
||||
context, just like when you use a ``RequestContext`` in Django. Any extra
|
||||
keyword arguments are passed directly to ``http.HttpResponse``.
|
||||
|
||||
|
||||
Template filters provided by jingo
|
||||
----------------------------------
|
||||
|
||||
These filters are injected into all templates automatically. Template filters
|
||||
are what Jinja uses instead of "helpers" in other systems. They're just
|
||||
functions that get called from templates using the ``|`` (pipe) syntax.
|
||||
|
||||
.. automodule:: jingo.helpers
|
||||
:members:
|
|
@ -1,91 +0,0 @@
|
|||
"""Adapter for using Jinja2 with Django."""
|
||||
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
from django.template.context import get_standard_processors
|
||||
|
||||
import jinja2
|
||||
|
||||
|
||||
def get_env():
|
||||
"""Configure and return a jinja2 Environment."""
|
||||
# Mimic Django's setup by loading templates from directories in
|
||||
# TEMPLATE_DIRS and packages in INSTALLED_APPS.
|
||||
x = ((jinja2.FileSystemLoader, settings.TEMPLATE_DIRS),
|
||||
(jinja2.PackageLoader, settings.INSTALLED_APPS))
|
||||
loaders = [loader(p) for loader, places in x for p in places]
|
||||
|
||||
opts = {'trim_blocks': True,
|
||||
'extensions': ['jinja2.ext.i18n'],
|
||||
'autoescape': True,
|
||||
'auto_reload': settings.DEBUG,
|
||||
'loader': jinja2.ChoiceLoader(loaders),
|
||||
}
|
||||
|
||||
if hasattr(settings, 'JINJA_CONFIG'):
|
||||
if hasattr(settings.JINJA_CONFIG, '__call__'):
|
||||
config = settings.JINJA_CONFIG()
|
||||
else:
|
||||
config = settings.JINJA_CONFIG
|
||||
opts.update(config)
|
||||
|
||||
e = jinja2.Environment(**opts)
|
||||
# TODO: use real translations
|
||||
e.install_null_translations()
|
||||
return e
|
||||
|
||||
|
||||
def render(request, template, context=None, **kwargs):
|
||||
"""
|
||||
Shortcut like Django's render_to_response, but better.
|
||||
|
||||
Minimal usage, with only a request object and a template name::
|
||||
|
||||
return jingo.render(request, 'template.html')
|
||||
|
||||
With template context and keywords passed to
|
||||
:class:`django.http.HttpResponse`::
|
||||
|
||||
return jingo.render(request, 'template.html',
|
||||
{'some_var': 42}, status=209)
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
for processor in get_standard_processors():
|
||||
context.update(processor(request))
|
||||
rendered = env.get_template(template).render(**context)
|
||||
return http.HttpResponse(rendered, **kwargs)
|
||||
|
||||
|
||||
def load_helpers():
|
||||
"""Try to import ``helpers.py`` from each app in INSTALLED_APPS."""
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
__import__('%s.helpers' % app)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class Register(object):
|
||||
"""Decorators to add filters and functions to the template Environment."""
|
||||
|
||||
def __init__(self, env):
|
||||
self.env = env
|
||||
|
||||
def filter(self, f):
|
||||
"""Adds the decorated function to Jinja's filter library."""
|
||||
self.env.filters[f.__name__] = f
|
||||
return f
|
||||
|
||||
def function(self, f):
|
||||
"""Adds the decorated function to Jinja's global namespace."""
|
||||
self.env.globals[f.__name__] = f
|
||||
return f
|
||||
|
||||
|
||||
env = get_env()
|
||||
register = Register(env)
|
||||
|
||||
# Import down here after the env is initialized.
|
||||
from . import helpers
|
||||
load_helpers()
|
|
@ -1,46 +0,0 @@
|
|||
from django.utils.translation import ugettext as _
|
||||
from django.template.defaulttags import CsrfTokenNode
|
||||
|
||||
import jinja2
|
||||
|
||||
from jingo import register
|
||||
|
||||
|
||||
@register.function
|
||||
@jinja2.contextfunction
|
||||
def csrf(context):
|
||||
return jinja2.Markup(CsrfTokenNode().render(context))
|
||||
|
||||
|
||||
@register.filter
|
||||
def f(string, *args, **kwargs):
|
||||
"""
|
||||
Uses ``str.format`` for string interpolation.
|
||||
|
||||
>>> {{ "{0} arguments and {x} arguments"|f('positional', x='keyword') }}
|
||||
"positional arguments and keyword arguments"
|
||||
"""
|
||||
string = unicode(string)
|
||||
return string.format(*args, **kwargs)
|
||||
|
||||
|
||||
@register.filter
|
||||
def nl2br(string):
|
||||
return jinja2.Markup('<br>'.join(jinja2.escape(string).splitlines()))
|
||||
|
||||
|
||||
@register.filter
|
||||
def datetime(t, format=_('%B %d, %Y')):
|
||||
return t.strftime(format)
|
||||
|
||||
|
||||
@register.filter
|
||||
def ifeq(a, b, text):
|
||||
"""Return ``text`` if ``a == b``."""
|
||||
return jinja2.Markup(text if a == b else '')
|
||||
|
||||
|
||||
@register.filter
|
||||
def class_selected(a, b):
|
||||
"""Return ``'class="selected"'`` if ``a == b``."""
|
||||
return ifeq(a, b, 'class="selected"')
|
|
@ -1,16 +0,0 @@
|
|||
from nose.tools import eq_
|
||||
from mock import Mock, patch, sentinel
|
||||
|
||||
import jingo
|
||||
|
||||
|
||||
@patch('jingo.env')
|
||||
def test_render(mock_env):
|
||||
mock_template = Mock()
|
||||
mock_env.get_template.return_value = mock_template
|
||||
|
||||
response = jingo.render(Mock(), sentinel.template, status=32)
|
||||
mock_env.get_template.assert_called_with(sentinel.template)
|
||||
assert mock_template.render.called
|
||||
|
||||
eq_(response.status_code, 32)
|
|
@ -1,60 +0,0 @@
|
|||
"""Tests for the jingo's builtin helpers."""
|
||||
from datetime import datetime
|
||||
|
||||
from nose.tools import eq_
|
||||
|
||||
import jingo
|
||||
|
||||
|
||||
def render(s, context={}):
|
||||
t = jingo.env.from_string(s)
|
||||
return t.render(**context)
|
||||
|
||||
|
||||
def test_f():
|
||||
s = render('{{ "{0} : {z}"|f("a", z="b") }}')
|
||||
eq_(s, 'a : b')
|
||||
|
||||
|
||||
def test_nl2br():
|
||||
text = "some\ntext\n\nwith\nnewlines"
|
||||
s = render('{{ x|nl2br }}', {'x': text})
|
||||
eq_(s, "some<br>text<br><br>with<br>newlines")
|
||||
|
||||
|
||||
def test_datetime():
|
||||
time = datetime(2009, 12, 25, 10, 11, 12)
|
||||
s = render('{{ d|datetime }}', {'d': time})
|
||||
eq_(s, 'December 25, 2009')
|
||||
|
||||
s = render('{{ d|datetime("%Y-%m-%d %H:%M:%S") }}', {'d': time})
|
||||
eq_(s, '2009-12-25 10:11:12')
|
||||
|
||||
|
||||
def test_ifeq():
|
||||
eq_context = {'a': 1, 'b': 1}
|
||||
neq_context = {'a': 1, 'b': 2}
|
||||
|
||||
s = render('{{ a|ifeq(b, "<b>something</b>") }}', eq_context)
|
||||
eq_(s, '<b>something</b>')
|
||||
|
||||
s = render('{{ a|ifeq(b, "<b>something</b>") }}', neq_context)
|
||||
eq_(s, '')
|
||||
|
||||
|
||||
def test_class_selected():
|
||||
eq_context = {'a': 1, 'b': 1}
|
||||
neq_context = {'a': 1, 'b': 2}
|
||||
|
||||
s = render('{{ a|class_selected(b) }}', eq_context)
|
||||
eq_(s, 'class="selected"')
|
||||
|
||||
s = render('{{ a|class_selected(b) }}', neq_context)
|
||||
eq_(s, '')
|
||||
|
||||
|
||||
def test_csrf():
|
||||
s = render('{{ csrf() }}', {'csrf_token': 'fffuuu'})
|
||||
eq_(s, "<div style='display:none'>"
|
||||
"<input type='hidden' name='csrfmiddlewaretoken' value='fffuuu' />"
|
||||
"</div>")
|
|
@ -1,10 +0,0 @@
|
|||
from mock import patch, sentinel
|
||||
|
||||
import jingo.views
|
||||
|
||||
|
||||
@patch('jingo.render')
|
||||
def test_direct_to_template(mock_render):
|
||||
request = sentinel.request
|
||||
jingo.views.direct_to_template(request, 'base.html', x=1)
|
||||
mock_render.assert_called_with(request, 'base.html', {'x': 1})
|
|
@ -1,5 +0,0 @@
|
|||
import jingo
|
||||
|
||||
|
||||
def direct_to_template(request, template, **kwargs):
|
||||
return jingo.render(request, template, kwargs)
|
|
@ -8,3 +8,4 @@ south
|
|||
|
||||
-e git://github.com/jbalogh/django-multidb-router.git#egg=django-multidb-router
|
||||
-e git://github.com/jbalogh/django-cache-machine.git#egg=django-cache-machine
|
||||
-e git://github.com/jbalogh/jingo.git#egg=jingo
|
||||
|
|
Загрузка…
Ссылка в новой задаче