@@ -16,9 +16,23 @@ class Metadata(Expando): | |||||
""" | """ | ||||
Container class for yaml meta data. | Container class for yaml meta data. | ||||
""" | """ | ||||
def __init__(self, text): | |||||
super(Metadata, self).__init__(yaml.load(text)) | |||||
def __init__(self, data, parent=None): | |||||
super(Metadata, self).__init__({}) | |||||
if parent: | |||||
self.update(parent.__dict__) | |||||
if data: | |||||
self.update(data) | |||||
def update(self, data): | |||||
""" | |||||
Updates the metadata with new stuff | |||||
""" | |||||
if isinstance(data, dict): | |||||
super(Metadata, self).update(data) | |||||
else: | |||||
super(Metadata, self).update(yaml.load(data)) | |||||
class MetaPlugin(Plugin): | class MetaPlugin(Plugin): | ||||
@@ -38,21 +52,46 @@ class MetaPlugin(Plugin): | |||||
def __init__(self, site): | def __init__(self, site): | ||||
super(MetaPlugin, self).__init__(site) | super(MetaPlugin, self).__init__(site) | ||||
def begin_site(self): | |||||
metadata = self.site.config.meta if hasattr(self.site.config, 'meta') else {} | |||||
self.site.meta = Metadata(metadata) | |||||
def begin_node(self, node): | |||||
""" | |||||
Look for nodemeta.yaml. Load and assign it to the node. | |||||
""" | |||||
nodemeta = node.get_resource('nodemeta.yaml') | |||||
parent_meta = node.parent.meta if node.parent else self.site.meta | |||||
if nodemeta: | |||||
nodemeta.is_processable = False | |||||
metadata = nodemeta.source_file.read_all() | |||||
if hasattr(node, 'meta') and node.meta: | |||||
node.meta.update(metadata) | |||||
else: | |||||
node.meta = Metadata(metadata, parent=parent_meta) | |||||
else: | |||||
node.meta = Metadata({}, parent=parent_meta) | |||||
def begin_text_resource(self, resource, text): | def begin_text_resource(self, resource, text): | ||||
""" | """ | ||||
Load meta data by looking for the marker. | Load meta data by looking for the marker. | ||||
Once loaded, remove the meta area from the text. | Once loaded, remove the meta area from the text. | ||||
""" | """ | ||||
logger.info("Trying to load metadata from resource [%s]" % resource) | logger.info("Trying to load metadata from resource [%s]" % resource) | ||||
# re from spjwebster's lanyon | |||||
yaml_finder = re.compile( r"^\s*---\s*\n((?:.|\n)+?)\n---\s*\n", re.MULTILINE) | |||||
yaml_finder = re.compile( | |||||
r"^\s*---\s*\n((?:.|\n)+?)\n\s*---\s*\n", | |||||
re.MULTILINE) | |||||
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 | return text | ||||
text = text[match.end():] | text = text[match.end():] | ||||
resource.meta = Metadata(match.group(1)) | |||||
data = match.group(1) | |||||
if not hasattr(resource, 'meta') or not resource.meta: | |||||
resource.meta = Metadata(data, resource.node.meta) | |||||
else: | |||||
resource.meta.update(data) | |||||
logger.info("Successfully loaded metadata from resource [%s]" | logger.info("Successfully loaded metadata from resource [%s]" | ||||
% resource) | % resource) | ||||
return text | |||||
return text |
@@ -11,6 +11,12 @@ class Expando(object): | |||||
def __init__(self, d): | def __init__(self, d): | ||||
super(Expando, self).__init__() | super(Expando, self).__init__() | ||||
self.update(d) | |||||
def update(self, d): | |||||
""" | |||||
Updates the expando with a new dictionary | |||||
""" | |||||
d = d or {} | d = d or {} | ||||
for key, value in d.items(): | for key, value in d.items(): | ||||
setattr(self, key, Expando.transform(value)) | setattr(self, key, Expando.transform(value)) | ||||
@@ -85,6 +85,24 @@ class Node(Processable): | |||||
self.child_nodes = [] | self.child_nodes = [] | ||||
self.resources = [] | self.resources = [] | ||||
def contains_resource(self, resource_name): | |||||
""" | |||||
Returns True if the given resource name exists as a file | |||||
in this node's source folder. | |||||
""" | |||||
return File(self.source_folder.child(resource_name)).exists | |||||
def get_resource(self, resource_name): | |||||
""" | |||||
Gets the resource if the given resource name exists as a file | |||||
in this node's source folder. | |||||
""" | |||||
if self.contains_resource(resource_name): | |||||
return self.root.resource_from_path(self.source_folder.child(resource_name)) | |||||
return None | |||||
def add_child_node(self, folder): | def add_child_node(self, folder): | ||||
""" | """ | ||||
Creates a new child node and adds it to the list of child nodes. | Creates a new child node and adds it to the list of child nodes. | ||||
@@ -20,18 +20,16 @@ class TestMeta(object): | |||||
def setUp(self): | def setUp(self): | ||||
TEST_SITE.make() | TEST_SITE.make() | ||||
TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
TEST_SITE.parent.child_folder( | |||||
'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
def tearDown(self): | def tearDown(self): | ||||
TEST_SITE.delete() | TEST_SITE.delete() | ||||
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', | ||||
'twitter': 'lakshmivyas' | |||||
} | |||||
'twitter': 'lakshmivyas'} | |||||
text = """ | text = """ | ||||
--- | --- | ||||
title: %(title)s | title: %(title)s | ||||
@@ -68,4 +66,149 @@ twitter: %(twitter)s | |||||
text = target.read_all() | text = target.read_all() | ||||
q = PyQuery(text) | q = PyQuery(text) | ||||
for k, v in d.items(): | for k, v in d.items(): | ||||
assert v in q("span." + k).text() | |||||
assert v in q("span." + k).text() | |||||
def test_can_load_from_node_meta(self): | |||||
d = {'title': 'A nice title', | |||||
'author': 'Lakshmi Vyas', | |||||
'twitter': 'lakshmivyas'} | |||||
text = """ | |||||
--- | |||||
title: Even nicer title | |||||
--- | |||||
{%% extends "base.html" %%} | |||||
{%% block main %%} | |||||
Hi! | |||||
I am a test template to make sure jinja2 generation works well with hyde. | |||||
<span class="title">{{resource.meta.title}}</span> | |||||
<span class="author">{{resource.meta.author}}</span> | |||||
<span class="twitter">{{resource.meta.twitter}}</span> | |||||
{%% endblock %%} | |||||
""" | |||||
about2 = File(TEST_SITE.child('content/about2.html')) | |||||
about2.write(text % d) | |||||
meta = File(TEST_SITE.child('content/nodemeta.yaml')) | |||||
meta.write(yaml.dump(d)) | |||||
s = Site(TEST_SITE) | |||||
s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin'] | |||||
gen = Generator(s) | |||||
gen.generate_all() | |||||
res = s.content.resource_from_path(about2.path) | |||||
assert hasattr(res, 'meta') | |||||
assert hasattr(res.meta, 'title') | |||||
assert hasattr(res.meta, 'author') | |||||
assert hasattr(res.meta, 'twitter') | |||||
assert res.meta.title == "Even nicer title" | |||||
assert res.meta.author == "Lakshmi Vyas" | |||||
assert res.meta.twitter == "lakshmivyas" | |||||
target = File(Folder(s.config.deploy_root_path).child('about2.html')) | |||||
text = target.read_all() | |||||
q = PyQuery(text) | |||||
for k, v in d.items(): | |||||
if not k == 'title': | |||||
assert v in q("span." + k).text() | |||||
assert q("span.title").text() == "Even nicer title" | |||||
def test_can_load_from_site_meta(self): | |||||
d = {'title': 'A nice title', | |||||
'author': 'Lakshmi Vyas'} | |||||
text = """ | |||||
--- | |||||
title: Even nicer title | |||||
--- | |||||
{%% extends "base.html" %%} | |||||
{%% block main %%} | |||||
Hi! | |||||
I am a test template to make sure jinja2 generation works well with hyde. | |||||
<span class="title">{{resource.meta.title}}</span> | |||||
<span class="author">{{resource.meta.author}}</span> | |||||
<span class="twitter">{{resource.meta.twitter}}</span> | |||||
{%% endblock %%} | |||||
""" | |||||
about2 = File(TEST_SITE.child('content/about2.html')) | |||||
about2.write(text % d) | |||||
meta = File(TEST_SITE.child('content/nodemeta.yaml')) | |||||
meta.write(yaml.dump(d)) | |||||
s = Site(TEST_SITE) | |||||
s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin'] | |||||
s.config.meta = { | |||||
'author': 'Lakshmi', | |||||
'twitter': 'lakshmivyas' | |||||
} | |||||
gen = Generator(s) | |||||
gen.generate_all() | |||||
res = s.content.resource_from_path(about2.path) | |||||
assert hasattr(res, 'meta') | |||||
assert hasattr(res.meta, 'title') | |||||
assert hasattr(res.meta, 'author') | |||||
assert hasattr(res.meta, 'twitter') | |||||
assert res.meta.title == "Even nicer title" | |||||
assert res.meta.author == "Lakshmi Vyas" | |||||
assert res.meta.twitter == "lakshmivyas" | |||||
target = File(Folder(s.config.deploy_root_path).child('about2.html')) | |||||
text = target.read_all() | |||||
q = PyQuery(text) | |||||
for k, v in d.items(): | |||||
if not k == 'title': | |||||
assert v in q("span." + k).text() | |||||
assert q("span.title").text() == "Even nicer title" | |||||
def test_multiple_levels(self): | |||||
page_d = {'title': 'An even nicer title'} | |||||
blog_d = {'author': 'Lakshmi'} | |||||
content_d = {'title': 'A nice title', | |||||
'author': 'Lakshmi Vyas'} | |||||
site_d = {'author': 'Lakshmi', | |||||
'twitter': 'lakshmivyas'} | |||||
text = """ | |||||
--- | |||||
title: %(title)s | |||||
--- | |||||
{%% extends "base.html" %%} | |||||
{%% block main %%} | |||||
Hi! | |||||
I am a test template to make sure jinja2 generation works well with hyde. | |||||
<span class="title">{{resource.meta.title}}</span> | |||||
<span class="author">{{resource.meta.author}}</span> | |||||
<span class="twitter">{{resource.meta.twitter}}</span> | |||||
{%% endblock %%} | |||||
""" | |||||
about2 = File(TEST_SITE.child('content/blog/about2.html')) | |||||
about2.write(text % page_d) | |||||
content_meta = File(TEST_SITE.child('content/nodemeta.yaml')) | |||||
content_meta.write(yaml.dump(content_d)) | |||||
content_meta = File(TEST_SITE.child('content/blog/nodemeta.yaml')) | |||||
content_meta.write(yaml.dump(blog_d)) | |||||
s = Site(TEST_SITE) | |||||
s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin'] | |||||
s.config.meta = site_d | |||||
gen = Generator(s) | |||||
gen.generate_all() | |||||
expected = {} | |||||
expected.update(site_d) | |||||
expected.update(content_d) | |||||
expected.update(blog_d) | |||||
expected.update(page_d) | |||||
res = s.content.resource_from_path(about2.path) | |||||
assert hasattr(res, 'meta') | |||||
for k, v in expected.items(): | |||||
assert hasattr(res.meta, k) | |||||
assert getattr(res.meta, k) == v | |||||
target = File(Folder(s.config.deploy_root_path).child('blog/about2.html')) | |||||
text = target.read_all() | |||||
q = PyQuery(text) | |||||
for k, v in expected.items(): | |||||
assert v in q("span." + k).text() |
@@ -26,6 +26,18 @@ def test_expando_three_levels(): | |||||
assert x.b.c == d['b']['c'] | assert x.b.c == d['b']['c'] | ||||
assert x.b.d.e == d['b']['d']['e'] | assert x.b.d.e == d['b']['d']['e'] | ||||
def test_expando_update(): | |||||
d1 = {"a": 123, "b": "abc"} | |||||
x = Expando(d1) | |||||
assert x.a == d1['a'] | |||||
assert x.b == d1['b'] | |||||
d = {"b": {"c": 456, "d": {"e": "abc"}}, "f": "lmn"} | |||||
x.update(d) | |||||
assert x.a == d1['a'] | |||||
assert x.b.c == d['b']['c'] | |||||
assert x.b.d.e == d['b']['d']['e'] | |||||
assert x.f == d["f"] | |||||
TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | ||||
import yaml | import yaml | ||||
class TestConfig(object): | class TestConfig(object): | ||||
@@ -82,6 +82,20 @@ def test_walk_resources(): | |||||
expected.sort() | expected.sort() | ||||
assert pages == expected | assert pages == expected | ||||
def test_contains_resource(): | |||||
s = Site(TEST_SITE_ROOT) | |||||
s.load() | |||||
path = 'blog/2010/december' | |||||
node = s.content.node_from_relative_path(path) | |||||
assert node.contains_resource('merry-christmas.html') | |||||
def test_get_resource(): | |||||
s = Site(TEST_SITE_ROOT) | |||||
s.load() | |||||
path = 'blog/2010/december' | |||||
node = s.content.node_from_relative_path(path) | |||||
resource = node.get_resource('merry-christmas.html') | |||||
assert resource == s.content.resource_from_relative_path(Folder(path).child('merry-christmas.html')) | |||||
class TestSiteWithConfig(object): | class TestSiteWithConfig(object): | ||||