From d7c9e772a00f86d87175f4cbf6b582e82ffb8657 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Thu, 30 Dec 2010 10:07:27 +0530 Subject: [PATCH] Refactored generator --- hyde/engine.py | 31 +---- hyde/generator.py | 127 +++++++++++++++++- hyde/layouts/basic/layout/base.html | 10 +- hyde/layouts/test/layout/base.html | 14 +- hyde/model.py | 12 +- hyde/site.py | 6 +- hyde/template.py | 14 +- .../tests/sites/test_jinja/content/about.html | 3 +- hyde/tests/sites/test_jinja/layout/base.html | 14 +- hyde/tests/test_generate.py | 37 +++++ hyde/tests/test_generator.py | 20 --- hyde/tests/test_model.py | 6 +- hyde/tests/test_site.py | 2 +- hyde/tests/util.py | 2 +- 14 files changed, 209 insertions(+), 89 deletions(-) create mode 100644 hyde/tests/test_generate.py delete mode 100644 hyde/tests/test_generator.py diff --git a/hyde/engine.py b/hyde/engine.py index f29411d..0026ec8 100644 --- a/hyde/engine.py +++ b/hyde/engine.py @@ -70,36 +70,13 @@ class Engine(Application): The generate command. Generates the site at the given deployment directory. """ sitepath = Folder(args.sitepath) - logger.info("Generating site at [%s]" % sitepath) - # Read the configuration config_file = sitepath.child(args.config) logger.info("Reading site configuration from [%s]", config_file) conf = {} with open(config_file) as stream: conf = yaml.load(stream) site = Site(sitepath, Config(sitepath, conf)) - # TODO: Find the appropriate template environment - from hyde.ext.templates.jinja import Jinja2Template - template = Jinja2Template(sitepath) - logger.info("Using [%s] as the template", template) - # Configure the environment - logger.info("Configuring Template environment") - template.configure(site.config) - # Prepare site info - logger.info("Analyzing site contents") - site.build() - context = dict(site=site) - # Generate site one file at a time - logger.info("Generating site to [%s]" % site.config.deploy_root_path) - for page in site.content.walk_resources(): - logger.info("Processing [%s]", page) - target = File(page.source_file.get_mirror(site.config.deploy_root_path, site.content.source_folder)) - target.parent.make() - if page.source_file.is_text: - logger.info("Rendering [%s]", page) - context.update(page=page) - text = template.render(page.source_file.read_all(), context) - target.write(text) - else: - logger.info("Copying binary file [%s]", page) - page.source_file.copy_to(target) \ No newline at end of file + + from hyde.generator import Generator + gen = Generator(site) + gen.generate_all() \ No newline at end of file diff --git a/hyde/generator.py b/hyde/generator.py index bacee29..6cf0908 100644 --- a/hyde/generator.py +++ b/hyde/generator.py @@ -1,6 +1,18 @@ """ The generator class and related utility functions. """ +from hyde.exceptions import HydeException +from hyde.fs import File +from hyde.template import Template + +from contextlib import contextmanager + +import logging +from logging import NullHandler + +logger = logging.getLogger('hyde.engine') +logger.addHandler(NullHandler()) + class Generator(object): """ @@ -10,25 +22,126 @@ class Generator(object): def __init__(self, site): super(Generator, self).__init__() self.site = site + self.__context__ = dict(site=site) + self.template = None + + @contextmanager + def context_for_resource(self, resource): + """ + Context manager that intializes the context for a given + resource and rolls it back after the resource is processed. + """ + # TODO: update metadata and other resource + # specific properties here. + self.__context__.update(resource=resource) + yield self.__context__ + self.__context__.update(resource=None) + + def initialize_template_if_needed(self): + """ + Loads and configures the template environement from the site + configuration if its not done already. + """ + if not self.template: + logger.info("Generating site at [%s]" % self.site.sitepath) + self.template = Template.find_template(self.site) + logger.info("Using [%s] as the template", self.template) + + logger.info("Configuring the template environment") + self.template.configure(self.site.config) + + + def rebuild_if_needed(self): + """ + Checks if the site requries a rebuild and builds if + necessary. + """ + #TODO: Perhaps this is better suited in Site + if not len(self.site.content.child_nodes): + logger.info("Reading site contents") + self.site.build() def generate_all(self): """ Generates the entire website """ - pass + logger.info("Reading site contents") + self.initialize_template_if_needed() + self.rebuild_if_needed() - def generate_node(self, node=None): + logger.info("Generating site to [%s]" % + self.site.config.deploy_root_path) + self.__generate_node__(self.site.content) + + def generate_node_at_path(self, node_path=None): """ - Generates a single node. If node is non-existent or empty + Generates a single node. If node_path is non-existent or empty, generates the entire site. """ - pass + self.initialize_template_if_needed() + self.rebuild_if_needed() + node = None + if node_path: + node = self.site.content.node_from_path(node_path) + self.generate_node(node) - def generate_resource(self, resource=None): + def generate_node(self, node=None): + """ + Generates the given node. If node is invalid, empty or + non-existent, generates the entire website. """ - Generates a single resource. If resource is non-existent or empty + self.initialize_template_if_needed() + self.rebuild_if_needed() + if not node: + return self.generate_all() + try: + self.__generate_node__(node) + except HydeException: + self.generate_all() + + def generate_resource_at_path(self, resource_path=None): + """ + Generates a single resource. If resource_path is non-existent or empty, generats the entire website. """ - pass + self.initialize_template_if_needed() + self.rebuild_if_needed() + resource = None + if resource_path: + resource = self.site.content.resource_from_path(resource_path) + return self.generate_resource(resource) + + def generate_resource(self, resource=None): + """ + Generates the given resource. If resource is invalid, empty or + non-existent, generates the entire website. + """ + self.initialize_template_if_needed() + self.rebuild_if_needed() + if not resource: + return self.generate_all() + try: + self.__generate_resource__(resource) + except HydeException: + self.generate_all() + def __generate_node__(self, node): + logger.info("Generating [%s]", node) + for resource in node.walk_resources(): + self.__generate_resource__(resource) + def __generate_resource__(self, resource): + logger.info("Processing [%s]", resource) + with self.context_for_resource(resource) as context: + target = File(resource.source_file.get_mirror( + self.site.config.deploy_root_path, + self.site.content.source_folder)) + target.parent.make() + if resource.source_file.is_text: + logger.info("Rendering [%s]", resource) + text = self.template.render(resource.source_file.read_all(), + context) + target.write(text) + else: + logger.info("Copying binary file [%s]", resource) + resource.source_file.copy_to(target) diff --git a/hyde/layouts/basic/layout/base.html b/hyde/layouts/basic/layout/base.html index c568763..9775224 100644 --- a/hyde/layouts/basic/layout/base.html +++ b/hyde/layouts/basic/layout/base.html @@ -9,7 +9,7 @@ {% block starthead %}{% endblock starthead %} - + @@ -20,9 +20,9 @@ - {% block title %}{{page.meta.title}}{% endblock %} - - + {% block title %}{{resource.meta.title}}{% endblock %} + + @@ -46,7 +46,7 @@ {% endblock headjs %} {% block endhead %}{% endblock endhead %} - + {% block content %}
{% block container %} diff --git a/hyde/layouts/test/layout/base.html b/hyde/layouts/test/layout/base.html index 62bafb8..10c1db1 100644 --- a/hyde/layouts/test/layout/base.html +++ b/hyde/layouts/test/layout/base.html @@ -4,15 +4,15 @@ {% block starthead %}{% endblock starthead %} - - + + - {% block title %}{{page.meta.title}}{% endblock %} - - + {% block title %}{{resource.meta.title}}{% endblock %} + + - + {% block favicons %} @@ -25,7 +25,7 @@ {% endblock css %} {% block endhead %}{% endblock endhead %} - + {% block content %}
{% block container %} diff --git a/hyde/model.py b/hyde/model.py index da3a35c..310c29b 100644 --- a/hyde/model.py +++ b/hyde/model.py @@ -32,7 +32,7 @@ class Config(Expando): Represents the hyde configuration file """ - def __init__(self, site_path, config_dict=None): + def __init__(self, sitepath, config_dict=None): default_config = dict( content_root = 'content', deploy_root = 'deploy', @@ -45,7 +45,7 @@ class Config(Expando): if config_dict: conf.update(config_dict) super(Config, self).__init__(conf) - self.site_path = Folder(site_path) + self.sitepath = Folder(sitepath) @property @@ -53,25 +53,25 @@ class Config(Expando): """ Derives the deploy root path from the site path """ - return self.site_path.child_folder(self.deploy_root) + return self.sitepath.child_folder(self.deploy_root) @property def content_root_path(self): """ Derives the content root path from the site path """ - return self.site_path.child_folder(self.content_root) + return self.sitepath.child_folder(self.content_root) @property def media_root_path(self): """ Derives the media root path from the site path """ - return self.site_path.child_folder(self.media_root) + return self.sitepath.child_folder(self.media_root) @property def layout_root_path(self): """ Derives the layout root path from the site path """ - return self.site_path.child_folder(self.layout_root) + return self.sitepath.child_folder(self.layout_root) diff --git a/hyde/site.py b/hyde/site.py index 010d1bc..4684a7c 100644 --- a/hyde/site.py +++ b/hyde/site.py @@ -264,10 +264,10 @@ class Site(object): Represents the site to be generated. """ - def __init__(self, site_path=None, config=None): + def __init__(self, sitepath=None, config=None): super(Site, self).__init__() - self.site_path = Folder(str(site_path)) - self.config = config if config else Config(self.site_path) + self.sitepath = Folder(str(sitepath)) + self.config = config if config else Config(self.sitepath) self.content = RootNode(self.config.content_root_path, self) def build(self): diff --git a/hyde/template.py b/hyde/template.py index 77298f9..7fddc9b 100644 --- a/hyde/template.py +++ b/hyde/template.py @@ -17,6 +17,7 @@ class Template(object): implementations are responsible for transforming this object to match the `settings` required for the template engines. """ + abstract def render(self, text, context): @@ -24,4 +25,15 @@ class Template(object): Given the text, and the context, this function must return the rendered string. """ - abstract \ No newline at end of file + + abstract + + @staticmethod + def find_template(site): + """ + Reads the configuration to find the appropriate template. + """ + # TODO: Find the appropriate template environment + from hyde.ext.templates.jinja import Jinja2Template + template = Jinja2Template(site.sitepath) + return template \ No newline at end of file diff --git a/hyde/tests/sites/test_jinja/content/about.html b/hyde/tests/sites/test_jinja/content/about.html index fce92b4..6fd8880 100644 --- a/hyde/tests/sites/test_jinja/content/about.html +++ b/hyde/tests/sites/test_jinja/content/about.html @@ -2,6 +2,7 @@ {% block main %} Hi! - + I am a test template to make sure jinja2 generation works well with hyde. + {{resource.name}} {% endblock %} \ No newline at end of file diff --git a/hyde/tests/sites/test_jinja/layout/base.html b/hyde/tests/sites/test_jinja/layout/base.html index 62bafb8..10c1db1 100644 --- a/hyde/tests/sites/test_jinja/layout/base.html +++ b/hyde/tests/sites/test_jinja/layout/base.html @@ -4,15 +4,15 @@ {% block starthead %}{% endblock starthead %} - - + + - {% block title %}{{page.meta.title}}{% endblock %} - - + {% block title %}{{resource.meta.title}}{% endblock %} + + - + {% block favicons %} @@ -25,7 +25,7 @@ {% endblock css %} {% block endhead %}{% endblock endhead %} - + {% block content %}
{% block container %} diff --git a/hyde/tests/test_generate.py b/hyde/tests/test_generate.py new file mode 100644 index 0000000..69a2fdd --- /dev/null +++ b/hyde/tests/test_generate.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" +Use nose +`$ pip install nose` +`$ nosetests` +""" + + +from hyde.generator import Generator +from hyde.fs import FS, File, Folder +from hyde.site import Site + +from nose.tools import raises, with_setup, nottest +from pyquery import PyQuery + +TEST_SITE = File(__file__).parent.child_folder('_test') + +@nottest +def create_test_site(): + TEST_SITE.make() + TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) + +@nottest +def delete_test_site(): + TEST_SITE.delete() + +@with_setup(create_test_site, delete_test_site) +def test_generate_resource_from_path(): + site = Site(TEST_SITE) + site.build() + gen = Generator(site) + gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) + about = File(Folder(site.config.deploy_root_path).child('about.html')) + assert about.exists + text = about.read_all() + q = PyQuery(text) + assert about.name in q("div#main").text() diff --git a/hyde/tests/test_generator.py b/hyde/tests/test_generator.py deleted file mode 100644 index a8eb1a9..0000000 --- a/hyde/tests/test_generator.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -""" -test_generator.py - -Created by FlowPlayer - Lakshmi Vyas on 2010-12-29. -Copyright (c) 2010 __MyCompanyName__. All rights reserved. -""" - -import sys -import os - - -def main(): - pass - - -if __name__ == '__main__': - main() - diff --git a/hyde/tests/test_model.py b/hyde/tests/test_model.py index 13d0fb6..e980fc9 100644 --- a/hyde/tests/test_model.py +++ b/hyde/tests/test_model.py @@ -54,7 +54,7 @@ class TestConfig(object): """ def test_default_configuration(self): - c = Config(site_path=TEST_SITE_ROOT) + c = Config(sitepath=TEST_SITE_ROOT) for root in ['content', 'layout', 'media']: name = root + '_root' path = name + '_path' @@ -67,11 +67,11 @@ class TestConfig(object): def test_conf1(self): - c = Config(site_path=TEST_SITE_ROOT, config_dict=yaml.load(self.conf1)) + c = Config(sitepath=TEST_SITE_ROOT, config_dict=yaml.load(self.conf1)) assert c.content_root_path == TEST_SITE_ROOT.child_folder('stuff') def test_conf2(self): - c = Config(site_path=TEST_SITE_ROOT, config_dict=yaml.load(self.conf2)) + c = Config(sitepath=TEST_SITE_ROOT, config_dict=yaml.load(self.conf2)) assert c.content_root_path == TEST_SITE_ROOT.child_folder('site/stuff') assert c.media_root_path == TEST_SITE_ROOT.child_folder('mmm') assert c.media_url == TEST_SITE_ROOT.child_folder('/media') diff --git a/hyde/tests/test_site.py b/hyde/tests/test_site.py index e045985..404551a 100644 --- a/hyde/tests/test_site.py +++ b/hyde/tests/test_site.py @@ -91,7 +91,7 @@ class TestSiteWithConfig(object): TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) cls.config_file = File(cls.SITE_PATH.child('alternate.yaml')) with open(cls.config_file.path) as config: - cls.config = Config(site_path=cls.SITE_PATH, config_dict=yaml.load(config)) + cls.config = Config(sitepath=cls.SITE_PATH, config_dict=yaml.load(config)) cls.SITE_PATH.child_folder('content').rename_to(cls.config.content_root) @classmethod diff --git a/hyde/tests/util.py b/hyde/tests/util.py index c34993e..08677cd 100644 --- a/hyde/tests/util.py +++ b/hyde/tests/util.py @@ -12,7 +12,7 @@ def assert_html_equals(expected, actual, sanitize=None): expected = sanitize(expected) actual = sanitize(actual) assert expected == actual - + def trap_exit_fail(f): def test_wrapper(*args): try: