On Mon, May 18, 2015 at 12:12 PM Amirouche Boubekki < [email protected]> wrote:
> I would like to implement similar functionality to Django template's
>>> custom tags in Jinja.
>>>
>>> In Django templates, to have a server-side function return a string
>>>
>> The example you give returns a dictionary. Not a string.
>
>
>> and have it inserted to a template, you can do something as this:
>>>
>> from django import template
>>>
>>> register = template.Library()
>>> @register.inclusion_tag('app/gallery.html')def get_gallery(active=None):
>>> ### do complex server side things, etc.
>>>
>>> context_dict = {
>>> ...
>>> }
>>> return context_dict
>>>
>>> You are updating the context of the template or only returning a
> variable that ca be used with a /for/ and /with/ tags?
>
>
>> Now, calling this is as simple as
>>>
>>> {% load app_tags %}{% get_gallery 'home' %}
>>>
>>> This seems clear enough for me.
>>>
>> See my above question please.
>
>> Now my question: even after reading through all parts of the Jinja2
>>> documentation, I do not see how a similar functionality should be
>>> implemented in Jinja. I've seen that Jinja supports macros, calls, imports,
>>> custom tests, custom filters, but I cannot see anything related to defining
>>> custom tags with arbitrary server-side Python code.
>>>
>> The main feature is that you can not replace context variable function
> with filters or the otherway around :))) #CognitveLoad
>
> Two concepts I know of:
>
> - filters, like /{live|love}/ this one will turn the content of the
> variable /live/ to something computed in python function named in the
> context /love/. filters can not be used as context variables.
>
> - context function that returns a context or string or else; maybe the
> context is updated. Anyway you can use assignements
> <http://jinja.pocoo.org/docs/dev/templates/#assignments> instead of
> django's with.
>
>
>> The closest I've seen is custom filters, but that has a weird syntax when
>>> used for tag-like purposes: to call tag(param1, param2), you'd need to
>>> write param1|tag(param2). Also, I don't see how it can be used for
>>> parameter-less functions.
>>>
>> not if you put the function in the context just like /user/, /message/,
> pass procedure as a variable of the template context.
>
>
>> There is also the whole extension support, but that seems overly
>>> complicated with the simplest example
>>> <http://jinja.pocoo.org/docs/dev/extensions/#example-extension> taking
>>> up 56 lines of code in the doc.
>>>
>>> Can you explain how do you solve this problem, both in standalone Jinja2
>>> environment (think static site generation)
>>>
>> I attached the make.py of my blog. There is a function called "jinja"
> it's commented what i does. and I also attach the index.jinja and
> base.jinja they must live in ./_templates. I never use this engine to do
> jinja alone. If you can't make it work hit me. Here is the jinja code:
>
> ```
> *-{{ "007"|love(42) }}*
> {% for ognoin in live(1985, 12, 31, bof="0x12ee7df") %}
> [[{{ ognoin }}]]
> {{ live(132)|love(4096) }}
> {% endfor %}
>
> {# {% for fish in love(1337) %} #}
> {# {{ "off" }} #}
> {# {% endfor %} #}
> ```
> rendering is at: http://www.hyperdev.fr/#main
>
> The context is populated by jinja function:
>
> ```
> def jinja(template, **context):
> """Render template /template/ found in /.//_templates// directory.
>
> wrapper around /Jinja.render/ where do add filter or improve
> context."""
> path = str(Path('./_templates').resolve())
>
> # to be used as a filter and context function
> def love(abc, *args, **kwargs):
> return ['a', 'b', 'c', 'abc', abc, args, kwargs]
>
> return Jinja.render(
> # the relative path of the directory we want
> # to render to, relative to /path/.
> template,
> # the root path where to look for templates
> # both the above /template/ but also other
> # template references in templates.
> path,
> # filter are text transformers. Some kind of html tags.
> # use functions in context to create Djano-like tags
> # aka. tags that be both manipulated the conte
> filters=dict(love=love),
> # every keyword except filters are made
> # available in the template context. The keyword
> # contexte variable is dubbed with the key of the
> # keyword argument.
> # Here /love/ is stored as /live/.
> live=love,
> # what was passed as context is forwarded as-is
> **context
> )
> ```
>
> Jinja.render is a *generic* frontend to jinja that allows to only use one
> class. You init with base settings and then use this jinja instance
> (usually several times). Here jinja **/function/** is only a shortcut *in
> the context of /make.py/*.
>
> Anyway, /love/ is a filter, /live/ is a function variable. /live/ does
> everything you need.
>
> and under the new Django versions (1.8+)?
>>>
>> Not time for this this time..
>
> NB: look up the code I render epub and pdf ^_^
>
I updated the make.py:jinja to make filters available as context function.
and remove the comment block in the jinja file. See attached files.
--
You received this message because you are subscribed to the Google Groups
"pocoo-libs" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/pocoo-libs.
For more options, visit https://groups.google.com/d/optout.
Jinja2==2.7.3 MarkupSafe==0.23 docopt==0.6.2 feedgen==0.3.0 tinycss==0.3 awesome-slugify
index.jinja2
Description: Binary data
base.jinja2
Description: Binary data
#!/usr/bin/env python3 # Copyright 2014 Amirouche Boubekki <[email protected]> # This work is free. You can redistribute it and/or modify it under the # terms of the Frak It To Public License, Version 14.08 or later. # # # FRAK IT TO PUBLIC LICENSE # Version 14.08 # Copyright (C) 2014 Amirouche Boubekki <[email protected]> # Everyone is permitted to copy and distribute verbatim or modified # copies of this license document, and changing it is allowed as long # as the name is changed. # FRAK IT TO PUBLIC LICENSE # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION # 0. Do what the FRAK you can do. # 1. Do FRAK what the FRAK you can do # 3. Do FRAK what the FRAK you can FRAK do import os import re from shlex import split as split_command from time import altzone from datetime import datetime from datetime import timezone from datetime import timedelta from subprocess import call from subprocess import check_output from pathlib import Path from docopt import docopt from slugify import slugify from feedgen.feed import FeedGenerator from ebooklib import epub from azf import HTML from azf import Jinja class NoteBuilder(HTML): def image(self, url, text=''): # make text optional & absolutize urls with self._inline(): url = ''.join(self.to_html(url)) url = self._context['baseurl'] + '/' + url return '<div class="image"><img src="%s" /></div>' % url def href(self, text, url): # XXX: invert url and text to match html return super().href(url, text) def keyword(self, value): with self._inline(): name = ''.join(self.to_html(value)).strip() try: self._context['keywords'].append(name) except KeyError: self._context['keywords'] = [name] return '' def title(self, value): with self._inline(): title = ''.join(self.to_html(value)) self._context['title'] = title self._context['slug'] = slug = slugify(title) return '<h1 id="%s">%s</h1>' % (slug, title) def introduction(self, value): intro = ''.join(self.to_html(value)) self._context['introduction'] = intro return intro def bold(self, value): with self._inline(): value = ''.join(self.to_html(value)) return '<b>%s</b>' % value def emphase(self, value): with self._inline(): value = ''.join(self.to_html(value)) return '<emphase>%s</emphase>' % value def context(self, name, value=None): if value: name, value = map(lambda x: ''.join(self.to_html(x)), (name, value)) self._context[name] = value return '' else: return super().context(name) def new_draft(title): title = title.capitalize() slug = slugify(title) directory = os.path.join('_drafts', slug) if os.path.exists(directory): print('Ark! There is already a draft with this name.') exit(1) else: os.makedirs(directory) index = os.path.join(directory, 'index.azf') with open(index, 'w') as f: body = 'âµ£title{%s}\n\n' % title f.write(body) print('Done. You can edit %s' % index) __settings__ = None def settings(): global __settings__ if not __settings__: import settings __settings__ = dict() for key, value in vars(settings).items(): if key.isupper(): __settings__[key] = value return __settings__ def jinja(template, **context): context.update(settings()) path = str(Path('./_templates').resolve()) return Jinja.render(template, path, **context) def keywordize(rooturl, keyword): slug = slugify(keyword) return dict( name=keyword, slug=keyword, url='%s/keyword/%s' % (rooturl, slug), ) def build_a_note(rooturl, relativedir): print('* building', relativedir) # parse azf file absdir = relativedir.resolve() filepath = absdir / 'index.azf' baseurl = rooturl + '/' + str(relativedir) with filepath.open() as f: context = NoteBuilder.render(f.read(), str(absdir), rooturl=rooturl, baseurl=baseurl) # improve context context['url'] = '%s/%s' % (rooturl, str(relativedir)) context['keywords'] = list(map(lambda keyword: keywordize(rooturl, keyword), sorted(context['keywords']))) created_at = re.findall('(\d+)/(\d+)/(\d+)', str(relativedir))[0] created_at = map(int, created_at) created_at = datetime(*list(created_at)) context['created_at'] = created_at context['url'] = rooturl + '/' + str(relativedir) context['slug'] = slugify(context['title']) # render jinja output = jinja('note.jinja2', **context) index = relativedir / 'index.html' with index.open('w') as f: f.write(output) return context def atom(title, alternate, self, path, notes): feed = FeedGenerator() feed.author({ 'name': 'Amirouche Boubekki', 'email': '[email protected]' }) feed.id(alternate) feed.title(title) feed.link(href=alternate, rel='alternate') feed.link(href=self, rel='self') for note in notes: entry = feed.add_entry() entry.id(note['url']) entry.title(note['title']) entry.link(href=note['url']) created_at = note['created_at'] year, month, day = (created_at.year, created_at.month, created_at.day) delta = -timedelta(seconds=altzone) delta = timezone(delta) published = datetime(year, month, day, tzinfo=delta) entry.published(published) feed.atom_file(path) def minify(filepath): if filepath.endswith('jquery.js'): print('* ignored', filepath) return if filepath.endswith('css'): command = 'cleancss ' elif filepath.endswith('js'): command = 'uglifyjs' elif filepath.endswith('html'): command = 'html-minifier --conservative-collapse --collapse-whitespace' else: print('* ignored', filepath) return print('* minify', filepath) command = command + ' ' + filepath command = split_command(command) output = check_output(command) with open(filepath, 'wb') as w: w.write(output) def autoprefix_dir(path): basecommand = 'autoprefixer --browsers "last 3 version" %s' for root, dirs, files in os.walk(path): for filename in files: filepath = os.path.join(root, filename) if filename.endswith('css'): command = basecommand % filepath command = split_command(command) call(command) def rebuild(rooturl, dev=False): curdir = Path('.') # rerender all notes and return them print('building notes') paths = curdir.glob('notes/*/*/*/*/') notes = list(map(lambda path: build_a_note(rooturl, path), paths)) notes.sort(key=lambda x: x['created_at'], reverse=True) # build keywords pages print('building keywords') keywords = dict() for note in notes: for keyword in note['keywords']: try: keywords[keyword['name']]['notes'].append(note) except: keywords[keyword['name']] = dict(keyword) keywords[keyword['name']]['notes'] = [note] keywords = sorted(keywords.values(), key=lambda x: x['name']) # build keyword pages with feeds for keyword in keywords: slug = keyword['slug'] path = curdir / 'keyword' / slug print('* building', str(path)) try: path.mkdir(parents=True) except OSError: pass output = jinja( 'keyword.jinja2', keyword=keyword, ) index = path / 'index.html' with index.open('w') as f: f.write(output) title = 'hypermove.net @ %s' % keyword['name'] atompath = str(path / 'atom.xml') atom(title, keyword['url'], keyword['url'] + 'atom.xml', atompath, keyword['notes']) print('building index') output = jinja( 'index.jinja2', notes=notes, keywords=keywords, rooturl=rooturl, ) index = curdir / 'index.html' with index.open('w') as f: f.write(output) print('building 404 page') output = jinja('404.jinja2', rooturl=rooturl) with open('404.html', 'w') as f: f.write(output) print('rendering feed') title = '[email protected]' atompath = str(curdir / 'atom.xml') atom(title, rooturl, rooturl + 'atom.xml', atompath, notes) print('building single page html') output = jinja('print.jinja2', notes=notes) with open('hypermove.net.html', 'w') as f: f.write(output) # print('autoprefix & minify all the thing') # list(map(lambda x: minify(str(x)), curdir.resolve().glob('notes/**/index.html'))) # list(map(lambda x: minify(str(x)), curdir.resolve().glob('static/*'))) # minify(str(curdir.resolve() / 'index.html')) # autoprefix_dir(str(curdir.resolve() / 'static')) print('building epub') # https://github.com/aerkalov/ebooklib book = epub.EpubBook() book.set_title('hypermove.net') book.add_author('Amirouche Boubekki') spine = ['nav'] # create chapter for note in notes: title = note['title'] filename = note['slug'] + '.xhtml' c = epub.EpubHtml( title=title, file_name=filename, ) c.content = note['body'] spine.append(c) book.add_item(c) book.toc.append(epub.Link(filename, title, note['slug'])) book.add_item(epub.EpubNcx()) book.add_item(epub.EpubNav()) book.spine = spine epub.write_epub('hypermove.net.epub', book, {}) if not dev: print('building pdf') command = 'weasyprint hypermove.net.html hypermove.net.pdf -s static/print.css' command = command.split() call(command) def main(): doc = """main.py. Usage: main.py draft <title> main.py publish <path> main.py build main.py rebuild [--dev] main.py -h | --help main.py --version Options: -h --help Show this screen. --version Show version. """ arguments = docopt(doc, version='15.02.13') if os.environ.get('DEBUG', False): print(arguments) # dispatch if arguments['draft']: title = arguments['<title>'] new_draft(title) print('Done!') elif arguments['publish']: path = arguments['<path>'] slug = path.split('/')[-1] if not slug: slug = path.split('/')[-2] created_at = datetime.now() created_at = created_at.strftime('%Y/%m/%d') target = os.path.join('notes', created_at, slug) # move(path, target) print('Done!') elif arguments['rebuild']: if arguments['--dev']: rebuild('http://127.0.0.1:8001', dev=True) else: rebuild('http://www.hyperdev.io') print('Rebuild done!') else: message = 'Oops! What did happen here?! Please fill ' message += 'a bug report with a lot of details using DEBUG=1 ' message += 'and send output at [email protected]' message += ', thanks!' raise Exception(message) if __name__ == '__main__': main()
Makefile
Description: Binary data
hypermove.net.epub
Description: application/epub
