@@ -84,19 +84,34 @@ class MetaPlugin(Plugin): | |||||
match = re.match(yaml_finder, text) | match = re.match(yaml_finder, text) | ||||
if not match: | if not match: | ||||
logger.info("No metadata found in resource [%s]" % resource) | logger.info("No metadata found in resource [%s]" % resource) | ||||
return text | |||||
text = text[match.end():] | |||||
data = match.group(1) | |||||
data = {} | |||||
else: | |||||
text = text[match.end():] | |||||
data = match.group(1) | |||||
if not hasattr(resource, 'meta') or not resource.meta: | if not hasattr(resource, 'meta') or not resource.meta: | ||||
if not hasattr(resource.node, 'meta'): | if not hasattr(resource.node, 'meta'): | ||||
resource.node.meta = Metadata({}) | resource.node.meta = Metadata({}) | ||||
resource.meta = Metadata(data, resource.node.meta) | resource.meta = Metadata(data, resource.node.meta) | ||||
else: | else: | ||||
resource.meta.update(data) | resource.meta.update(data) | ||||
self.__update_standard_attributes__(resource) | |||||
logger.info("Successfully loaded metadata from resource [%s]" | logger.info("Successfully loaded metadata from resource [%s]" | ||||
% resource) | % resource) | ||||
return text | return text | ||||
def __update_standard_attributes__(self, obj): | |||||
""" | |||||
Updates standard attributes on the resource and | |||||
page based on the provided meta data. | |||||
""" | |||||
if not hasattr(obj, 'meta'): | |||||
return | |||||
standard_attributes = ['is_processable', 'uses_template'] | |||||
for attr in standard_attributes: | |||||
if hasattr(obj.meta, attr): | |||||
setattr(obj, attr, getattr(obj.meta, attr)) | |||||
def __read_node__(self, node): | def __read_node__(self, node): | ||||
""" | """ | ||||
Look for nodemeta.yaml. Load and assign it to the node. | Look for nodemeta.yaml. Load and assign it to the node. | ||||
@@ -112,6 +127,7 @@ class MetaPlugin(Plugin): | |||||
node.meta = Metadata(metadata, parent=parent_meta) | node.meta = Metadata(metadata, parent=parent_meta) | ||||
else: | else: | ||||
node.meta = Metadata({}, parent=parent_meta) | node.meta = Metadata({}, parent=parent_meta) | ||||
self.__update_standard_attributes__(node) | |||||
def begin_node(self, node): | def begin_node(self, node): | ||||
""" | """ | ||||
@@ -4,7 +4,8 @@ Jinja template utilties | |||||
from hyde.fs import File, Folder | from hyde.fs import File, Folder | ||||
from hyde.template import Template | from hyde.template import Template | ||||
from jinja2 import contextfunction, Environment, FileSystemLoader, Undefined, nodes | |||||
from jinja2 import contextfunction, Environment, FileSystemLoader | |||||
from jinja2 import environmentfilter, Markup, Undefined, nodes | |||||
from jinja2.ext import Extension | from jinja2.ext import Extension | ||||
from jinja2.exceptions import TemplateError | from jinja2.exceptions import TemplateError | ||||
@@ -30,6 +31,20 @@ def content_url(context, path): | |||||
site = context['site'] | site = context['site'] | ||||
return Folder(site.config.base_url).child(path) | return Folder(site.config.base_url).child(path) | ||||
@environmentfilter | |||||
def markdown(env, value): | |||||
try: | |||||
import markdown | |||||
except ImportError: | |||||
raise TemplateError("Cannot load the markdown library") | |||||
output = value | |||||
d = {} | |||||
if hasattr(env.config, 'markdown'): | |||||
d['extensions'] = getattr(env.config.markdown, 'extensions', []) | |||||
d['extension_configs'] = getattr(env.config.markdown, 'extension_configs', {}) | |||||
md = markdown.Markdown(**d) | |||||
return md.convert(output) | |||||
class Markdown(Extension): | class Markdown(Extension): | ||||
tags = set(['markdown']) | tags = set(['markdown']) | ||||
@@ -43,19 +58,10 @@ class Markdown(Extension): | |||||
).set_lineno(lineno) | ).set_lineno(lineno) | ||||
def _render_markdown(self, caller=None): | def _render_markdown(self, caller=None): | ||||
try: | |||||
import markdown | |||||
except ImportError: | |||||
raise TemplateError("Cannot load the markdown library") | |||||
if not caller: | if not caller: | ||||
return '' | return '' | ||||
output = caller().strip() | output = caller().strip() | ||||
d = {} | |||||
if hasattr(self.environment.config, 'markdown'): | |||||
d['extensions'] = getattr(self.environment.config.markdown, 'extensions', []) | |||||
d['extension_configs'] = getattr(self.environment.config.markdown, 'extension_configs', {}) | |||||
md = markdown.Markdown(**d) | |||||
return md.convert(output) | |||||
return markdown(self.environment, output) | |||||
# pylint: disable-msg=W0104,E0602,W0613,R0201 | # pylint: disable-msg=W0104,E0602,W0613,R0201 | ||||
class Jinja2Template(Template): | class Jinja2Template(Template): | ||||
@@ -80,13 +86,14 @@ class Jinja2Template(Template): | |||||
self.env = Environment(loader=loader, | self.env = Environment(loader=loader, | ||||
undefined=SilentUndefined, | undefined=SilentUndefined, | ||||
trim_blocks=True, | trim_blocks=True, | ||||
extensions=[Markdown, | |||||
'jinja2.ext.do', | |||||
extensions=[Markdown, | |||||
'jinja2.ext.do', | |||||
'jinja2.ext.loopcontrols', | 'jinja2.ext.loopcontrols', | ||||
'jinja2.ext.with_']) | 'jinja2.ext.with_']) | ||||
self.env.globals['media_url'] = media_url | self.env.globals['media_url'] = media_url | ||||
self.env.globals['content_url'] = content_url | self.env.globals['content_url'] = content_url | ||||
self.env.extend(config=config) | self.env.extend(config=config) | ||||
self.env.filters['markdown'] = markdown | |||||
try: | try: | ||||
from typogrify.templatetags import jinja2_filters | from typogrify.templatetags import jinja2_filters | ||||
@@ -212,8 +212,9 @@ class Generator(object): | |||||
if resource.source_file.is_text: | if resource.source_file.is_text: | ||||
text = resource.source_file.read_all() | text = resource.source_file.read_all() | ||||
text = self.events.begin_text_resource(resource, text) or text | text = self.events.begin_text_resource(resource, text) or text | ||||
logger.info("Rendering [%s]", resource) | |||||
text = self.template.render(text, context) | |||||
if resource.uses_template: | |||||
logger.info("Rendering [%s]", resource) | |||||
text = self.template.render(text, context) | |||||
text = self.events.text_resource_complete( | text = self.events.text_resource_complete( | ||||
resource, text) or text | resource, text) or text | ||||
target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
@@ -120,10 +120,7 @@ class HydeWebServer(HTTPServer): | |||||
self.exception_count += 1 | self.exception_count += 1 | ||||
logger.error('Error occured when regenerating the site [%s]' | logger.error('Error occured when regenerating the site [%s]' | ||||
% exception.message) | % exception.message) | ||||
if self.exception_count > 1: | |||||
self.shutdown() | |||||
exit() | |||||
else: | |||||
if self.exception_count <= 1: | |||||
self.__reinit__() | self.__reinit__() | ||||
@@ -22,6 +22,7 @@ class Processable(object): | |||||
super(Processable, self).__init__() | super(Processable, self).__init__() | ||||
self.source = FS.file_or_folder(source) | self.source = FS.file_or_folder(source) | ||||
self.is_processable = True | self.is_processable = True | ||||
self.uses_template = True | |||||
@property | @property | ||||
def name(self): | def name(self): | ||||
@@ -26,6 +26,26 @@ class TestMeta(object): | |||||
def tearDown(self): | def tearDown(self): | ||||
TEST_SITE.delete() | TEST_SITE.delete() | ||||
def test_can_set_standard_attributes(self): | |||||
text = """ | |||||
--- | |||||
is_processable: False | |||||
--- | |||||
{% extends "base.html" %} | |||||
""" | |||||
about2 = File(TEST_SITE.child('content/about2.html')) | |||||
about2.write(text) | |||||
s = Site(TEST_SITE) | |||||
s.load() | |||||
res = s.content.resource_from_path(about2.path) | |||||
assert res.is_processable | |||||
s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin'] | |||||
gen = Generator(s) | |||||
gen.generate_all() | |||||
assert not res.meta.is_processable | |||||
assert not res.is_processable | |||||
def test_can_load_front_matter(self): | def test_can_load_front_matter(self): | ||||
d = {'title': 'A nice title', | d = {'title': 'A nice title', | ||||
'author': 'Lakshmi Vyas', | 'author': 'Lakshmi Vyas', | ||||
@@ -47,6 +47,20 @@ def test_generate_resource_from_path_with_is_processable_false(): | |||||
about = File(Folder(site.config.deploy_root_path).child('about.html')) | about = File(Folder(site.config.deploy_root_path).child('about.html')) | ||||
assert not about.exists | assert not about.exists | ||||
@with_setup(create_test_site, delete_test_site) | |||||
def test_generate_resource_from_path_with_uses_template_false(): | |||||
site = Site(TEST_SITE) | |||||
site.load() | |||||
resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
resource.uses_template = False | |||||
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() | |||||
expected = resource.source_file.read_all() | |||||
assert text == expected | |||||
@with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
def test_generate_resource_from_path_with_deploy_override(): | def test_generate_resource_from_path_with_deploy_override(): | ||||
site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||