{
"info": {
"author": "Tom Gracey",
"author_email": "tomgracey@gmail.com",
"bugtrack_url": null,
"classifiers": [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6"
],
"description": "# Template-Nest Templating System\n\n## TL;DR\nYou should definitely use Template-Nest because it's the most logical templating system in existence. \n\n* Create templates that have no code in them whatsoever. No \"template ifs\", \"template loops\". No \"template includes\". Nothing but holes to fill in.\n* No code in your templates means they are language independent. Use the same templates with Java, Ruby, Perl, Javascript...\n* If templating html, produce only ordinary html files that can be displayed independently in a browser. (ie. no files with weird extensions)\n* Build views as a tree structure of templates (exactly as the DOM does!) limiting the number of templates needed and avoiding repetition.\n* There is no \"template language\" to use or learn\n* Eliminate the confusion of where to put processing. Get rid of the mess this confusion produces.\n\nFor intrigue, also see: \"Building a template-less platform with Template-Nest\" at the bottom of this page.\n\n## SYNOPSIS\n\n~~~html\n\t\n\t\n\t\t
\n\t\t\t\n\t\t\n\n\t\t\n\t\t\t<% contents %>\n\t\t\n\t\n~~~ \n\n```html\n\t\n\t\n
\n\n```\n\nie. `defaults` allows you to preload your `nest` with any values which you expect to remain constant throughout your project.\n\n\nYou can also *namespace* your default values. Say you think it's a better idea to differentiate parameters coming from config from those you are expecting to explicitly pass in. You can do something like this:\n\n### link.html:\n\n```html\n \">Soup of the day is !\n```\n\nie you are reserving the `config.` prefix for parameters you are expecting to come from the config. To set the defaults in this case you could do this:\n\n```python\n\n defaults = {\n 'config.soup_website_url' => 'http://www.example.com/soup-addicts',\n 'config.some_other_url' => 'http://www.example.com/some-other-url'\n \n #...\n }\n\n nest.defaults( defaults )\n\n```\n\nbut writing 'config.' repeatedly is a bit effortful, so Template-Nest allows you to do the following:\n\n\n```html\n\n defaults = {\n\n 'config': {\n\n 'soup_website_url': 'http://www.example.com/soup-addicts',\n 'some_other_url': 'http://www.example.com/some-other-url'\n \n #...\n },\n\n 'some_other_namespace': {\n\n # other params?\n\n }\n\n }\n\n nest.defaults( defaults )\n nest.defaults_namespace_char('.') # not actually necessary, as '.' is the default\n\n```\n\nNow Template-Nest will replace `config.soup_website_url` with what it finds in\n\n```python\n\n defaults[config][soup_website_url]\n\n```\n\nSee `defaults_namespace_char`.\n\n\n\n### defaults_namespace_char\n\nAllows you to provide a \"namespaced\" defaults dict rather than just a flat one. ie instead of doing this:\n\n```python\n\n nest.defaults({\n 'variable1': 'value1',\n 'variable2': 'value2',\n\n # ...\n\n })\n```\n\nYou can do this:\n\n\n```python\n\n nest.defaults({ \n 'namespace1': {\n 'variable1': 'value1',\n 'variable2': 'value2'\n },\n\n 'namespace2': {\n 'variable1': 'value3',\n 'variable2': 'value4\n }\n })\n```\n\nSpecify your `defaults_namespace_char` to tell Template-Nest how to match these defaults in your template:\n\n```python\n nest.defaults_namespace_char('-')\n```\n\nso now the token\n\n```\n <% namespace1-variable1 %>\n```\n\nwill be replaced with `value2`. Note the default `defaults_namespace_char` is a fullstop (period) character.\n\n\n### error_on_bad_params\n\nIf you attempt to populate a template with a parameter that doesn't exist (ie the name is not found in the template) then this normally results in an error. This default behaviour is recommended in most circumstances as it guards against typos and sloppy code. However, there may be circumstances where you want processing to carry on regardless. In this case set `error_on_bad_params` to `False`:\n\n```python\n nest.error_on_bad_params( False )\n```\n\n### escape_char\n\nOn rare occasions you may actually want to use the exact character string you are using for your token delimiters in one of your templates. e.g. say you are using token_delims `[%` and `%]`, and you have this in your template:\n\n```\n Hello [% name %],\n\n did you know we are using token delimiters [% and %] in our templates?\n\n lots of love\n Roger\n```\n\nClearly in this case we are a bit stuck because Template-Nest is going to think `[% and %]` is a token to be replaced. Not to worry, we can *escape* the opening token delimiter:\n\n```\n Hello [% name %],\n\n did you know we are using token delimiters \\[% and %] in our templates?\n\n lots of love\n Roger\n```\n\nIn the output the backslash will be removed, and the `[% and %]` will get printed verbatim. \n\n`escape_char` is set to be a backslash by default. This means if you want an actual backslash to be printed, you would need a double backslash in your template.\n\nYou can change the escape character if necessary:\n\n```python\n nest.escape_char('X')\n```\n\nor you can turn it off completely if you are confident you'll never want to escape anything. Do so by passing in the empty string to `escape_char`:\n\n```python\n nest.escape_char('')\n```\n\n\n### fixed_indent\n\nIntended to improve readability when inspecting nested templates. Consider the following example:\n\nbox.html:\n\n```html\n
\n \n
\n```\n\n\nphoto.html:\n\n```html\n
\n

\n
\n```\n\nIn the python:\n\n\n```python\n nest = Nest({\n token_delims => ['']\n })\n \n nest.render({\n 'NAME': 'box',\n 'contents': 'image'\n })\n```\n\nOutput:\n\n```html\n
\n
\n

\n
\n
\n```\n\nNote the ugly indenting. In fact this is completely correct behaviour in terms of faithfully replacing the token \n\n```html\n \n```\n\nwith the `photo.html` template - the nested template starts exactly from where the token was placed, and each character is printed verbatim, including the new lines.\n\nHowever, a lot of the time we really want output that looks like this:\n\n```html\n
\n
\n # the indent is maintained\n
# for every line in the child\n
# template\n```\n\nTo get this more readable output, then set `fixed_indent` to `True`:\n\n```python\n nest.fixed_indent( True )\n```\n\nBear in mind that this will result in extra space characters being inserted into the output.\n\n\n\n### name_label\n\nThe default is `NAME` (all-caps, case-sensitive). Of course if `NAME` is interpreted as the filename of the template, then you can't use `NAME` as one of the variables in your template. ie\n\n```\n <% NAME %>\n```\n\nwill never get populated. If you really are adamant about needing to have a template variable called `NAME` - or you have some other reason for wanting an alternative label point to your template filename, then you can set name_label:\n\n```python\n nest.name_label( 'MYLABEL' )\n\n #and now\n\n component = {\n 'MYLABEL': 'name_of_my_component'\n #...\n }\n```\n\n### render\n\nConvert a template structure to output text. Expects a dict (or list) containing dicts/lists/plain text.\n\ne.g.\n\nwidget.html:\n\n```html\n
\n```\n\n\nwidget_body.html:\n```html\n
\n
I am the widget body!
\n
\n
\n```\n\n```python\n widget = {\n 'NAME': 'widget',\n 'widget_body': {\n 'NAME': 'widget_body',\n 'some_widget_property': 'Totally useless widget'\n }\n }\n\n print( nest.render( widget ) )\n```\n\nOutput:\n\n```html\n
\n```\n\n\n### show_labels\n\nGet/set the show_labels property. This is a boolean with default `False`. Setting this to `True` results in adding comments to the output so you can identify which template output text came from. This is useful in development when you have many templates. E.g. adding \n\n```python\n nest.show_labels( True )\n```\n\nto the example in the synopsis results in the following:\n\n```html\n \n \n \n \n \n\n \n \n \n
\n First nested box\n
\n \n\n \n
\n Second nested box\n
\n \n\n \n \n \n```\n\nWhat if you're not templating html, and you still want labels? Then you should set `comment_delims` to whatever is appropriate for the thing you are templating.\n\n\n\n### template_dir\n\nGet/set the dir where Template-Nest looks for your templates. E.g.\n\n```python\n nest.template_dir( '/my/template/dir' )\n```\n\nNow if I have\n\n```python\n component = {\n NAME => 'hello',\n #...\n }\n```\n\nand `template_ext = '.html'`, we'll expect to find the template at `/my/template/dir/hello.html`.\n\n\nNote that if you have some kind of directory structure for your templates (ie they are not all in the same directory), you can do something like this:\n\n```python\n component = {\n 'NAME': '/my/component/location',\n 'contents': 'some contents or other'\n }\n```\n\nTemplate-Nest will then prepend `NAME` with `template_dir`, append `template_ext` and look in that location for the file. So in our example if `template_dir = '/my/template/dir'` and `template_ext = '.html'` then the template file will be expected to exist at `/my/template/dir/my/component/location.html`.\n\nOf course if you want components to be nested arbitrarily, it might not make sense to contain them in a prescriptive directory structure. \n\n\n### template_ext\n\nGet/set the template extension. This is so you can save typing your template extension all the time if it's always the same. The default is `.html` - however, there is no reason why this templating system could not be used to construct any other type of file (or why you could not use another extension even if you were producing html). So e.g. if you are wanting to manipulate javascript files:\n\n```python\n nest.template_ext('.js')\n\n # then\n\n js_file = {\n 'NAME': 'some_js_file'\n #...\n }\n```\n\nSo here Template-Nest will look in `template_dir` for `some_js_file.js`.\n\nIf you don't want to specify a particular `template_ext` (presumably because files don't all have the same extension) - then you can do\n\n```python\n nest.template_ext('')\n```\n\nIn this case you would need to have NAME point to the full filename. ie\n\n```python\n nest.template_ext('')\n\n component = {\n NAME => 'hello.html',\n #...\n }\n```\n\n\n### token_delims\n\nGet/set the delimiters that define a token (to be replaced). `token_delims` is a 2 element list - corresponding to the opening and closing delimiters. For example\n\n```python\n nest.token_delims( ['[%', '%]'] )\n```\n\nwould mean that Template-Nest would now recognise and interpolate tokens in the format\n\n```\n [% token_name %]\n```\n\nThe default token_delims are the mason style delimiters `<%` and `%>`. Note that for `HTML` the token delimiters `` make a lot of sense, since they allow raw templates (ie that have not had values filled in) to render as good `HTML`.\n\n\n## Building a template-less platform with Template-Nest\n\nRecently someone reviewing Template-Nest suggested it wasn't a good system because \"you end up with thousands of tiny templates\". After some consideration I realised not only is this claim incorrect, the truth is pretty much the opposite: while you do end up with small templates (surely a good thing), you don't end up with more of them. In fact, you ought to end up with substantially less.\n\nThis is because once you've created the smallest units you expect to repeat, you then collect those together to create larger units - and so on. In other words you create a tree structure, which will tend to have the smallest repeating units at the bottom (the actual templates). You are in fact creating your pages with the minimal amount of repetition.\n\nAt this point I began to wonder what you would end up with if you took this philosophy to its extreme. In other words you start with the smallest possible building blocks imaginable, and build your pages from there. The result is quite interesting. I am not sure if it is a purely academic exercise for now, as I haven't tried to build it. However, so far I've been able to think of a response to every objection I could dream up. So it remains for someone to tell me why they think the scheme I am about to propose is not a good idea.\n\nIt goes like this: you can build any html page with the following 3 \"templates\":\n\n* \"standalone\":\n\n```\n<[% tag %][% attributes %]>\n```\n\n* \"closing\":\n\n```\n<[% tag %][% attributes %]>[% contents %][% tag %]>\n```\n\n\n* \"attribute\":\n\n```\n [% key %]=\"[% value %]\"\n```\n\n(note the single space character at the beginning of the `attribute` template.)\n\nThe main purpose of coming up with this was as a \"riducto ad absurdum\" argument to respond to the \"thousands of templates\" claim: its clear from this that the finer-grained you make your templates, the *less* of them you need.\n\nThen I started wondering if this is isn't just an academic idea, but would actually work practically, and might even be a considerably superior method of rendering html?\n\nThe templates are so small, they barely qualify as templates. It would be just as easy to stick them in constants in your code (given html itself is not likely to change any time soon). Hence this is why I referred to this system as \"template-less\". We go down to such tiny granularity that we end up with repeating units that might as well just be supplied directly by code.\n\nRidiculous, you think? Actually, due to the \"tree effect\" phenomenon I already mentioned, it seems really quite quick to get to page-sized output. Consider this:\n\n```python\ndef css_tag( href ):\n\n return nest.render({\n 'NAME': 'standalone',\n 'tag': 'link',\n 'attributes': [{\n 'NAME': 'attribute'\n 'key': 'href',\n 'value': href\n }, {\n 'NAME': 'attribute'\n 'key': 'href',\n 'value': href\n }]\n })\n\n\ndef script_tag( href ):\n\n return nest.render({\n 'NAME': 'closing',\n 'tag': 'script',\n 'attributes': [{\n 'NAME': 'attribute',\n 'key': 'type',\n 'value': 'text/javascript'\n }, {\n 'NAME': 'attribute',\n 'key': 'src',\n 'value': href\n }\n })\n\n\ndef section_container( contents ):\n return nest.render({\n 'NAME': 'closing',\n 'tag': 'div',\n 'attributes': {\n 'NAME': 'attribute',\n 'key': 'class',\n 'value': 'section'\n },\n 'contents': contents\n })\n)\n\ndef page_header( title ):\n\n return nest.render({\n 'NAME': 'closing',\n 'tag': 'header',\n 'contents': [\n css_tag('/css/local.css'),\n css_tag('/css/third_party.css'),\n script_tag(/js/local.js'),\n script_tag(/js/third_party.js'),\n { 'NAME': 'closing',\n 'contents': title\n } \n ]\n })\n \ndef page( title,contents,footer ):\n\n return nest.render([\n page_header(title),\n {\n 'NAME': 'closing',\n 'tag': 'body',\n 'contents': [{\n section_container( contents ),\n section_container( footer )\n }]\n }\n ])\n \n # etc...\n```\n\n\nSo in this scheme you have functions that hand you the individual tags. Then functions that call those functions... and so on. Pretty quickly you have functions which produce much larger scale content. (In fact, doesn't the size of output increase exponentially with each nested function call? Say your first function produces a single tag, and the second invokes a set of those tags. The third produces a set of sets. The fourth...)\n\nWhat you've really done is swap out flat relatively immutable text files for dynamic, modifiable functions. Take for example the `script_tag` function above. That function is now going to get called for every single script tag on the page. Want to change `text/javascript` to something else? You can do it in a second in our template-less system. Can you do that with large flat templates? It's going to be a major search and replace job.\n\nActually you can intervene and make modifications at any point in the tree structure, with a change made only in one place, so that no more or less than the desired elements are affected. It is DRY taken to it's logical conclusion.\n\n### Objections?\n\n1. Overhead\n\n The main objection that immediately sprung to my mind is processing overhead. \"Surely you don't seriously want to build large pages by individually rendering each and every single element? Those bottom level functions are going to get called an insane number of times!\"\n\n That's true, but what about if we introduce caching? Suppose we create some kind of wrapper around those functions to return a cached value if we've seen the input before? So for example, the first time you call `css_tag` with `href = \"example.com/css/my.css\"` it actually performs the render, but the second time it finds the value corresponding to `example.com/css/my.css` in the cache, and delivers the chunk of html directly? That cuts the function call chain short, and delivers the html without hitting the lower level functions at all. Actually, isn't this fantastically efficient? Now you're only going to render those specific chunks you've never seen before. \n\n (Ok - I guess there are going to be limits to how much you can cache, but still...)\n\n2. \"You end up with a lot of functions!\"\n\n I don't actually think this is going to be any kind of issue, but I'm trying to imagine what an objector might say. Yes, I think you'll have more functions - you'll probably have an extra module or so full of those low level functions that deliver individual tags. But so what?\n\n3. \"It's difficult to visualise page output.\"\n\n If you like looking at files in a text editor, then this is true. However, it would be quite easy to have some kind of test system which meant you could call any individual function and look at the output. Potentially you could have it render in \"debug\" mode so it inserted comments saying which function each chunk came from (similar to Template-Nest's `show_comments` method.\n\n4. ...?\n\n I don't know! Tell me why this wouldn't work? If you can't, maybe I'll try and build it...\n\n\n## COPYRIGHT AND LICENSE\n\nCopyright © 2019 by Tom Gracey\n\nThis library is free software; you can redistribute it and/or modify\nit under the MIT License.",
"description_content_type": "text/markdown",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "",
"keywords": "",
"license": "MIT",
"maintainer": "",
"maintainer_email": "",
"name": "template-nest",
"package_url": "https://pypi.org/project/template-nest/",
"platform": "",
"project_url": "https://pypi.org/project/template-nest/",
"project_urls": null,
"release_url": "https://pypi.org/project/template-nest/1.0.0/",
"requires_dist": null,
"requires_python": "",
"summary": "Represent a nested structure of templates in a dict",
"version": "1.0.0"
},
"last_serial": 5999959,
"releases": {
"1.0.0": [
{
"comment_text": "",
"digests": {
"md5": "cfd110666daed57982af1c899fc15584",
"sha256": "121984358633634c0e635fb5acdf0dc6c1a35b6292bf817bbe1dca4cedec1957"
},
"downloads": -1,
"filename": "template-nest-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "cfd110666daed57982af1c899fc15584",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 36502,
"upload_time": "2019-10-19T14:16:54",
"url": "https://files.pythonhosted.org/packages/d2/ab/f0d95e176925cb9ee8078d9c381ffc4958a2f9db36051e254bbe285025ae/template-nest-1.0.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "cfd110666daed57982af1c899fc15584",
"sha256": "121984358633634c0e635fb5acdf0dc6c1a35b6292bf817bbe1dca4cedec1957"
},
"downloads": -1,
"filename": "template-nest-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "cfd110666daed57982af1c899fc15584",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 36502,
"upload_time": "2019-10-19T14:16:54",
"url": "https://files.pythonhosted.org/packages/d2/ab/f0d95e176925cb9ee8078d9c381ffc4958a2f9db36051e254bbe285025ae/template-nest-1.0.0.tar.gz"
}
]
}