Jouons un peu avec Jinja2 et YAML

Publié le 07/07/2015

Bon, et si je voulais développer mon propre générateur de sites statiques ? Pas que ce soit nécessaire, car il y en a des dizaines de centaines de milliers (+une petite dizaine chaque jour), juste que j'en ai envie. Et aussi pour découvrir VueJS 😁.

https://vuejs.org/[VueJS] se définit comme un framework progressif. Contrairement à un framework type React ou Angular, il est effectivement possible d'intégrer un morceau du framework à certains endroits uniquement, pour faire évoluer une application Web existante.

Les idées de base sont les suivantes:

Côté backend

from jinja2 import Environment, PackageLoader, select_autoescape

from models import Site

env = Environment(
    loader=PackageLoader('grnx', 'templates'),
    autoescape=select_autoescape(['html'])
)

if __name__ == "__main__":
    root = Site('content')
    root.serialize()

    template = env.get_template('single.html')
    for article in root.articles:
        print(template.render(article.__dict__))

import datetime
import json
import re
import os

import yaml


RE_HEADER = re.compile('---((.)?(\n)?)*---')

date_handler = lambda obj: (
    obj.isoformat()
    if isinstance(obj, datetime.datetime)
    or isinstance(obj, datetime.date)
    else None
)

def json_handler(obj):
    """Handles the JSON object serializer.

    Returns:
        The iso format if the object is a date.
        The __dict__ attribute for any other JSON serializable object.

    Excepts:
        TypeError when object is not JSON serialisable.
    """

    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    elif obj.__dict__:
        return obj.__dict__
    else:
        raise TypeError(
            'Object of type %s with value %s is not JSON serializable'
            % (type(obj), repr(obj))
        )


class Content(object):

    def __init__(self, filepath):

        with open(filepath, 'r') as f:
            try:
                file_content = f.read()
                print(file_content)
                regex_result = RE_HEADER.search(file_content)
                if regex_result:
                    header = file_content[regex_result.start():regex_result.end()-3]
                    self.properties = yaml.load(header)
                    headers = self.properties
                    print('headers: ' + str(headers))

                    self.published_date = headers.pop('Date', None)
                    self.last_modified_date = headers.pop('LastModified', self.published_date)
                    self.title = headers.pop('Title', None)
                    self.slug = headers.pop('Slug', None)
                    self.path = filepath
            except yaml.YAMLError as error:
                print(error)


class Site(object):
    """Represents a Site object.

    Args:
        articles: containt all articles.
        metadata: hum. I don't remember what I would store in this property.
        taxonomies: the taxonomies we find for this site. Tags, categories, ...

    """

    def __init__(self, current_path):
        self.articles = []
        self.metadata = {}
        self.taxonomies = {}

        for directory, dirnames, files in os.walk(current_path):
            for file in files:
                if file.endswith(".md"):
                    self.manage_article(directory, file)

    def manage_article(self, directory, file):
        article = Content(os.path.join(directory, file))
        self.articles.append(article)

        for taxonomy in article.properties:
            if taxonomy not in self.taxonomies:
                self.taxonomies[taxonomy] = []

            self.taxonomies[taxonomy].append(article.slug)

    def to_json(self):
        """Serialize the content of the current structure to JSON format."""

        return json.dumps(self, default=json_handler, sort_keys=True, indent=4)

    def serialize(self):
        """Serialize the current files structure to index.json"""

        with open('index.json', 'w') as json_serialized_file:
            json_serialized_file.write(self.to_json())

Après relecture, il y a teeellement d'améliorations possibles... 😢 que ce ne sera sans doute jamais terminé. Je garde le code sous le coude, au cas où.

Côte frontend (que je ne réaliserai pas)

Voir ici pour un retour d'expérience.

En gros, pour l'environnement technique, on a:

... Pas vraiment le temps de rentrer dans tout ça pour le moment ⚔