| @@ -1,3 +1,10 @@ | |||||
| Version 0.8.3c11 | |||||
| ================= | |||||
| * `hyde serve` now picks up changes in config data automatically. | |||||
| (Issue #24) | |||||
| Version 0.8.3c10 | Version 0.8.3c10 | ||||
| =============== | =============== | ||||
| @@ -27,15 +27,19 @@ class Generator(object): | |||||
| self.site = site | self.site = site | ||||
| self.generated_once = False | self.generated_once = False | ||||
| self.deps = Dependents(site.sitepath) | self.deps = Dependents(site.sitepath) | ||||
| self.create_context() | |||||
| self.template = None | |||||
| Plugin.load_all(site) | |||||
| self.events = Plugin.get_proxy(self.site) | |||||
| def create_context(self): | |||||
| site = self.site | |||||
| self.__context__ = dict(site=site) | self.__context__ = dict(site=site) | ||||
| if hasattr(site.config, 'context'): | if hasattr(site.config, 'context'): | ||||
| site.context = Context.load(site.sitepath, site.config.context) | site.context = Context.load(site.sitepath, site.config.context) | ||||
| self.__context__.update(site.context) | self.__context__.update(site.context) | ||||
| self.template = None | |||||
| Plugin.load_all(site) | |||||
| self.events = Plugin.get_proxy(self.site) | |||||
| @contextmanager | @contextmanager | ||||
| def context_for_resource(self, resource): | def context_for_resource(self, resource): | ||||
| @@ -43,8 +47,6 @@ class Generator(object): | |||||
| Context manager that intializes the context for a given | Context manager that intializes the context for a given | ||||
| resource and rolls it back after the resource is processed. | resource and rolls it back after the resource is processed. | ||||
| """ | """ | ||||
| # TODO: update metadata and other resource | |||||
| # specific properties here. | |||||
| self.__context__.update( | self.__context__.update( | ||||
| resource=resource, | resource=resource, | ||||
| node=resource.node, | node=resource.node, | ||||
| @@ -103,10 +105,7 @@ class Generator(object): | |||||
| Checks if the site requries a reload and loads if | Checks if the site requries a reload and loads if | ||||
| necessary. | necessary. | ||||
| """ | """ | ||||
| #TODO: Perhaps this is better suited in Site | |||||
| if not len(self.site.content.child_nodes): | |||||
| logger.info("Reading site contents") | |||||
| self.site.load() | |||||
| self.site.reload_if_needed() | |||||
| def finalize(self): | def finalize(self): | ||||
| """ | """ | ||||
| @@ -154,8 +153,9 @@ class Generator(object): | |||||
| last generation. | last generation. | ||||
| """ | """ | ||||
| logger.debug("Checking for changes in %s" % resource) | logger.debug("Checking for changes in %s" % resource) | ||||
| self.load_site_if_needed() | |||||
| self.load_template_if_needed() | self.load_template_if_needed() | ||||
| self.load_site_if_needed() | |||||
| target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | resource.relative_deploy_path)) | ||||
| if not target.exists or target.older_than(resource.source_file): | if not target.exists or target.older_than(resource.source_file): | ||||
| @@ -164,6 +164,11 @@ class Generator(object): | |||||
| if resource.source_file.is_binary: | if resource.source_file.is_binary: | ||||
| logger.debug("No Changes found in %s" % resource) | logger.debug("No Changes found in %s" % resource) | ||||
| return False | return False | ||||
| if self.site.config.needs_refresh() or \ | |||||
| not target.has_changed_since(self.site.config.last_modified): | |||||
| logger.debug("Site configuration changed") | |||||
| return True | |||||
| deps = self.get_dependencies(resource) | deps = self.get_dependencies(resource) | ||||
| if not deps or None in deps: | if not deps or None in deps: | ||||
| logger.debug("No changes found in %s" % resource) | logger.debug("No changes found in %s" % resource) | ||||
| @@ -282,8 +287,14 @@ class Generator(object): | |||||
| except HydeException: | except HydeException: | ||||
| self.generate_all() | self.generate_all() | ||||
| def refresh_config(self): | |||||
| if self.site.config.needs_refresh(): | |||||
| logger.debug("Refreshing configuration and context") | |||||
| self.site.refresh_config() | |||||
| self.create_context() | |||||
| def __generate_node__(self, node, incremental=False): | def __generate_node__(self, node, incremental=False): | ||||
| self.refresh_config() | |||||
| for node in node.walk(): | for node in node.walk(): | ||||
| logger.debug("Generating Node [%s]", node) | logger.debug("Generating Node [%s]", node) | ||||
| self.events.begin_node(node) | self.events.begin_node(node) | ||||
| @@ -293,6 +304,7 @@ class Generator(object): | |||||
| def __generate_resource__(self, resource, incremental=False): | def __generate_resource__(self, resource, incremental=False): | ||||
| self.refresh_config() | |||||
| if not resource.is_processable: | if not resource.is_processable: | ||||
| logger.debug("Skipping [%s]", resource) | logger.debug("Skipping [%s]", resource) | ||||
| return | return | ||||
| @@ -6,8 +6,9 @@ from hyde.fs import File, Folder | |||||
| import codecs | import codecs | ||||
| import yaml | import yaml | ||||
| from datetime import datetime | |||||
| from UserDict import IterableUserDict | from UserDict import IterableUserDict | ||||
| from hyde.util import getLoggerWithNullHandler | from hyde.util import getLoggerWithNullHandler | ||||
| logger = getLoggerWithNullHandler('hyde.engine') | logger = getLoggerWithNullHandler('hyde.engine') | ||||
| @@ -135,13 +136,27 @@ class Config(Expando): | |||||
| plugins = [], | plugins = [], | ||||
| ignore = [ "*~", "*.bak" ] | ignore = [ "*~", "*.bak" ] | ||||
| ) | ) | ||||
| conf = dict(**default_config) | |||||
| self.config_file = config_file | |||||
| self.config_dict = config_dict | |||||
| self.load_time = datetime.min | |||||
| self.config_files = [] | |||||
| self.sitepath = Folder(sitepath) | self.sitepath = Folder(sitepath) | ||||
| conf = dict(**default_config) | |||||
| conf.update(self.read_config(config_file)) | conf.update(self.read_config(config_file)) | ||||
| if config_dict: | if config_dict: | ||||
| conf.update(config_dict) | conf.update(config_dict) | ||||
| super(Config, self).__init__(conf) | super(Config, self).__init__(conf) | ||||
| @property | |||||
| def last_modified(self): | |||||
| return max((conf.last_modified for conf in self.config_files)) | |||||
| def needs_refresh(self): | |||||
| if not self.config_files: | |||||
| return True | |||||
| return any((conf.has_changed_since(self.load_time) | |||||
| for conf in self.config_files)) | |||||
| def read_config(self, config_file): | def read_config(self, config_file): | ||||
| """ | """ | ||||
| Reads the configuration file and updates this | Reads the configuration file and updates this | ||||
| @@ -152,6 +167,7 @@ class Config(Expando): | |||||
| config_file else 'site.yaml') | config_file else 'site.yaml') | ||||
| conf = {} | conf = {} | ||||
| if File(conf_file).exists: | if File(conf_file).exists: | ||||
| self.config_files.append(File(conf_file)) | |||||
| logger.info("Reading site configuration from [%s]", conf_file) | logger.info("Reading site configuration from [%s]", conf_file) | ||||
| with codecs.open(conf_file, 'r', 'utf-8') as stream: | with codecs.open(conf_file, 'r', 'utf-8') as stream: | ||||
| conf = yaml.load(stream) | conf = yaml.load(stream) | ||||
| @@ -159,6 +175,7 @@ class Config(Expando): | |||||
| parent = self.read_config(conf['extends']) | parent = self.read_config(conf['extends']) | ||||
| parent.update(conf) | parent.update(conf) | ||||
| conf = parent | conf = parent | ||||
| self.load_time = datetime.now() | |||||
| return conf | return conf | ||||
| @@ -8,6 +8,7 @@ import select | |||||
| import threading | import threading | ||||
| import urlparse | import urlparse | ||||
| import urllib | import urllib | ||||
| from datetime import datetime | |||||
| from SimpleHTTPServer import SimpleHTTPRequestHandler | from SimpleHTTPServer import SimpleHTTPRequestHandler | ||||
| from BaseHTTPServer import HTTPServer | from BaseHTTPServer import HTTPServer | ||||
| from hyde.fs import File, Folder | from hyde.fs import File, Folder | ||||
| @@ -18,8 +19,6 @@ from hyde.exceptions import HydeException | |||||
| from hyde.util import getLoggerWithNullHandler | from hyde.util import getLoggerWithNullHandler | ||||
| logger = getLoggerWithNullHandler('hyde.server') | logger = getLoggerWithNullHandler('hyde.server') | ||||
| from datetime import datetime | |||||
| class HydeRequestHandler(SimpleHTTPRequestHandler): | class HydeRequestHandler(SimpleHTTPRequestHandler): | ||||
| """ | """ | ||||
| Serves files by regenerating the resource (or) | Serves files by regenerating the resource (or) | ||||
| @@ -5,12 +5,13 @@ Parses & holds information about the site to be generated. | |||||
| import os | import os | ||||
| import fnmatch | import fnmatch | ||||
| import urlparse | import urlparse | ||||
| from functools import wraps | |||||
| from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
| from hyde.fs import FS, File, Folder | from hyde.fs import FS, File, Folder | ||||
| from hyde.model import Config | from hyde.model import Config | ||||
| from hyde.util import getLoggerWithNullHandler | from hyde.util import getLoggerWithNullHandler | ||||
| from functools import wraps | |||||
| def path_normalized(f): | def path_normalized(f): | ||||
| @wraps(f) | @wraps(f) | ||||
| @@ -359,7 +360,6 @@ class RootNode(Node): | |||||
| return | return | ||||
| self.add_resource(afile) | self.add_resource(afile) | ||||
| class Site(object): | class Site(object): | ||||
| """ | """ | ||||
| Represents the site to be generated. | Represents the site to be generated. | ||||
| @@ -373,11 +373,29 @@ class Site(object): | |||||
| self.plugins = [] | self.plugins = [] | ||||
| self.context = {} | self.context = {} | ||||
| def refresh_config(self): | |||||
| """ | |||||
| Refreshes config data if one or more config files have | |||||
| changed. Note that this does not refresh the meta data. | |||||
| """ | |||||
| if self.config.needs_refresh(): | |||||
| logger.debug("Refreshing config data") | |||||
| self.config = Config(self.sitepath, | |||||
| self.config.config_file, | |||||
| self.config.config_dict) | |||||
| def reload_if_needed(self): | |||||
| """ | |||||
| Reloads if the site has not been loaded before or if the | |||||
| configuration has changed since the last load. | |||||
| """ | |||||
| if not len(self.content.child_nodes): | |||||
| self.load() | |||||
| def load(self): | def load(self): | ||||
| """ | """ | ||||
| Walks the content and media folders to load up the sitemap. | Walks the content and media folders to load up the sitemap. | ||||
| """ | """ | ||||
| self.content.load() | self.content.load() | ||||
| def content_url(self, path): | def content_url(self, path): | ||||
| @@ -303,7 +303,6 @@ class TestSorterMeta(object): | |||||
| "another-sad-post.html", | "another-sad-post.html", | ||||
| "happy-post.html"] | "happy-post.html"] | ||||
| for r in s.content.walk_resources(): | for r in s.content.walk_resources(): | ||||
| print r.meta.to_dict() | |||||
| expected = r.name in have_index | expected = r.name in have_index | ||||
| assert attributes_checker(r, ['meta.index']) == expected | assert attributes_checker(r, ['meta.index']) == expected | ||||
| @@ -77,9 +77,9 @@ class TestGenerator(object): | |||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | ||||
| gen = Generator(site) | gen = Generator(site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert not gen.has_resource_changed(resource) | |||||
| import time | import time | ||||
| time.sleep(1) | time.sleep(1) | ||||
| assert not gen.has_resource_changed(resource) | |||||
| text = resource.source_file.read_all() | text = resource.source_file.read_all() | ||||
| resource.source_file.write(text) | resource.source_file.write(text) | ||||
| assert gen.has_resource_changed(resource) | assert gen.has_resource_changed(resource) | ||||
| @@ -160,4 +160,4 @@ main: | |||||
| assert "abc = def" in out | assert "abc = def" in out | ||||
| assert "home" in out | assert "home" in out | ||||
| assert "articles" in out | assert "articles" in out | ||||
| assert "projects" in out | |||||
| assert "projects" in out | |||||