├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── changelog.txt ├── concepts.txt ├── conf.py ├── configuration.txt ├── contrib │ ├── baseencode.txt │ ├── cal.txt │ ├── comparison.txt │ ├── context.txt │ ├── feeds.txt │ ├── gchart.txt │ ├── generic_content.txt │ ├── generic_markup.txt │ ├── hash.txt │ ├── index.txt │ ├── mapreduce.txt │ ├── math.txt │ ├── op.txt │ ├── pygmentize.txt │ ├── rand.txt │ ├── regex.txt │ └── serializers.txt ├── decorators.txt ├── features.txt ├── index.txt ├── make.bat └── testing.txt ├── example_project ├── __init__.py ├── app │ ├── __init__.py │ ├── models.py │ ├── templatetags │ │ ├── __init__.py │ │ └── app_tags.py │ └── tests.py ├── manage.py ├── media │ └── index.html ├── settings.py ├── templates │ ├── feeds.html │ ├── flatpages │ │ └── default.html │ └── unittest.html ├── test.db └── urls.py ├── native_tags ├── __init__.py ├── contrib │ ├── LICENSE.txt │ ├── __init__.py │ ├── _django.py │ ├── _markup.py │ ├── baseencode.py │ ├── cal.py │ ├── comparison.py │ ├── context.py │ ├── feeds.py │ ├── gchart.py │ ├── generic_content.py │ ├── generic_markup.py │ ├── hash.py │ ├── mapreduce.py │ ├── math_.py │ ├── op.py │ ├── pygmentize.py │ ├── rand.py │ ├── regex.py │ └── serializers.py ├── decorators.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── nativelib.py ├── models.py ├── nodes.py ├── registry.py ├── settings.py ├── templatetags │ ├── __init__.py │ └── native.py └── tests.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | docs/build 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Justin Quick 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | * Neither the name of the author nor the names of other 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include native_tags * 2 | include README.rst 3 | global-exclude *pyc 4 | recursive-exclude example_project * -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc test upload-docs 2 | 3 | all: clean-pyc test 4 | 5 | test: 6 | cd example_project; ./manage.py test native_tags 7 | 8 | dist: 9 | python setup.py sdist 10 | 11 | rmpyc: 12 | rm -rf `find . -name '*.pyc'`; rm -rf `find . -name '*.pyo'` 13 | 14 | html: 15 | cd docs; make html 16 | cp -r docs/build/html/* export/ 17 | rm -rf export/sphinx_sources export/sphinx_static 18 | mv export/_sources export/sphinx_sources 19 | mv export/_static export/sphinx_static 20 | perl -i -pe 's/_static/sphinx_static/g' `find export/ -name '*.html'` 21 | perl -i -pe 's/_sources/sphinx_sources/g' `find export/ -name '*.html'` 22 | 23 | upload: 24 | cd export/; git pull origin master; git add *; git commit -a -m 'doc update'; git push origin master 25 | 26 | tex: 27 | cd docs; make latex; make all-pdf 28 | 29 | #cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html 30 | #scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/ 31 | #scp -r docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf 32 | #scp -r docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/ 33 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Native Tags Documentation 2 | ========================= 3 | 4 | :Authors: 5 | Justin Quick 6 | :Version: 0.5 7 | 8 | :: 9 | 10 | pip install django-native-tags==0.5.3 11 | 12 | Django Native Tags is a way of making the creation of template tags stupidly simple. 13 | Tags are "native" because there is a much closer relationship between the tag in the template and a Python function behind the scenes. 14 | The app abstracts the work needed to parse out the templatetag syntax into a useable form for a Python function. 15 | For example: 16 | 17 | Define an arbitrary function in your templatetags:: 18 | 19 | def add(x, y): 20 | return x + y 21 | add.function = True 22 | 23 | Use the function in your template:: 24 | 25 | {% add 1000 100 as num %} 26 | {{ num|intcomma }} 27 | 28 | Which outputs:: 29 | 30 | 1,100 31 | 32 | Other features of Native Tags: 33 | 34 | * Keyword argument parsing 35 | * Quoted strings parsed correctly 36 | * Add templatetags to Django's builtins (no ``{% load %}`` required) 37 | * Auto resolve of template variables 38 | * Universal and per-tag caching 39 | * Straightforward template tag unittesting 40 | * Error tolerant by letting you specify a fallback return value 41 | 42 | The real power of the module comes in the contrib add ons which has tons of tags for various uses including 43 | comparisons, regex operations, math operations, and much more. By default it is a functional replacement to `James Bennett`_'s `django-template-utils`_ right out of the box 44 | 45 | .. _django-template-utils: http://bitbucket.org/ubernostrum/django-template-utils/ 46 | .. _James Bennett: http://www.b-list.org/ 47 | 48 | For full documentation, checkout the `Sphinx doc`_ 49 | 50 | .. _fancy Sphinx doc: http://justquick.github.com/django-native-tags/ 51 | 52 | Email me with any questions/concerns/issues/hate mail: 53 | 54 | justquick [@] the gmails .com 55 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DjangoNativeTags.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DjangoNativeTags.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /docs/changelog.txt: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | .. highlight:: django 3 | 4 | Change Log 5 | =========== 6 | 7 | v0.4 8 | ----- 9 | 10 | * Reorganized loader to index tags at the beginning and only load tags at runtime, good call legutierr! 11 | * Per tag unittesting with the ``test`` funciton attribute and added lots more tests to use the new feature 12 | * Added operator tags to ``native_tags.contrib.op`` 13 | 14 | v0.3 15 | ---- 16 | * ``smart_if`` tag removed in favor of Django v1.2's updated if tag. 17 | * Universal and per-tag caching 18 | * Bugfixes to numerous contrib packages 19 | 20 | v0.2 21 | ---- 22 | 23 | * Filter expressions render correctly 24 | * ``math_`` and ``smart_if`` contrib packages added 25 | * ``NATIVE_CONTRIB`` setting changed to ``NATIVE_TAGS`` 26 | * No more ``NATIVE_LOAD`` setting 27 | * Ability to have custom names for comparison tags (no prepended ``if_``) 28 | 29 | v0.1 30 | ----- 31 | 32 | * ``django-template-tags`` functionality 33 | * Auto load tags 34 | * Parsing with ``shlex`` 35 | * Auto resolve template arguments and keyword arguments 36 | * ``function``, ``comparison``, ``block``, and ``filter`` native tag types -------------------------------------------------------------------------------- /docs/concepts.txt: -------------------------------------------------------------------------------- 1 | .. _concepts: 2 | .. highlight:: django 3 | 4 | Types of Tags 5 | =============== 6 | 7 | .. automodule:: native_tags.nodes 8 | 9 | 10 | .. _function-tags: 11 | 12 | Function tags 13 | -------------- 14 | 15 | .. autofunction:: do_function 16 | 17 | 18 | .. _comparison-tags: 19 | 20 | Comparison tags 21 | ---------------- 22 | 23 | .. autofunction:: do_comparison 24 | 25 | 26 | .. _block-tags: 27 | 28 | Block tags 29 | -------------- 30 | 31 | .. autofunction:: do_block 32 | 33 | 34 | .. _filter-tags: 35 | 36 | Filter tags 37 | -------------- 38 | 39 | Native Filter tags are pretty much the same as regular Django filter tags. 40 | There is no special sauce here because the arguments that you can pass filter tags is very limited and is currently being expanded `in this issue `_. 41 | Filters take the piped value as the first argument and as of now *only one* extra argument like so:: 42 | 43 | 44 | 45 | {{ value|filter:arg }} 46 | 47 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Django Native Tags documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Oct 30 03:18:09 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('..')) 20 | os.environ['DJANGO_SETTINGS_MODULE'] = 'example_project.settings' 21 | import native_tags 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # Add any Sphinx extension module names here, as strings. They can be extensions 25 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 26 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] 27 | 28 | # Add any paths that contain templates here, relative to this directory. 29 | templates_path = ['templates'] 30 | 31 | # The suffix of source filenames. 32 | source_suffix = '.txt' 33 | 34 | # The encoding of source files. 35 | #source_encoding = 'utf-8' 36 | 37 | # The master toctree document. 38 | master_doc = 'index' 39 | 40 | # General information about the project. 41 | project = u'Django Native Tags' 42 | copyright = u'2010, Justin Quick ' 43 | 44 | # The version info for the project you're documenting, acts as replacement for 45 | # |version| and |release|, also used in various other places throughout the 46 | # built documents. 47 | # 48 | # The short X.Y version. 49 | version = release = native_tags.__version__ 50 | 51 | # The language for content autogenerated by Sphinx. Refer to documentation 52 | # for a list of supported languages. 53 | #language = None 54 | 55 | # There are two options for replacing |today|: either, you set today to some 56 | # non-false value, then it is used: 57 | #today = '' 58 | # Else, today_fmt is used as the format for a strftime call. 59 | #today_fmt = '%B %d, %Y' 60 | 61 | # List of documents that shouldn't be included in the build. 62 | #unused_docs = [] 63 | 64 | # List of directories, relative to source directory, that shouldn't be searched 65 | # for source files. 66 | exclude_trees = ['build'] 67 | 68 | # The reST default role (used for this markup: `text`) to use for all documents. 69 | #default_role = None 70 | 71 | # If true, '()' will be appended to :func: etc. cross-reference text. 72 | #add_function_parentheses = True 73 | 74 | # If true, the current module name will be prepended to all description 75 | # unit titles (such as .. function::). 76 | #add_module_names = True 77 | 78 | # If true, sectionauthor and moduleauthor directives will be shown in the 79 | # output. They are ignored by default. 80 | #show_authors = False 81 | 82 | # The name of the Pygments (syntax highlighting) style to use. 83 | pygments_style = 'sphinx' 84 | 85 | # A list of ignored prefixes for module index sorting. 86 | #modindex_common_prefix = [] 87 | 88 | 89 | # -- Options for HTML output --------------------------------------------------- 90 | 91 | # The theme to use for HTML and HTML Help pages. Major themes that come with 92 | # Sphinx are currently 'default' and 'sphinxdoc'. 93 | html_theme = 'default' 94 | 95 | # Theme options are theme-specific and customize the look and feel of a theme 96 | # further. For a list of options available for each theme, see the 97 | # documentation. 98 | #html_theme_options = {} 99 | 100 | # Add any paths that contain custom themes here, relative to this directory. 101 | #html_theme_path = [] 102 | 103 | # The name for this set of Sphinx documents. If None, it defaults to 104 | # " v documentation". 105 | #html_title = None 106 | 107 | # A shorter title for the navigation bar. Default is the same as html_title. 108 | #html_short_title = None 109 | 110 | # The name of an image file (relative to this directory) to place at the top 111 | # of the sidebar. 112 | #html_logo = None 113 | 114 | # The name of an image file (within the static path) to use as favicon of the 115 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 116 | # pixels large. 117 | #html_favicon = None 118 | 119 | # Add any paths that contain custom static files (such as style sheets) here, 120 | # relative to this directory. They are copied after the builtin static files, 121 | # so a file named "default.css" will overwrite the builtin "default.css". 122 | html_static_path = [] 123 | 124 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 125 | # using the given strftime format. 126 | #html_last_updated_fmt = '%b %d, %Y' 127 | 128 | # If true, SmartyPants will be used to convert quotes and dashes to 129 | # typographically correct entities. 130 | #html_use_smartypants = True 131 | 132 | # Custom sidebar templates, maps document names to template names. 133 | #html_sidebars = {} 134 | 135 | # Additional templates that should be rendered to pages, maps page names to 136 | # template names. 137 | #html_additional_pages = {} 138 | 139 | # If false, no module index is generated. 140 | #html_use_modindex = True 141 | 142 | # If false, no index is generated. 143 | #html_use_index = True 144 | 145 | # If true, the index is split into individual pages for each letter. 146 | #html_split_index = False 147 | 148 | # If true, links to the reST sources are added to the pages. 149 | #html_show_sourcelink = True 150 | 151 | # If true, an OpenSearch description file will be output, and all pages will 152 | # contain a tag referring to it. The value of this option must be the 153 | # base URL from which the finished HTML is served. 154 | #html_use_opensearch = '' 155 | 156 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 157 | #html_file_suffix = '' 158 | 159 | # Output file base name for HTML help builder. 160 | htmlhelp_basename = 'DjangoNativeTagsdoc' 161 | 162 | 163 | # -- Options for LaTeX output -------------------------------------------------- 164 | 165 | # The paper size ('letter' or 'a4'). 166 | #latex_paper_size = 'letter' 167 | 168 | # The font size ('10pt', '11pt' or '12pt'). 169 | #latex_font_size = '10pt' 170 | 171 | # Grouping the document tree into LaTeX files. List of tuples 172 | # (source start file, target name, title, author, documentclass [howto/manual]). 173 | latex_documents = [ 174 | ('index', 'DjangoNativeTags.tex', u'Django Native Tags Documentation', 175 | u'Justin Quick', 'manual'), 176 | ] 177 | 178 | # The name of an image file (relative to this directory) to place at the top of 179 | # the title page. 180 | #latex_logo = None 181 | 182 | # For "manual" documents, if this is true, then toplevel headings are parts, 183 | # not chapters. 184 | #latex_use_parts = False 185 | 186 | # Additional stuff for the LaTeX preamble. 187 | #latex_preamble = '' 188 | 189 | # Documents to append as an appendix to all manuals. 190 | #latex_appendices = [] 191 | 192 | # If false, no module index is generated. 193 | #latex_use_modindex = True 194 | -------------------------------------------------------------------------------- /docs/configuration.txt: -------------------------------------------------------------------------------- 1 | .. _configuration: 2 | .. highlight:: python 3 | 4 | Settings 5 | ======== 6 | 7 | DJANGO_BUILTIN_TAGS 8 | ------------------- 9 | 10 | Tuple of Django templatetags modules to load by default. 11 | This places them in the Django template Library builtins. 12 | Place your most commonly used templatetags here and you will never have to ``{% load %}`` them. 13 | 14 | Example:: 15 | 16 | DJANGO_BUILTIN_TAGS = ( 17 | 'native_tags.templatetags.native', 18 | 'django.contrib.markup.templatetags.markup', 19 | #... 20 | ) 21 | 22 | NATIVE_LIBRARY 23 | -------------- 24 | 25 | Updates the library of tags to load by default. 26 | Define any valid types of functions here. 27 | Lets you define them right in ``settings.py`` 28 | 29 | Example:: 30 | 31 | NATIVE_LIBRARY = { 32 | 'function':{ 'add': lambda x, y: x + y }, 33 | 'comparison':{ 'is_in': lambda x, y: x in y }, 34 | 'filter':{ 'rstrip': lambda s: s.rstrip() }, 35 | 'block':{ 'comment': lambda: '' } 36 | } 37 | 38 | NATIVE_TAGS 39 | -------------- 40 | 41 | Tuple of contrib tag modules to load. 42 | Use the ones from :ref:`contrib-index` or add your own 43 | 44 | Out of the box, this is just the set of tags needed to be a functional replacement to django-template-utils 45 | 46 | All available options included in this app:: 47 | 48 | 'native_tags.contrib.comparison', 49 | 'native_tags.contrib.context', 50 | 'native_tags.contrib.generic_content', 51 | 'native_tags.contrib.generic_markup', 52 | 'native_tags.contrib.hash', 53 | 'native_tags.contrib.serializers', 54 | 'native_tags.contrib.baseencode', 55 | 'native_tags.contrib.regex', 56 | 'native_tags.contrib.math', 57 | 'native_tags.contrib.mapreduce', 58 | 'native_tags.contrib.cal', 59 | 'native_tags.contrib.rand', 60 | 61 | # Native tags with dependencies 62 | 'native_tags.contrib.gchart', # GChartWrapper 63 | 'native_tags.contrib.pygmentize', # Pygments 64 | 'native_tags.contrib.feeds', # Feedparser 65 | 66 | .. _django-template-utils: http://bitbucket.org/ubernostrum/django-template-utils/ 67 | 68 | 69 | NATIVE_DEFAULT_CACHE_TIMEOUT 70 | ----------------------------- 71 | 72 | Cache timeout (if any) in seconds. If this is set, all tags will cache for this amount of time, otherwise 73 | you may define caching at the per-tag level. Defaults to `None` 74 | 75 | 76 | Example 1hr cache:: 77 | 78 | NATIVE_DEFAULT_CACHE_TIMEOUT = 60 * 60 79 | 80 | -------------------------------------------------------------------------------- /docs/contrib/baseencode.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-baseencode: 2 | .. highlight:: django 3 | 4 | :mod:`baseencode` - :mod:`base64` encoding/decoding 5 | ==================================================== 6 | 7 | .. automodule:: native_tags.contrib.baseencode 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/cal.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-cal: 2 | .. highlight:: django 3 | 4 | :mod:`cal` - create formatted :mod:`HTMLCalendar` 5 | ================================================= 6 | 7 | .. automodule:: native_tags.contrib.cal 8 | 9 | .. autofunction:: calendar 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/contrib/comparison.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-comparison: 2 | .. highlight:: django 3 | 4 | :mod:`comparison` - compare template variables 5 | ============================================== 6 | 7 | .. automodule:: native_tags.contrib.comparison 8 | 9 | Tags supported by ``django-template-utils`` 10 | ------------------------------------------- 11 | 12 | .. autofunction:: greater 13 | .. autofunction:: greater_or_equal 14 | .. autofunction:: less 15 | .. autofunction:: less_or_equal 16 | 17 | New comparison tags 18 | ------------------- 19 | 20 | .. autofunction:: startswith 21 | .. autofunction:: endswith 22 | .. autofunction:: contains 23 | .. autofunction:: setting 24 | .. autofunction:: divisible_by 25 | .. autofunction:: superset 26 | .. autofunction:: subset 27 | 28 | -------------------------------------------------------------------------------- /docs/contrib/context.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-context: 2 | .. highlight:: django 3 | 4 | :mod:`context` - context manipulation tags 5 | ========================================== 6 | 7 | .. automodule:: native_tags.contrib.context 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/feeds.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-feeds: 2 | .. highlight:: django 3 | 4 | :mod:`feeds` - RSS feed parsing using :mod:`feedparser` 5 | ======================================================== 6 | 7 | .. automodule:: native_tags.contrib.feeds 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/gchart.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-gchart: 2 | .. highlight:: django 3 | 4 | :mod:`gchart` - Google Charts via :mod:`GChartWrapper` 5 | ====================================================== 6 | 7 | .. automodule:: native_tags.contrib.gchart 8 | 9 | .. autofunction:: gchart 10 | -------------------------------------------------------------------------------- /docs/contrib/generic_content.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-generic_content: 2 | .. highlight:: django 3 | 4 | :mod:`generic_content` - access model content 5 | ============================================= 6 | 7 | .. automodule:: native_tags.contrib.generic_content 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/generic_markup.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-generic_markup: 2 | .. highlight:: django 3 | 4 | :mod:`generic_markup` - filters for converting plain text to HTML 5 | ================================================================= 6 | 7 | .. automodule:: native_tags.contrib.generic_markup 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/hash.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-hash: 2 | .. highlight:: django 3 | 4 | :mod:`hash` - MD5 and SHA tags 5 | ==================================== 6 | 7 | .. automodule:: native_tags.contrib.hash 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/index.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-index: 2 | 3 | Contrib Add Ons 4 | =============== 5 | 6 | Tags ported from `django-template-utils`_ and other sources 7 | 8 | .. _django-template-utils: http://bitbucket.org/ubernostrum/django-template-utils/ 9 | 10 | .. automodule:: native_tags.contrib 11 | 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | baseencode 17 | cal 18 | context 19 | comparison 20 | hash 21 | feeds 22 | gchart 23 | generic_content 24 | generic_markup 25 | mapreduce 26 | math 27 | op 28 | pygmentize 29 | rand 30 | regex 31 | serializers 32 | -------------------------------------------------------------------------------- /docs/contrib/mapreduce.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-mapreduce: 2 | .. highlight:: django 3 | 4 | :mod:`mapreduce` - native ``map`` and ``reduce`` tags 5 | ====================================================== 6 | 7 | .. automodule:: native_tags.contrib.mapreduce 8 | 9 | .. autofunction:: do_map 10 | .. autofunction:: do_reduce 11 | -------------------------------------------------------------------------------- /docs/contrib/math.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-math: 2 | .. highlight:: django 3 | 4 | :mod:`math` - mathematical operations 5 | ====================================================== 6 | 7 | .. automodule:: native_tags.contrib.math_ 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/contrib/op.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-op: 2 | .. highlight:: django 3 | 4 | :mod:`op` - Operators 5 | ===================== 6 | 7 | .. automodule:: native_tags.contrib.op 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/contrib/pygmentize.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-pygmentize: 2 | .. highlight:: django 3 | 4 | :mod:`pygmentize` - using :mod:`Pygments` to highlight source code 5 | ================================================================== 6 | 7 | .. automodule:: native_tags.contrib.pygmentize 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/rand.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-rand: 2 | .. highlight:: django 3 | 4 | :mod:`rand` - random number operations 5 | ======================================= 6 | 7 | .. automodule:: native_tags.contrib.rand 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/regex.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-regex: 2 | .. highlight:: django 3 | 4 | :mod:`regex` - regular expression pattern operations 5 | ==================================================== 6 | 7 | .. automodule:: native_tags.contrib.regex 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /docs/contrib/serializers.txt: -------------------------------------------------------------------------------- 1 | .. _contrib-serializers: 2 | .. highlight:: django 3 | 4 | :mod:`serializers` - wrapper for :mod:`django.core.serializers` 5 | =============================================================== 6 | 7 | 8 | .. automodule:: native_tags.contrib.serializers 9 | 10 | .. autofunction:: serialize 11 | .. autofunction:: serialize_json 12 | .. autofunction:: serialize_xml 13 | .. autofunction:: serialize_python 14 | .. autofunction:: serialize_yaml 15 | 16 | -------------------------------------------------------------------------------- /docs/decorators.txt: -------------------------------------------------------------------------------- 1 | .. _decorators: 2 | 3 | 4 | Decorators 5 | =========================== 6 | 7 | .. highlight:: python 8 | .. automodule:: native_tags.decorators 9 | 10 | The decorators that come with this app are there for convienence but are not necessary to use. 11 | Their purpose is to just set some important attributes on the wrapped function and maintain docstrings. 12 | 13 | The normal syntax (without decorators):: 14 | 15 | def myfunc(context): 16 | return context['myvar'].split() 17 | myfunc.function = 1 18 | myfunc.takes_context = 1 19 | myfunc.name = 'my_function_tag' 20 | myfunc.cache = 3600 21 | 22 | Is equivalent to:: 23 | 24 | from native_tags.decorators import function 25 | 26 | @function(takes_context=1, cache=3600, name='my_function_tag') 27 | def myfunc(context): 28 | return context['myvar'].split() 29 | 30 | And can be used in the template using the `name`:: 31 | 32 | {% my_function_tag as varsplit %} 33 | 34 | Important Attributes 35 | -------------------- 36 | 37 | ``function``, ``filter``, ``comparison``, ``block`` - boolean. Determines which kind of tag the function represents. 38 | A function can have multiple uses (eg. function and filter) 39 | 40 | ``name`` - string. The actual name of the tag to use in the template. Defaults to the function name. 41 | 42 | ``resolve`` - boolean. If ``True``, all argments are resolved in the context of the tag. 43 | Set to ``False`` to parse your own raw text arguments. Default is ``True`` 44 | 45 | ``takes_context`` - boolean. If ``True`` the context itself is prepended to the function arguments. Default is ``False`` 46 | 47 | ``takes_request`` - boolean. If ``True`` the request object is prepended to the function arguments. 48 | Make sure ``django.core.context_processors.request`` is in your ``TEMPLATE_CONTEXT_PROCESSORS`` setting. Default is ``False`` 49 | 50 | ``inclusion`` - boolean. If ``True`` then the function is treated as an inclusion tag. Inclusion tags work a bit differently in 51 | native tags, the function must return a tuple of (``template_name``, ``context``). This lets you dynamically define the name of 52 | the template to use in rendering. 53 | 54 | ``apply_filters`` - boolean. If ``True``, the filter expressions are resolved for each argument and keyword argument if present. 55 | Default is ``True`` 56 | 57 | ``cache`` - integer. The cache timeout time in seconds to use if any. 58 | Default is no caching. 59 | 60 | ``test`` - dictionary. Configuration data for a unittest. 61 | Keys are: ``args``,``kwargs``, and ``result``. When testing native_tags it will 62 | assert that the tag called with args/kwargs will return the expected result. 63 | Default expected result is ``True``. 64 | 65 | ``fallback`` - arbitrary object. If for any reason the native tag you are running encounters an error, 66 | return this fallback value instead. Providing a default (like an empty list) is a good way to fail elegantly and not have your site crash. 67 | 68 | 69 | Decorator Types 70 | --------------- 71 | 72 | .. autofunction:: function 73 | .. autofunction:: comparison 74 | .. autofunction:: block 75 | .. autofunction:: filter 76 | 77 | 78 | -------------------------------------------------------------------------------- /docs/features.txt: -------------------------------------------------------------------------------- 1 | .. _features: 2 | .. highlight:: django 3 | 4 | Features of Native Tags 5 | ======================= 6 | 7 | Quoted Strings 8 | -------------- 9 | 10 | Using the ``shlex`` module, double/single quoted strings are interpreted as a single string element now. 11 | Take this example templatetag: ``{% myfunc 'hello world' %}``. If you were to write your own templatetag from scratch to handle this 12 | when calling ``token.contents.split()`` it would return ``["'hello", "world'"]`` and ruin the continuity of the string. With Native Tags quoted 13 | strings act just like they should and the arguments parsed from the above example would be ``['hello world']`` as expected. 14 | 15 | Keyword arguments 16 | ----------------- 17 | 18 | Arguments and keyword arguments are parsed out of the template to more closely match the Python API. That way 19 | ``{% myfunc arg1 arg2 kwarg1=value1 kwarg2=value2 %}`` represents the function with signature ``def myfunc(arg1, arg2, kwarg1=value1, kwarg2=value2)``. 20 | This also means that ``*args`` and ``**kwargs`` can also be used in native tag functions. 21 | 22 | Auto resolve 23 | ------------ 24 | 25 | By default variables passed into Native Tags are by default resolved in the tag's context. If a variable cannot be found, 26 | then the string is just returned. This functionality can be turned off by setting the tag function's ``resolve`` attribute to ``False``. 27 | 28 | Auto load 29 | --------- 30 | 31 | Tired of calling ``{% load %}`` on every single template you want to use your favorite tags on? 32 | Native Tags lets you define templatetags as part of the builtins which are loaded automagically. 33 | Just place your favorite templatetags into the ``DJANGO_BUILTIN_TAGS`` setting and kiss ``{% load %}`` tags good bye 34 | -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | .. _index: 2 | 3 | Native Tags Documentation 4 | ========================= 5 | 6 | :Authors: 7 | Justin Quick 8 | :Version: 0.5 9 | 10 | :: 11 | 12 | pip install django-native-tags==0.5.3 13 | 14 | Django Native Tags is a way of making the creation of template tags stupidly simple. 15 | Tags are "native" because there is a much closer relationship between the tag in the template and a Python function behind the scenes. 16 | The app abstracts the work needed to parse out the templatetag syntax into a useable form for a Python function. 17 | For example: 18 | 19 | Define an arbitrary function in your templatetags:: 20 | 21 | def add(x, y): 22 | return x + y 23 | add.function = True 24 | 25 | Use the function in your template:: 26 | 27 | {% add 1000 100 as num %} 28 | {{ num|intcomma }} 29 | 30 | Which outputs:: 31 | 32 | 1,100 33 | 34 | Other :ref:`Features ` of Native Tags: 35 | 36 | * Keyword argument parsing 37 | * Quoted strings parsed correctly 38 | * Add templatetags to Django's builtins (no ``{% load %}`` required) 39 | * Auto resolve of template variables 40 | * Universal and per-tag caching 41 | * Straightforward template tag unittesting 42 | * Error tolerant by letting you specify a fallback return value 43 | 44 | 45 | The real power of the module comes in the :ref:`contrib-index` which has tons of tags for various uses including 46 | comparisons, regex operations, math operaions, and much more. By default it is a functional replacement to `James Bennett`_'s `django-template-utils`_ right out of the box 47 | 48 | .. _django-template-utils: http://bitbucket.org/ubernostrum/django-template-utils/ 49 | .. _James Bennett: http://www.b-list.org/ 50 | 51 | Contents 52 | ========= 53 | 54 | .. toctree:: 55 | :maxdepth: 1 56 | 57 | changelog 58 | concepts 59 | features 60 | configuration 61 | decorators 62 | testing 63 | contrib/index 64 | 65 | Indices and tables 66 | ================== 67 | 68 | * :ref:`genindex` 69 | * :ref:`modindex` 70 | * :ref:`search` -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | set SPHINXBUILD=sphinx-build 6 | set BUILDDIR=build 7 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 8 | if NOT "%PAPER%" == "" ( 9 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 10 | ) 11 | 12 | if "%1" == "" goto help 13 | 14 | if "%1" == "help" ( 15 | :help 16 | echo.Please use `make ^` where ^ is one of 17 | echo. html to make standalone HTML files 18 | echo. dirhtml to make HTML files named index.html in directories 19 | echo. pickle to make pickle files 20 | echo. json to make JSON files 21 | echo. htmlhelp to make HTML files and a HTML help project 22 | echo. qthelp to make HTML files and a qthelp project 23 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 24 | echo. changes to make an overview over all changed/added/deprecated items 25 | echo. linkcheck to check all external links for integrity 26 | echo. doctest to run all doctests embedded in the documentation if enabled 27 | goto end 28 | ) 29 | 30 | if "%1" == "clean" ( 31 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 32 | del /q /s %BUILDDIR%\* 33 | goto end 34 | ) 35 | 36 | if "%1" == "html" ( 37 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 38 | echo. 39 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 40 | goto end 41 | ) 42 | 43 | if "%1" == "dirhtml" ( 44 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 45 | echo. 46 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 47 | goto end 48 | ) 49 | 50 | if "%1" == "pickle" ( 51 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 52 | echo. 53 | echo.Build finished; now you can process the pickle files. 54 | goto end 55 | ) 56 | 57 | if "%1" == "json" ( 58 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 59 | echo. 60 | echo.Build finished; now you can process the JSON files. 61 | goto end 62 | ) 63 | 64 | if "%1" == "htmlhelp" ( 65 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 66 | echo. 67 | echo.Build finished; now you can run HTML Help Workshop with the ^ 68 | .hhp project file in %BUILDDIR%/htmlhelp. 69 | goto end 70 | ) 71 | 72 | if "%1" == "qthelp" ( 73 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 74 | echo. 75 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 76 | .qhcp project file in %BUILDDIR%/qthelp, like this: 77 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\DjangoNativeTags.qhcp 78 | echo.To view the help file: 79 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\DjangoNativeTags.ghc 80 | goto end 81 | ) 82 | 83 | if "%1" == "latex" ( 84 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 85 | echo. 86 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 87 | goto end 88 | ) 89 | 90 | if "%1" == "changes" ( 91 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 92 | echo. 93 | echo.The overview file is in %BUILDDIR%/changes. 94 | goto end 95 | ) 96 | 97 | if "%1" == "linkcheck" ( 98 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 99 | echo. 100 | echo.Link check complete; look for any errors in the above output ^ 101 | or in %BUILDDIR%/linkcheck/output.txt. 102 | goto end 103 | ) 104 | 105 | if "%1" == "doctest" ( 106 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 107 | echo. 108 | echo.Testing of doctests in the sources finished, look at the ^ 109 | results in %BUILDDIR%/doctest/output.txt. 110 | goto end 111 | ) 112 | 113 | :end 114 | -------------------------------------------------------------------------------- /docs/testing.txt: -------------------------------------------------------------------------------- 1 | .. _testing: 2 | .. highlight:: python 3 | 4 | Testing Your Tags 5 | ================== 6 | 7 | With native tags, the templatetag is simply just a function you define which 8 | means that testing the underlying functionality of your templatetag does not 9 | require going to your browser. Just pass in the appropriate arguments and test the output. 10 | Here is an example of unittesting a native tag which demonstrates how trivial it can be:: 11 | 12 | # tests.py 13 | from django.test import TestCase 14 | from native_tags import function 15 | 16 | # Define or import your tags here 17 | 18 | @function 19 | def adder(x, y): 20 | return x + y 21 | 22 | class AdderTest(TestCase): 23 | def test_adder(self): 24 | self.assertEqual(adder(1, 1), 2) 25 | 26 | That is the full example above, but to add simple tests to a native function, you can use the 27 | ``test`` attribute passing in the arguments and expected result of the function call. 28 | Here is a shorter example that does the same thing as above:: 29 | 30 | def adder(x, y): 31 | return x + y 32 | adder = function(adder, test={'args': (1, 1), 'result': 2}) 33 | 34 | Running ``./manage.py test native_tags`` will test *only* your own tags in your project. 35 | This is useful for testing the sanity of your project and its applications and great to put in a CI job. 36 | 37 | Test Application 38 | ================= 39 | 40 | The ``example_project`` folder in the source tree root is also useful for testing 41 | native tags itself. Run ``./manage.py test app`` in that directory to run a test application 42 | on native tags. -------------------------------------------------------------------------------- /example_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justquick/django-native-tags/d40b976ee1cb13faeb04f0dedf02933d4274abf2/example_project/__init__.py -------------------------------------------------------------------------------- /example_project/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justquick/django-native-tags/d40b976ee1cb13faeb04f0dedf02933d4274abf2/example_project/app/__init__.py -------------------------------------------------------------------------------- /example_project/app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /example_project/app/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justquick/django-native-tags/d40b976ee1cb13faeb04f0dedf02933d4274abf2/example_project/app/templatetags/__init__.py -------------------------------------------------------------------------------- /example_project/app/templatetags/app_tags.py: -------------------------------------------------------------------------------- 1 | from native_tags.decorators import function, comparison, filter 2 | from datetime import datetime 3 | 4 | def dynamic(*a, **kw): 5 | return list(a) + sorted(kw.items()) 6 | dynamic = function(dynamic) 7 | 8 | def no_render(*a, **kw): 9 | return list(a) + sorted(kw.items()) 10 | no_render = function(no_render, resolve=False) 11 | 12 | def myfilter(value, arg): 13 | return value + arg 14 | myfilter = filter(myfilter, test={'args':(1,1),'result':2}) 15 | 16 | def adder(x, y): 17 | return x + y 18 | adder = function(adder, name='add', test={'args':(1,1),'result':2}) 19 | 20 | def cmp_kwargs(**kw): 21 | return len(kw) 22 | cmp_kwargs = comparison(cmp_kwargs) 23 | 24 | def myinc(noun): 25 | return 'unittest.html', {'noun': noun} 26 | myinc = function(myinc, inclusion=True) 27 | 28 | def ifsomething(): 29 | return True 30 | ifsomething = comparison(ifsomething) 31 | 32 | def date(): 33 | return datetime.now() 34 | date = function(date, cache=3600) 35 | 36 | def fail(): 37 | 1/0 38 | fail = function(fail, fallback='woot') -------------------------------------------------------------------------------- /example_project/app/tests.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from django.contrib.auth.models import User 3 | from django.template import Template, Context, TemplateSyntaxError 4 | from django.test import TestCase 5 | from django.core.serializers import deserialize 6 | from django.core.cache import cache 7 | 8 | from native_tags.registry import register, AlreadyRegistered 9 | from native_tags.nodes import get_cache_key 10 | 11 | def render(src, ctx={}): 12 | return Template(src).render(Context(ctx)) 13 | 14 | class TemplateTests(TestCase): 15 | def setUp(self): 16 | self.test_user = User.objects.create(username='tester', email='test') 17 | # User.objects.create(username='tester2', email='test2') 18 | # User.objects.create(username='tester3', email='test3') 19 | # User.objects.create(username='tester4', email='test4') 20 | self.hash = {'foo':'bar'} 21 | 22 | 23 | def test_less(self): 24 | t = """{% if_less 1 2 %}y{% endif_less %}{% if_less_or_equal 1 2 %}y{% endif_less_or_equal %}{% if_greater 2 1 %}n{% endif_greater %}{% if_greater_or_equal 1 1 %}y{% endif_greater_or_equal %}""" 25 | self.assertEqual(render(t), 'yyny') 26 | 27 | def test_set(self): 28 | t = "{% set src='import this' %}{{ src }}" 29 | self.assertEqual(render(t), 'import this') 30 | 31 | def test_del(self): 32 | t = "{% del test %}{{ test }}" 33 | self.assertEqual(render(t,{'test': 'yup'}), '') 34 | 35 | def _serialize(self, format): 36 | t = "{% serialize format users as seria %}{{ seria|safe }}" 37 | seria = render(t, {'format':format,'users':User.objects.all()}) 38 | if format == 'python': seria = eval(seria) 39 | self.assertEqual(deserialize(format, seria).next().object.username, 'tester') 40 | 41 | def test_serialize_json(self): 42 | self._serialize('json') 43 | 44 | def test_serialize_python(self): 45 | self._serialize('python') 46 | 47 | def test_serialize_xml(self): 48 | self._serialize('xml') 49 | 50 | def test_contains(self): 51 | t = "{% if_contains 'team' 'i' %}yup{% endif_contains %}" 52 | self.assertEqual(render(t), '') 53 | 54 | def test_divisible(self): 55 | t = "{% if_divisible_by 150 5 %}buzz{% endif_divisible_by %}" 56 | self.assertEqual(render(t), 'buzz') 57 | 58 | def test_startswith(self): 59 | t = "{% if_startswith 'python' 'p' %}yup{% endif_startswith %}" 60 | self.assertEqual(render(t), 'yup') 61 | 62 | def test_subset(self): 63 | t = "{% if_subset l1 l2 %}yup{% endif_subset %}" 64 | self.assertEqual(render(t, {'l1':[2,3], 'l2':range(5)}), 'yup') 65 | 66 | def test_superset(self): 67 | self.assertEqual(render("{% if_superset l1 l2 %}yup{% endif_superset %}",{'l1':range(5),'l2':[2,3]}),'yup') 68 | 69 | def test_endswith(self): 70 | self.assertEqual(render("{% if_endswith 'python' 'n' %}yup{% endif_endswith %}"), 'yup') 71 | 72 | def test_startswith_negate(self): 73 | t = "{% if_startswith 'python' 'p' negate %}yup{% endif_startswith %}" 74 | self.assertEqual(render(t), '') 75 | 76 | def test_startswith_negate_else(self): 77 | t = "{% if_startswith 'python' 'p' negate %}yup{% else %}nope{% endif_startswith %}" 78 | self.assertEqual(render(t), 'nope') 79 | 80 | def test_setting(self): 81 | t = "{% if_setting 'DEBUG' %}debug{% endif_setting %}" 82 | self.assertEqual(render(t), 'debug') 83 | 84 | def test_sha1_filter(self): 85 | sha1 = '62cdb7020ff920e5aa642c3d4066950dd1f01f4d' 86 | self.assertEqual(render('{{ foo|sha1 }}', self.hash), sha1) 87 | 88 | def test_sha1_function(self): 89 | sha1 = '62cdb7020ff920e5aa642c3d4066950dd1f01f4d' 90 | self.assertEqual(render('{% sha1 foo %}', self.hash), sha1) 91 | 92 | def test_md5_2X(self): 93 | md5 = '37b51d194a7513e45b56f6524f2d51f2' 94 | self.assertEqual(render('{{ foo|md5 }}{% md5 foo %}', self.hash), md5*2) 95 | 96 | def test_greater(self): 97 | t = '{% if_greater 2 1 %}yup{% endif_greater %}' 98 | self.assertEqual(render(t), 'yup') 99 | 100 | def test_render_block(self): 101 | t = '{% render_block as myvar %}hello {{ place }}{% endrender_block %}{{ myvar }}' 102 | self.assertEqual(render(t, {'place':'world'}), 'hello world') 103 | 104 | def test_get_latest_object(self): 105 | t = '{% get_latest_object auth.user date_joined %}' 106 | self.assertEqual(render(t), 'tester') 107 | 108 | def test_get_latest_objects(self): 109 | t = '{% get_latest_objects auth.user 10 %}' 110 | self.assertEqual(render(t), '[]') 111 | 112 | def test_get_random_object(self): 113 | t = '{% get_random_object auth.user %}' 114 | self.assertEqual(render(t), 'tester') 115 | 116 | def test_get_random_objects(self): 117 | t = '{% get_random_objects auth.user 10 %}' 118 | self.assertEqual(render(t), '[]') 119 | 120 | def test_retrieve_object(self): 121 | t = '{% retrieve_object auth.user username=tester %}' 122 | self.assertEqual(render(t), 'tester') 123 | 124 | def test_matches(self): 125 | t = "{% if_matches '\w{4}' 'hiya' %}yup{% endif_matches %}" 126 | self.assertEqual(render(t), 'yup') 127 | 128 | def test_search(self): 129 | t = "{% search '^(\d{3})$' 800 as match %}{{ match.groups|safe }}" 130 | self.assertEqual(render(t), u"('800',)") 131 | 132 | def test_substitute(self): 133 | t = "{% substitute 'ROAD$' 'RD.' '100 NORTH MAIN ROAD' %}" 134 | self.assertEqual(render(t), '100 NORTH MAIN RD.') 135 | 136 | def test_map(self): 137 | t = '{% map sha1 hello world as hashes %}{{ hashes|join:"-" }}' 138 | self.assertEqual(render(t), 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d-7c211433f02071597741e6ff5a8ea34789abbf43') 139 | 140 | def test_reduce(self): 141 | t = '{% reduce add 300 30 3 %}' 142 | self.assertEqual(render(t), '333') 143 | 144 | def test_calendar_month(self): 145 | self.assert_(render('{% calendar month 2009 10 %}').startswith('')) 152 | 153 | def test_randrange(self): 154 | self.assert_(render('{% randrange 10 %}') in map(str,range(10))) 155 | 156 | def test_randint(self): 157 | self.assert_(0 <= int(render('{% randint 0 10 %}')) <= 10) 158 | 159 | def test_randchoice(self): 160 | self.assert_(render('{% randchoice 1 2 3 %}') in '123') 161 | 162 | def test_random(self): 163 | self.assert_(0. <= float(render('{% random %}')) < 1.) 164 | 165 | def test_loops_work(self): 166 | """ 167 | Does looping while setting a context variable work 168 | """ 169 | t = "{% for i in items %}{% add 1 i as roomba %}{{roomba}}{% endfor %}" 170 | o = render(t, {'items':[1,2,3]}) 171 | self.assertEqual(o, "234") 172 | 173 | def test_b64encode(self): 174 | self.assertEqual(render('{% b64encode "hello world" %}'), 'aGVsbG8gd29ybGQ=') 175 | 176 | def test_b64decode(self): 177 | self.assertEqual(render('{% b64decode encoded %}', {'encoded':'aGVsbG8gd29ybGQ='}), 'hello world') 178 | 179 | def test_dynamic(self): 180 | self.assertEqual(eval(render('{% dynamic a b c d=1 e=2 %}')), 181 | ['a', 'b', 'c', ('d', 1), ('e', 2)]) 182 | 183 | def test_no_render(self): 184 | self.assertEqual(eval(render('{% no_render a b c d d=1 e=2 f=var %}', {'var':'hello'})), 185 | ['a', 'b', 'c', 'd', ('d', '1'), ('e', '2'), ('f', 'var')]) 186 | 187 | def test_filter_args(self): 188 | self.assertEqual(render('{{ var|myfilter:"baz" }}', {'var':'foobar'}), 'foobarbaz') 189 | 190 | def test_adder(self): 191 | self.assertEqual(render('{% load native humanize %}{% add 1000 100 as num %}{{ num|intcomma }}'), '1,100') 192 | 193 | def test_cmp_kwargs(self): 194 | self.assertEqual(render('{% if_cmp_kwargs foo=bar %}yup{% endif_cmp_kwargs %}'), 'yup') 195 | 196 | def test_zremove_tag(self): 197 | self.assert_('add' in register['function']) 198 | register.unregister('function', 'add') 199 | self.assert_(not 'add' in register['function']) 200 | 201 | def test_inclusion(self): 202 | self.assertEqual(render('{% myinc cheese %}'), 'im just here for the cheese') 203 | 204 | def test_map_builtins(self): 205 | self.assertEqual(render('{% map len l1 l2 l3 %}', {'l1':[1], 'l2':[1,2], 'l3':[1,2,3]}), '[1, 2, 3]') 206 | 207 | def test_smartypants(self): 208 | # this should b bombing, but i get DEBUG as False when testing despite the settings (just in testing?) 209 | self.assertEqual(render('{{ value|smartypants }}', {'value': 'wtf'}), 'wtf') 210 | 211 | def test_custom_if(self): 212 | self.assertEqual(render('{% ifsomething %}yup{% endifsomething %}'), 'yup') 213 | 214 | def test_filter_faker(self): 215 | self.assertRaises(TemplateSyntaxError, render, '{% sha1 "my | filter | faker" %}') 216 | 217 | def test_math(self): 218 | import math 219 | self.assertAlmostEqual(float(render('{% acos .3 %}')), 1.26610367278) 220 | self.assertEqual(float(render('{{ 1.5|floor }}')), 1.) 221 | self.assertEqual(float(render('{{ 4|sqrt }}')), 2.) 222 | self.assertAlmostEqual(float(render('{{ 180|radians }}')), math.pi) 223 | 224 | def test_native_debug(self): 225 | self.assertEqual(render('{% native_debug as debug %}{{ debug.keys|safe }}'), "['function', 'comparison', 'filter', 'block']") 226 | 227 | def test_cache(self): 228 | k = get_cache_key('function', 'date', (), {}) 229 | self.assert_(cache.get(k) is None) 230 | self.assertEqual(render('{% date %}'), render('{% date %}')) 231 | self.assert_(isinstance(cache.get(k), datetime.datetime)) 232 | 233 | def test_split(self): 234 | from native_tags.nodes import split 235 | a = 'fetch_user username as "author"' 236 | b = 'fetch_user "what the fuck" as "author"' 237 | self.assertEqual(split(a), ['fetch_user', 'username', 'as', 'author']) 238 | self.assertEqual(split(b), ['fetch_user', 'what the fuck', 'as', 'author']) 239 | 240 | def test_fail(self): 241 | self.assertEqual(render('{% fail %}'), 'woot') 242 | 243 | try: 244 | import hashlib 245 | def test_sha224_hashlib(self): 246 | ctx = {'foo':'bar'} 247 | sha224 = '07daf010de7f7f0d8d76a76eb8d1eb40182c8d1e7a3877a6686c9bf0' 248 | self.assertEqual(render('{{ foo|sha224 }}{% sha224 foo %}', ctx), sha224*2) 249 | except ImportError: 250 | pass 251 | 252 | try: 253 | import pygments 254 | def test_highlight_style(self): 255 | t = '' 256 | self.assert_(render(t).startswith('