@@ -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 |