2010-10-01 27 views
1

Salut J'ai essayé de créer une extension pour jinja2 qui rejoindrait plusieurs éléments avec un séparateur, tout en ignorant les éléments (fragments de gabarit) qui évaluent les espaces.Comment écrire une extension "joiner" pour Jinja2?

Il existe plusieurs de ces fragments et vous ne savez jamais à l'avance lesquels seront non-vides et lesquels le seront.

Cela ressemble à une tâche triviale, mais j'ai vraiment eu du mal à faire fonctionner cela dans jinja2. Peut-être qu'une partie de la raison est que jinja ne permet pas de définir des nœuds de templates personnalisés.

Souhaitez-vous des suggestions? Voici un extrait qui fera le travail d'analyse, mais il manque la partie évaluation.

class JoinerExtension(Extension): 
    """Template tag that joins non-whitespace (string) items 
    with a specified separator 

    Usage syntax: 

    {% joinitems separator='|' %} 
    .... 
    {% separator %} 
    .... 
    {% separator %} 
    .... 
    {% endjoinitems %} 

    where value of "separator" within the joinitems tag 
    can be an expression, not necessarily a sting 
    """ 

    tags = set(['joinitems']) 

    def parse(self, parser): 
     """parse function for the 
     joinitems template tag 
     """ 
     lineno = next(parser.stream).lineno 

     #1) read separator 
     separator = None 
     while parser.stream.current.type != 'block_end': 
      name = parser.stream.expect('name') 
      if name.value != 'separator': 
       parser.fail('found %r, "separator" expected' % 
          name.value, name.lineno, 
          exc=TemplateAssertionError) 

      # expressions 
      if parser.stream.current.type == 'assign': 
       next(parser.stream) 
       separator = parser.parse_expression() 
      else: 
       var = parser.stream.current 
       parser.fail('assignment expected after the separator' % 
          var.value, var.lineno, 
          exc=TemplateAssertionError) 

     #2) read the items 
     items = list() 
     end_tags = ['name:separator', 'name:endjoinitems'] 
     while True: 
      item = parser.parse_statements(end_tags) 
      items.append(item) 
      if parser.stream.current.test('name:separator'): 
       next(parser.stream) 
      else: 
       next(parser.stream) 
       break 

Répondre

4

La classe intégrée joiner pourrait-elle fonctionner? Voici un exemple simple de la documentation.

{% set pipe = joiner("|") %} 
{% if categories %} {{ pipe() }} 
    Categories: {{ categories|join(", ") }} 
{% endif %} 
{% if author %} {{ pipe() }} 
    Author: {{ author() }} 
{% endif %} 
{% if can_edit %} {{ pipe() }} 
    <a href="?action=edit">Edit</a> 
{% endif %} 

Vous avez mentionné qu'il n'est pas connu à l'avance quels fragments seront vides; il est peut-être possible de stocker la valeur de chaque fragment dans une variable avant de l'afficher pour pouvoir déterminer quels fragments sont effectivement vides. Par exemple:

{% set pipe = joiner("|") %} 
{% set fragment = gen_fragment1() %} 
{% if fragment|trim is not "" %} 
    {{ pipe() }} {{ fragment }} 
{% endif %} 
... 

Vous pouvez même résumer le schéma ci-dessus dans une macro pour réduire la répétition:

{% set pipe = joiner("|") %} 
{{ print_if_notblank(pipe, gen_fragment1()) }} 
{{ print_if_notblank(pipe, gen_fragment2()) }} 
... 

print_if_notblank est une macro définie comme:

{% macro print_if_notblank(separator, content) %} 
    {% if content|trim is not "" %} 
     {{ separator() }} {{ content }} 
    {% endif %} 
{% endmacro %} 
+0

qui fonctionnera, en utilisant le builtin pour l'instant avec des appels stratégiquement placés {{pipe()}}. – Evgeny