Browse Source

Refactored generator

main
Lakshmi Vyasarajan 14 years ago
parent
commit
d7c9e772a0
14 changed files with 209 additions and 89 deletions
  1. +4
    -27
      hyde/engine.py
  2. +120
    -7
      hyde/generator.py
  3. +5
    -5
      hyde/layouts/basic/layout/base.html
  4. +7
    -7
      hyde/layouts/test/layout/base.html
  5. +6
    -6
      hyde/model.py
  6. +3
    -3
      hyde/site.py
  7. +13
    -1
      hyde/template.py
  8. +2
    -1
      hyde/tests/sites/test_jinja/content/about.html
  9. +7
    -7
      hyde/tests/sites/test_jinja/layout/base.html
  10. +37
    -0
      hyde/tests/test_generate.py
  11. +0
    -20
      hyde/tests/test_generator.py
  12. +3
    -3
      hyde/tests/test_model.py
  13. +1
    -1
      hyde/tests/test_site.py
  14. +1
    -1
      hyde/tests/util.py

+ 4
- 27
hyde/engine.py View File

@@ -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)

from hyde.generator import Generator
gen = Generator(site)
gen.generate_all()

+ 120
- 7
hyde/generator.py View File

@@ -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)

+ 5
- 5
hyde/layouts/basic/layout/base.html View File

@@ -9,7 +9,7 @@
<!--[if (gte IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
<head>
{% block starthead %}{% endblock starthead %}
<meta charset="{{page.meta.charset|default('utf-8')}}">
<meta charset="{{resource.meta.charset|default('utf-8')}}">

<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
@@ -20,9 +20,9 @@
<!-- meta element for compatibility mode needs to be before all elements except title & meta msdn.microsoft.com/en-us/library/cc288325(VS.85).aspx -->
<!-- Chrome Frame is only invoked if meta element for compatibility mode is within the first 1K bytes code.google.com/p/chromium/issues/detail?id=23003 -->

<title>{% block title %}{{page.meta.title}}{% endblock %}</title>
<meta name="description" content="{{page.meta.description}}">
<meta name="author" content="{{page.meta.author}}">
<title>{% block title %}{{resource.meta.title}}{% endblock %}</title>
<meta name="description" content="{{resource.meta.description}}">
<meta name="author" content="{{resource.meta.author}}">

<!-- Mobile viewport optimized: j.mp/bplateviewport -->
<meta name="viewport" content="{{page.meta.viewport|default('width=device-width, initial-scale=1.0')">
@@ -46,7 +46,7 @@
{% endblock headjs %}
{% block endhead %}{% endblock endhead %}
</head>
<body id="{{page.id if page.id else page.name_without_extension}}">
<body id="{{resource.id if resource.id else resource.name_without_extension}}">
{% block content %}
<div id="container">
{% block container %}


+ 7
- 7
hyde/layouts/test/layout/base.html View File

@@ -4,15 +4,15 @@
<html lang="en">
<head>
{% block starthead %}{% endblock starthead %}
<meta charset="{{page.meta.charset|default('utf-8')}}">
<meta http-equiv="X-UA-Compatible" content="{{page.meta.compatibility|default('IE=edge,chrome=1')}}">
<meta charset="{{resource.meta.charset|default('utf-8')}}">
<meta http-equiv="X-UA-Compatible" content="{{resource.meta.compatibility|default('IE=edge,chrome=1')}}">

<title>{% block title %}{{page.meta.title}}{% endblock %}</title>
<meta name="description" content="{{page.meta.description}}">
<meta name="author" content="{{page.meta.author}}">
<title>{% block title %}{{resource.meta.title}}{% endblock %}</title>
<meta name="description" content="{{resource.meta.description}}">
<meta name="author" content="{{resource.meta.author}}">

<!-- Mobile viewport optimized: j.mp/bplateviewport -->
<meta name="viewport" content="{{page.meta.viewport|default('width=device-width, initial-scale=1.0')}}">
<meta name="viewport" content="{{resource.meta.viewport|default('width=device-width, initial-scale=1.0')}}">

{% block favicons %}
<!-- Place favicon.ico & apple-touch-icon.png in the root of your domain and delete these references -->
@@ -25,7 +25,7 @@
{% endblock css %}
{% block endhead %}{% endblock endhead %}
</head>
<body id="{{page.id if page.id else page.name_without_extension}}">
<body id="{{resource.id if resource.id else resource.name_without_extension}}">
{% block content %}
<div id="container">
{% block container %}


+ 6
- 6
hyde/model.py View File

@@ -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)

+ 3
- 3
hyde/site.py View File

@@ -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):


+ 13
- 1
hyde/template.py View File

@@ -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

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

+ 2
- 1
hyde/tests/sites/test_jinja/content/about.html View File

@@ -2,6 +2,7 @@

{% block main %}
Hi!
I am a test template to make sure jinja2 generation works well with hyde.
{{resource.name}}
{% endblock %}

+ 7
- 7
hyde/tests/sites/test_jinja/layout/base.html View File

@@ -4,15 +4,15 @@
<html lang="en">
<head>
{% block starthead %}{% endblock starthead %}
<meta charset="{{page.meta.charset|default('utf-8')}}">
<meta http-equiv="X-UA-Compatible" content="{{page.meta.compatibility|default('IE=edge,chrome=1')}}">
<meta charset="{{resource.meta.charset|default('utf-8')}}">
<meta http-equiv="X-UA-Compatible" content="{{resource.meta.compatibility|default('IE=edge,chrome=1')}}">

<title>{% block title %}{{page.meta.title}}{% endblock %}</title>
<meta name="description" content="{{page.meta.description}}">
<meta name="author" content="{{page.meta.author}}">
<title>{% block title %}{{resource.meta.title}}{% endblock %}</title>
<meta name="description" content="{{resource.meta.description}}">
<meta name="author" content="{{resource.meta.author}}">

<!-- Mobile viewport optimized: j.mp/bplateviewport -->
<meta name="viewport" content="{{page.meta.viewport|default('width=device-width, initial-scale=1.0')}}">
<meta name="viewport" content="{{resource.meta.viewport|default('width=device-width, initial-scale=1.0')}}">

{% block favicons %}
<!-- Place favicon.ico & apple-touch-icon.png in the root of your domain and delete these references -->
@@ -25,7 +25,7 @@
{% endblock css %}
{% block endhead %}{% endblock endhead %}
</head>
<body id="{{page.id if page.id else page.name_without_extension}}">
<body id="{{resource.id if resource.id else resource.name_without_extension}}">
{% block content %}
<div id="container">
{% block container %}


+ 37
- 0
hyde/tests/test_generate.py View File

@@ -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()

+ 0
- 20
hyde/tests/test_generator.py View File

@@ -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()


+ 3
- 3
hyde/tests/test_model.py View File

@@ -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')


+ 1
- 1
hyde/tests/test_site.py View File

@@ -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


+ 1
- 1
hyde/tests/util.py View File

@@ -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:


Loading…
Cancel
Save