From 98bbdc694ff743eac4ba67b540d3d6bc92b8e369 Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Tue, 28 Dec 2010 08:59:52 +0530 Subject: [PATCH] Added configuration support to hyde --- hyde/fs.py | 20 ++++++ hyde/model.py | 68 ++++++++++++++++++++ hyde/site.py | 17 +++-- hyde/tests/sites/test_jinja/alternate.yaml | 7 +++ hyde/tests/sites/test_jinja/site.yaml | 15 ++--- hyde/tests/test_fs.py | 32 ++++++++++ hyde/tests/test_model.py | 73 ++++++++++++++++++++++ hyde/tests/test_site.py | 33 ++++++++++ 8 files changed, 247 insertions(+), 18 deletions(-) create mode 100644 hyde/model.py create mode 100644 hyde/tests/sites/test_jinja/alternate.yaml create mode 100644 hyde/tests/test_model.py diff --git a/hyde/fs.py b/hyde/fs.py index 9e906c0..ce3aa77 100644 --- a/hyde/fs.py +++ b/hyde/fs.py @@ -346,6 +346,26 @@ class Folder(FS): shutil.copytree(self.path, str(target)) return target + def move_to(self, destination): + """ + Moves this directory to the given destination. Returns a Folder object + that represents the moved directory. + """ + target = self.__get_destination__(destination) + logger.info("Move %s to %s" % (self, target)) + shutil.move(self.path, str(target)) + return target + + def rename_to(self, destination_name): + """ + Moves this directory to the given destination. Returns a Folder object + that represents the moved directory. + """ + target = self.parent.child_folder(destination_name) + logger.info("Rename %s to %s" % (self, target)) + shutil.move(self.path, str(target)) + return target + def _create_target_tree(self, target): """ There is a bug in dir_util that makes `copy_tree` crash if a folder in diff --git a/hyde/model.py b/hyde/model.py new file mode 100644 index 0000000..4acb7bb --- /dev/null +++ b/hyde/model.py @@ -0,0 +1,68 @@ +""" +Contains data structures and utilities for hyde. +""" +class Expando(object): + """ + A generic expando class that creates attributes from the passed in dictionary. + """ + + def __init__(self, d): + super(Expando, self).__init__() + d = d or {} + for key, value in d.items(): + setattr(self, key, Expando.transform(value)) + + @staticmethod + def transform(primitive): + """ + Creates an expando object, a sequence of expando objects or just + returns the primitive based on the primitive's type. + """ + if isinstance(primitive, dict): + return Expando(primitive) + elif isinstance(primitive, (tuple, list, set, frozenset)): + return type(primitive)(Expando.transform(attr) for attr in primitive) + else: + return primitive + + +from hyde.fs import File, Folder +class Config(Expando): + """ + Represents the hyde configuration file + """ + + def __init__(self, site_path, config_dict=None): + default_config = dict( + content_root = 'content', + media_root = 'media', + layout_root = 'layout', + media_url = '/media', + site_url = '/' + ) + conf = dict(**default_config) + if config_dict: + conf.update(config_dict) + super(Config, self).__init__(conf) + self.site_path = Folder(site_path) + + @property + def content_root_path(self): + """ + Derives the content root path from the site path + """ + return self.site_path.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) + + @property + def layout_root_path(self): + """ + Derives the layout root path from the site path + """ + return self.site_path.child_folder(self.layout_root) diff --git a/hyde/site.py b/hyde/site.py index 518420b..65adea0 100644 --- a/hyde/site.py +++ b/hyde/site.py @@ -4,8 +4,10 @@ Parses & holds information about the site to be generated. """ -from hyde.fs import File, Folder + from hyde.exceptions import HydeException +from hyde.fs import File, Folder +from hyde.model import Config, Expando import logging import os @@ -223,16 +225,14 @@ class RootNode(Node): class Site(object): """ - Represents the site to be generated + Represents the site to be generated. """ - def __init__(self, site_path): + def __init__(self, site_path=None, config=None): super(Site, self).__init__() self.site_path = Folder(str(site_path)) - - # TODO: Get the value from config - content_folder = self.site_path.child_folder('content') - self.content = RootNode(content_folder, self) + self.config = config if config else Config(self.site_path) + self.content = RootNode(self.config.content_root_path, self ) self.node_map = {} self.resource_map = {} @@ -241,5 +241,4 @@ class Site(object): Walks the content and media folders to build up the sitemap. """ - self.content.build() - + self.content.build() \ No newline at end of file diff --git a/hyde/tests/sites/test_jinja/alternate.yaml b/hyde/tests/sites/test_jinja/alternate.yaml new file mode 100644 index 0000000..4fee947 --- /dev/null +++ b/hyde/tests/sites/test_jinja/alternate.yaml @@ -0,0 +1,7 @@ +mode: development +content_root: stuff # Relative path from site root +media_root: media # Relative path from site root +media_url: /media +widgets: +plugins: +aggregators: \ No newline at end of file diff --git a/hyde/tests/sites/test_jinja/site.yaml b/hyde/tests/sites/test_jinja/site.yaml index ab997cb..95aac84 100644 --- a/hyde/tests/sites/test_jinja/site.yaml +++ b/hyde/tests/sites/test_jinja/site.yaml @@ -1,9 +1,6 @@ -site: - mode: development - media: - root: - path: media # Relative path from site root (the directory where this file exists) - url: /media - widgets: - plugins: - aggregators: \ No newline at end of file +mode: development +media_root:: media # Relative path from site root (the directory where this file exists) +media_url: /media +widgets: +plugins: +aggregators: \ No newline at end of file diff --git a/hyde/tests/test_fs.py b/hyde/tests/test_fs.py index ba31c65..4e29106 100644 --- a/hyde/tests/test_fs.py +++ b/hyde/tests/test_fs.py @@ -187,6 +187,38 @@ def test_copy_folder_contents(): for f in [HELPERS, INDEX, LAYOUT]: assert File(DATA_ROOT.child(f.name)).exists +@with_setup(setup_data, cleanup_data) +def test_move_folder(): + DATA_JUNK = DATA_ROOT.child_folder('junk') + assert not DATA_JUNK.exists + JINJA2.copy_contents_to(DATA_JUNK) + for f in [HELPERS, INDEX, LAYOUT]: + assert File(DATA_JUNK.child(f.name)).exists + DATA_JUNK2 = DATA_ROOT.child_folder('junk2') + assert DATA_JUNK.exists + assert not DATA_JUNK2.exists + DATA_JUNK.move_to(DATA_JUNK2) + assert not DATA_JUNK.exists + assert DATA_JUNK2.exists + for f in [HELPERS, INDEX, LAYOUT]: + assert File(DATA_JUNK2.child_folder('junk').child(f.name)).exists + +@with_setup(setup_data, cleanup_data) +def test_rename_folder(): + DATA_JUNK = DATA_ROOT.child_folder('junk') + assert not DATA_JUNK.exists + JINJA2.copy_contents_to(DATA_JUNK) + for f in [HELPERS, INDEX, LAYOUT]: + assert File(DATA_JUNK.child(f.name)).exists + DATA_JUNK2 = DATA_ROOT.child_folder('junk2') + assert DATA_JUNK.exists + assert not DATA_JUNK2.exists + DATA_JUNK.rename_to('junk2') + assert not DATA_JUNK.exists + assert DATA_JUNK2.exists + for f in [HELPERS, INDEX, LAYOUT]: + assert File(DATA_JUNK2.child(f.name)).exists + @with_setup(setup_data, cleanup_data) def test_read_all(): utxt = u'åßcdeƒ' diff --git a/hyde/tests/test_model.py b/hyde/tests/test_model.py new file mode 100644 index 0000000..1a4eb18 --- /dev/null +++ b/hyde/tests/test_model.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +""" +Use nose +`$ pip install nose` +`$ nosetests` +""" +from hyde.model import Config, Expando +from hyde.fs import * + +def test_expando_one_level(): + d = {"a": 123, "b": "abc"} + x = Expando(d) + assert x.a == d['a'] + assert x.b == d['b'] + +def test_expando_two_levels(): + d = {"a": 123, "b": {"c": 456}} + x = Expando(d) + assert x.a == d['a'] + assert x.b.c == d['b']['c'] + +def test_expando_three_levels(): + d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} + x = Expando(d) + assert x.a == d['a'] + assert x.b.c == d['b']['c'] + assert x.b.d.e == d['b']['d']['e'] + +TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') +import yaml +class TestConfig(object): + + @classmethod + def setup_class(cls): + cls.conf1 = """ + mode: development + content_root: stuff # Relative path from site root + media_root: media # Relative path from site root + media_url: /media + widgets: + plugins: + aggregators: + """ + + cls.conf2 = """ + mode: development + content_root: site/stuff # Relative path from site root + media_root: mmm # Relative path from site root + media_url: /media + widgets: + plugins: + aggregators: + """ + + def test_default_configuration(self): + c = Config(site_path=TEST_SITE_ROOT) + for root in ['content', 'layout', 'media']: + name = root + '_root' + path = name + '_path' + assert hasattr(c, name) + assert getattr(c, name) == root + assert hasattr(c, path) + assert getattr(c, path) == TEST_SITE_ROOT.child_folder(root) + + def test_conf1(self): + c = Config(site_path=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)) + 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 8ee1949..07e24c0 100644 --- a/hyde/tests/test_site.py +++ b/hyde/tests/test_site.py @@ -4,9 +4,13 @@ Use nose `$ pip install nose` `$ nosetests` """ +import yaml + from hyde.fs import File, Folder +from hyde.model import Config, Expando from hyde.site import Node, RootNode, Site +from nose.tools import raises, with_setup, nottest TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') @@ -60,3 +64,32 @@ def test_build(): assert resource assert resource.relative_path == path assert not s.content.resource_from_relative_path('/happy-festivus.html') + +class TestSiteWithConfig(object): + + @classmethod + def setup_class(cls): + cls.SITE_PATH = File(__file__).parent.child_folder('sites/test_jinja_with_config') + cls.SITE_PATH.make() + 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.SITE_PATH.child_folder('content').rename_to(cls.config.content_root) + + @classmethod + def teardown_class(cls): + cls.SITE_PATH.delete() + + def test_build_with_config(self): + s = Site(self.SITE_PATH, config = self.config) + s.build() + path = 'blog/2010/december' + node = s.content.node_from_relative_path(path) + assert node + assert Folder(node.relative_path) == Folder(path) + path += '/merry-christmas.html' + resource = s.content.resource_from_relative_path(path) + assert resource + assert resource.relative_path == path + assert not s.content.resource_from_relative_path('/happy-festivus.html') \ No newline at end of file