@@ -16,7 +16,7 @@ import yaml | |||||
HYDE_LAYOUTS = "HYDE_LAYOUTS" | HYDE_LAYOUTS = "HYDE_LAYOUTS" | ||||
logger = getLoggerWithConsoleHandler('hyde.engine') | |||||
logger = getLoggerWithConsoleHandler('hyde') | |||||
class Engine(Application): | class Engine(Application): | ||||
""" | """ | ||||
@@ -104,6 +104,21 @@ class Jinja2Template(Template): | |||||
jinja2_filters.register(self.env) | jinja2_filters.register(self.env) | ||||
def get_dependencies(self, text): | |||||
""" | |||||
Finds dependencies hierarchically based on the included | |||||
files. | |||||
""" | |||||
from jinja2.meta import find_referenced_templates | |||||
ast = self.env.parse(text) | |||||
tpls = find_referenced_templates(ast) | |||||
deps = [] | |||||
for dep in tpls: | |||||
deps.append(dep) | |||||
source = self.env.loader.get_source(self.env, dep)[0] | |||||
deps.extend(self.get_dependencies(source)) | |||||
return list(set(deps)) | |||||
@property | @property | ||||
def exception_class(self): | def exception_class(self): | ||||
return TemplateError | return TemplateError | ||||
@@ -2,7 +2,7 @@ | |||||
The generator class and related utility functions. | The generator class and related utility functions. | ||||
""" | """ | ||||
from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
from hyde.fs import File | |||||
from hyde.fs import File, Folder | |||||
from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
from hyde.template import Template | from hyde.template import Template | ||||
@@ -110,6 +110,29 @@ class Generator(object): | |||||
logger.info("Generation Complete") | logger.info("Generation Complete") | ||||
self.events.generation_complete() | self.events.generation_complete() | ||||
def has_resource_changed(self, resource): | |||||
target = File(self.site.config.deploy_root_path.child( | |||||
resource.relative_deploy_path)) | |||||
if not target.exists or target.older_than(resource.source_file): | |||||
return True | |||||
if resource.source_file.is_binary: | |||||
return False | |||||
deps = self.template.get_dependencies(resource.source_file.read_all()) | |||||
if not deps or None in deps: | |||||
return True | |||||
content = self.site.content.source_folder | |||||
layout = Folder(self.site.sitepath).child_folder('layout') | |||||
for dep in deps: | |||||
source = File(content.child(dep)) | |||||
if not source.exists: | |||||
source = File(layout.child(dep)) | |||||
if not source.exists: | |||||
return True | |||||
if target.older_than(source): | |||||
return True | |||||
return False | |||||
def generate_all(self): | def generate_all(self): | ||||
""" | """ | ||||
Generates the entire website | Generates the entire website | ||||
@@ -10,9 +10,9 @@ from BaseHTTPServer import HTTPServer | |||||
from hyde.fs import File, Folder | from hyde.fs import File, Folder | ||||
from hyde.site import Site | from hyde.site import Site | ||||
from hyde.generator import Generator | from hyde.generator import Generator | ||||
from hyde.util import getLoggerWithConsoleHandler | |||||
logger = getLoggerWithConsoleHandler('hyde.engine') | |||||
from hyde.util import getLoggerWithNullHandler | |||||
logger = getLoggerWithNullHandler('hyde.server') | |||||
class HydeRequestHandler(SimpleHTTPRequestHandler): | class HydeRequestHandler(SimpleHTTPRequestHandler): | ||||
""" | """ | ||||
@@ -131,7 +131,7 @@ class HydeWebServer(HTTPServer): | |||||
""" | """ | ||||
target = self.site.config.deploy_root_path.child( | target = self.site.config.deploy_root_path.child( | ||||
resource.relative_deploy_path) | resource.relative_deploy_path) | ||||
if File(target).older_than(resource.source_file): | |||||
if self.generator.has_resource_changed(resource): | |||||
try: | try: | ||||
logger.info('Generating resource [%s]' % resource) | logger.info('Generating resource [%s]' % resource) | ||||
self.generator.generate_resource(resource) | self.generator.generate_resource(resource) | ||||
@@ -25,6 +25,13 @@ class Template(object): | |||||
""" | """ | ||||
abstract | abstract | ||||
def get_dependencies(self, text): | |||||
""" | |||||
Finds the dependencies based on the included | |||||
files. | |||||
""" | |||||
return None | |||||
def render(self, text, context): | def render(self, text, context): | ||||
""" | """ | ||||
@@ -73,4 +73,24 @@ def test_generate_resource_from_path_with_deploy_override(): | |||||
assert about.exists | assert about.exists | ||||
text = about.read_all() | text = about.read_all() | ||||
q = PyQuery(text) | q = PyQuery(text) | ||||
assert resource.name in q("div#main").text() | |||||
assert resource.name in q("div#main").text() | |||||
@with_setup(create_test_site, delete_test_site) | |||||
def test_has_resource_changed(): | |||||
site = Site(TEST_SITE) | |||||
site.load() | |||||
resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
gen = Generator(site) | |||||
gen.generate_all() | |||||
assert not gen.has_resource_changed(resource) | |||||
import time | |||||
time.sleep(1) | |||||
text = resource.source_file.read_all() | |||||
resource.source_file.write(text) | |||||
assert gen.has_resource_changed(resource) | |||||
gen.generate_all() | |||||
assert not gen.has_resource_changed(resource) | |||||
time.sleep(1) | |||||
l = File(TEST_SITE.child('layout/root.html')) | |||||
l.write(l.read_all()) | |||||
assert gen.has_resource_changed(resource) |
@@ -73,6 +73,31 @@ def test_render(): | |||||
assert actual("div.article p.meta").length == 20 | assert actual("div.article p.meta").length == 20 | ||||
assert actual("div.article div.text").length == 20 | assert actual("div.article div.text").length == 20 | ||||
def test_depends(): | |||||
t = Jinja2Template(JINJA2.path) | |||||
t.configure(None) | |||||
source = File(JINJA2.child('index.html')).read_all() | |||||
deps = list(t.get_dependencies(source)) | |||||
assert len(deps) == 2 | |||||
assert 'helpers.html' in deps | |||||
assert 'layout.html' in deps | |||||
def test_depends_multi_level(): | |||||
t = Jinja2Template(JINJA2.path) | |||||
t.configure(None) | |||||
source = "{% extends 'index.html' %}" | |||||
deps = list(t.get_dependencies(source)) | |||||
assert len(deps) == 3 | |||||
assert 'helpers.html' in deps | |||||
assert 'layout.html' in deps | |||||
assert 'index.html' in deps | |||||
def test_typogrify(): | def test_typogrify(): | ||||
source = """ | source = """ | ||||
{%filter typogrify%} | {%filter typogrify%} | ||||
@@ -94,12 +119,12 @@ def test_markdown(): | |||||
t.configure(None) | t.configure(None) | ||||
html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
assert html == u'<h3>Heading 3</h3>' | assert html == u'<h3>Heading 3</h3>' | ||||
def test_markdown_with_extensions(): | def test_markdown_with_extensions(): | ||||
source = """ | source = """ | ||||
{%markdown%} | {%markdown%} | ||||
### Heading 3 | ### Heading 3 | ||||
{%endmarkdown%} | {%endmarkdown%} | ||||
""" | """ | ||||
t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
@@ -18,7 +18,7 @@ except: | |||||
pass | pass | ||||
def getLoggerWithConsoleHandler(logger_name): | def getLoggerWithConsoleHandler(logger_name): | ||||
logger = logging.getLogger('hyde.server') | |||||
logger = logging.getLogger(logger_name) | |||||
logger.setLevel(logging.DEBUG) | logger.setLevel(logging.DEBUG) | ||||
handler = logging.StreamHandler(sys.stdout) | handler = logging.StreamHandler(sys.stdout) | ||||
formatter = ColorFormatter(fmt="$COLOR%(levelname)s " | formatter = ColorFormatter(fmt="$COLOR%(levelname)s " | ||||