Permalink
Browse files

update docs for templetor

  • Loading branch information...
1 parent cbd51cb commit a0c7d52e1c701cfa3c645a7c28cba3ffea5a2bcb @asldevi asldevi committed Nov 6, 2013
Showing with 257 additions and 29 deletions.
  1. +257 −29 docs/templating.rst
View
@@ -1,64 +1,292 @@
-Templates
-=========
+Templating
+==========
-Writing HTML from inside Python can get cumbersome; it's much more fun to write Python from inside HTML. Luckily, web.py makes that pretty easy.
+There are almost as many Python templating systems as there are web
+frameworks (and, indeed, it seems like many templating systems are
+adopting web framework-like features). The following are the goals of `templetor`, which is the (codename of) templating system of web.py.
-Let's make a new directory for our templates (we'll call it templates). Inside, make a new file whose name ends with HTML (we'll call it index.html). Now, inside, you can just write normal HTML:
-<em>Hello</em>, world!
-Or you can use web.py's templating language to add code to your HTML:
+1. The templating system has to *look* decent. No ``<%#foo#%>`` crud.
+2. Reuse Python terms and semantics as much as possible.
+3. Expressive enough to do real computation.
+4. Usable for any text language, not just HTML and XML.
+
+And requirements for the implementation as well:
+
+4. Sandboxable so that you can let untrusted users write templates.
+5. Simple and fast implementation.
+
+So here it is.
+
+Variable substitution
+---------------------
+
+::
+
+ Look, a $string.
+ Hark, an ${arbitrary + expression}.
+ Gawk, a $dictionary[key].function('argument').
+ Cool, a $(limit)ing.
+
+ Stop, \$money isn't evaluated.
+
+We use basically the same semantics as (rejected) `PEP
+215 <http://www.python.org/peps/pep-0215.html>`__. Variables can go
+anywhere in a document.
+
+Newline suppression
+-------------------
+
+::
+
+ If you put a backslash \
+ at the end of a line \
+ (like these) \
+ then there will be no newline.
+
+renders as all one line.
+
+Expressions
+-----------
::
- $def with (name)
+ Here are some expressions:
+
+ $for var in iterator: I like $var!
- $if name:
- I just wanted to say <em>hello</em> to $name.
+ $if times > max:
+ Stop! In the name of love.
$else:
- <em>Hello</em>, world!
+ Keep on, you can do it.
+
+ That's all, folks.
+
+All your old Python friends are here: ``if``, ``while``, ``for``,
+``else``, ``break``, ``continue``, and ``pass`` also act as you'd
+expect. (Obviously, you can't have variables named any of these.) The
+Python code starts at the ``$`` and ends at the ``:``. The ``$`` has to
+be at the beginning of the line, but that's not such a burden because of
+newline suppression (above).
+
+Also, we're very careful about spacing -- all the lines will render with
+no spaces at the beginning. (Open question: what if you want spaces at
+the beginning?) Also, a trailing space might break your code.
+
+There are a couple changes from Python: ``for`` and ``while`` now take
+an ``else`` clause that gets called if the loop is never evaluated.
+
+(Possible feature to add: Django-style for loop variables.)
+
+Comments
+--------
+
+::
+
+ $# Here's where we hoodwink the folks at home:
+
+ Please enter in your deets:
+
+ CC: [ ] $#this is the important one
+ SSN: $#Social Security Number#$ [ ]
+
+Comments start with ``$#`` and go to ``#$`` or the end of the line,
+whichever is first.
+
+Code
+----
+
+**NOTE: This feature has not been implemented in the current web.py
+implementation of templetor.**
+
+::
+
+ Sometimes you just need to break out the Python.
+
+ $ mapping = {
+ $ 'cool': ['nice', 'sweet', 'hot'],
+ $ 'suck': ['bad', 'evil', 'awful']
+ $ }
+
+ Isn't that $mapping[thought]?
+ That's$ del mapping $ fine with me.
+
+ $ complicatedfunc()
+
+ $ for x in bugs:
+ $ if bug.level == 'severe':
+ Ooh, this one is bad.
+ $ continue
+ And there's $x...
+
+**Body of loops have to be indented with exactly 4 spaces.**
+
+Code begins with a ``$`` and a space and goes until the next ``$`` or
+the end of the line, whichever comes first. Nothing ever gets output if
+the first character after the ``$`` is a space (so ``complicatedfunc``
+above doesn't write anything to the screen like it might without the
+space).
+
+Python integration
+------------------
+
+A template begins with a line like this:
+
+::
+
+ $def with (name, title, company='BigCo')
-As you can see, the templates look a lot like Python files except for the def with statement at the top (saying what the template gets called with) and the $s placed in front of any code. Currently, the templetor requires the $def statement to be the first line of the file.
+which declares that the template takes those arguments. (The ``with``
+keyword is special, like ``def`` or ``if``.)
+**Don't forget to put spaces in the definition**
-Now go back to code.py. Under the first line, add:
+The following *will not work*:
::
- render = web.template.render('templates/')
+ $def with (name,title,company='BigCo')
-This tells web.py to look for templates in your templates directory. Then change index.GET to:
+Inside Python, the template looks like a function that takes these
+arguments. It returns a storage object with the special property that
+evaluating it as a string returns the value of the body of the template.
+The elements in the storage object are the results of the ``def``\ s and
+the ``set``\ s.
+
+Perhaps an example will make this clearer. Here's a template, "entry":
+
+::
+
+ $def with (post)
+
+ $var title: $post.title
+
+ <p>$markdown(post.body)</p>
+
+ <p class="byline">by $post.author</p>
+
+Here's another; "base":
+
+::
+
+ $def with (self)
+ <html><head>
+ <title>$self.title</title>
+ </head><body>
+ <h1>$self.title</h1>
+
+ $:self
+ </body></html>
+
+Now let's say we compile both from within Python, the first as
+``entry``, the second as ``base``. Here's how we might use them:
+
+::
+
+ print base( entry( post ) )
+
+``entry`` takes the argument post and returns an object whose string
+value is a bit of HTML showing the post with its title in the property
+``title``. ``base`` takes this object and places the title in the
+appropriate place and displays the page itself in the body of the page.
+The Python code prints out the result.
+
+*Where did ``markdown`` come from? It wasn't passed as an argument.* You
+can pass a list of functions and variables to the template compiler to
+be made globally available to templates. *Why $:self?* See below
+
+Here's an example:
+
+::
+
+ import template
+ render = template.render('templates/')
+ template.Template.globals['len'] = len
+
+ print render.base(render.message('Hello, world!'))
+
+The first line imports templetor. The second says that our templates are
+in the directory ``templates/``. The third give all our templates access
+to the ``len`` function. The fourth grabs the template ``message.html``,
+passes it the argument ``'Hello, world!'``, passes the result of
+rendering it to `mcitp <http://www.buyitcert.com/mcitp.html>`__ the
+template ``base.html`` and prints the result. (If your templates don't
+end in ``.html`` or ``.xml``, templetor will still find them, but it
+won't do its automatic HTML-encoding.)
+
+Turning Off Filter
+------------------
+
+By default ``template.render`` will use ``web.websafe`` filter to do
+HTML-encoding. To turn it off, put a : after the $ as in:
+
+::
+
+ $:form.render()
+
+Output from form.render() will be displayed as is.
+
+::
+
+ $:fooBar $# fooBar = <span>lorem ipsum</span>
+
+Output from variable in template will be displayed as is.
+
+Including / nesting templates
+-----------------------------
+
+If you want to nest one template within another, you nest the
+``render()`` calls, and then include the variable (unfiltered) in the
+page. In your handler:
::
- name = 'Bob'
- return render.index(name)
+ print render.foo(render.bar())
-('index' is the name of the template and 'name' is the argument passed to it)
+or (to make things a little more clear):
-Visit your site and it should say hello to Bob.
+::
+
+ barhtml = render.bar()
+ print render.foo(barhtml)
-But let's say we want to let people enter their own name in. Replace the two lines we added above with:
+Then in the template ``foo.html``:
::
- i = web.input(name=None)
- return render.index(i.name)
+ $def with (bar)
+ html goes here
+ $:bar
+ more html
-Visit / and it should say hello to the world. Visit /?name=Joe and it should say hello to Joe.
+This replaces the ``$:bar`` with the output of the ``render.bar()`` call
+(which is why it must be ``$:``/unfiltered, so
+`ccnp <http://www.buyitcert.com/ccnp.html>`__ that you get un-encoded
+HTML (unless you want something else of course)). You can pass variables
+in, in the same way:
-Of course, having that ? in the URL is kind of ugly. Instead, change your URL line at the top to:
+::
-'/(.*)', 'index'
-and change the definition of index.GET to:
+ print render.foo(render.bar(baz), qux)
+
+In the template bar (``bar.html``):
::
- def GET(self, name):
- return render.index(name)
+ $def with (baz)
+ bar stuff goes here + baz
+
+In template foo (``foo.html``):
-and delete the line setting name. Now visit /Joe and it should say hello to Joe.
+::
+
+ $def with (bar, qux)
+ html goes here
+ $:bar
+ Value of qux is $qux
Escaping
-````````
+--------
+
web.py automatically escapes any variables used in templates, so that if for some reason name is set to a value containing some HTML, it will get properly escaped and appear as plain text. If you want to turn this off, write $:name instead of $name.

0 comments on commit a0c7d52

Please sign in to comment.