| @@ -49,7 +49,7 @@ class Engine(Application): | |||
| The create command. Creates a new site from the template at the given | |||
| sitepath. | |||
| """ | |||
| sitepath = Folder(args.sitepath) | |||
| sitepath = Folder(Folder(args.sitepath).fully_expanded_path) | |||
| if sitepath.exists and not args.overwrite: | |||
| raise HydeException( | |||
| "The given site path[%s] is not empty" % sitepath) | |||
| @@ -75,14 +75,41 @@ class Engine(Application): | |||
| The generate command. Generates the site at the given | |||
| deployment directory. | |||
| """ | |||
| sitepath = Folder(args.sitepath) | |||
| site = self.make_site(args.sitepath, args.config) | |||
| from hyde.generator import Generator | |||
| gen = Generator(site) | |||
| gen.generate_all() | |||
| @subcommand('serve', help='Serve the website') | |||
| @store('-a', '--address', default='localhost', dest='address', | |||
| help='The address where the website must be served from.') | |||
| @store('-p', '--port', type=int, default=8080, dest='port', | |||
| help='The port where the website must be served from.') | |||
| @store('-c', '--config-path', default='site.yaml', dest='config', | |||
| help='The configuration used to generate the site') | |||
| @store('-d', '--deploy-path', default='deploy', | |||
| help='Where should the site be generated?') | |||
| def serve(self, args): | |||
| """ | |||
| The serve command. Serves the site at the given | |||
| deployment directory, address and port. Regenerates | |||
| the entire site or specific files based on ths request. | |||
| """ | |||
| sitepath = Folder(Folder(args.sitepath).fully_expanded_path) | |||
| config_file = sitepath.child(args.config) | |||
| site = self.make_site(args.sitepath, args.config) | |||
| from hyde.server import HydeWebServer | |||
| server = HydeWebServer(site, args.address, args.port) | |||
| server.serve_forever() | |||
| def make_site(self, sitepath, config): | |||
| """ | |||
| Creates a site object from the given sitepath and the config file. | |||
| """ | |||
| sitepath = Folder(Folder(sitepath).fully_expanded_path) | |||
| config_file = sitepath.child(config) | |||
| logger.info("Reading site configuration from [%s]", config_file) | |||
| conf = {} | |||
| with open(config_file) as stream: | |||
| conf = yaml.load(stream) | |||
| site = Site(sitepath, Config(sitepath, conf)) | |||
| from hyde.generator import Generator | |||
| gen = Generator(site) | |||
| gen.generate_all() | |||
| return Site(sitepath, Config(sitepath, conf)) | |||
| @@ -27,9 +27,11 @@ class FS(object): | |||
| """ | |||
| The base file system object | |||
| """ | |||
| def __init__(self, path): | |||
| super(FS, self).__init__() | |||
| self.path = os.path.expanduser(str(path).strip().rstrip(os.sep)) | |||
| self.path = os.path.expandvars(os.path.expanduser( | |||
| str(path).strip().rstrip(os.sep))) | |||
| def __str__(self): | |||
| return self.path | |||
| @@ -43,6 +45,18 @@ class FS(object): | |||
| def __ne__(self, other): | |||
| return str(self) != str(other) | |||
| @property | |||
| def fully_expanded_path(self): | |||
| """ | |||
| Returns the absolutely absolute path. Calls os.( | |||
| normpath, normcase, expandvars and expanduser). | |||
| """ | |||
| return os.path.abspath( | |||
| os.path.normpath( | |||
| os.path.normcase( | |||
| os.path.expandvars( | |||
| os.path.expanduser(self.path))))) | |||
| @property | |||
| def exists(self): | |||
| """ | |||
| @@ -142,6 +156,7 @@ class File(FS): | |||
| """ | |||
| The File object. | |||
| """ | |||
| def __init__(self, path): | |||
| super(File, self).__init__(path) | |||
| @@ -436,6 +451,7 @@ class Folder(FS): | |||
| """ | |||
| Represents a directory. | |||
| """ | |||
| def __init__(self, path): | |||
| super(Folder, self).__init__(path) | |||
| @@ -510,6 +526,7 @@ class Folder(FS): | |||
| """ | |||
| source = self | |||
| with source.walker as walker: | |||
| @walker.folder_visitor | |||
| def visit_folder(folder): | |||
| """ | |||
| @@ -81,7 +81,8 @@ class Generator(object): | |||
| if not self.template: | |||
| logger.info("Generating site at [%s]" % self.site.sitepath) | |||
| self.template = Template.find_template(self.site) | |||
| logger.info("Using [%s] as the template", self.template) | |||
| logger.info("Using [%s] as the template", | |||
| self.template.__class__.__name__) | |||
| logger.info("Configuring the template environment") | |||
| self.template.configure(self.site.config) | |||
| @@ -2,6 +2,8 @@ mode: development | |||
| media_root:: media # Relative path from site root (the directory where this file exists) | |||
| media_url: /media | |||
| template: hyde.ext.jinja2 | |||
| widgets: | |||
| plugins: | |||
| aggregators: | |||
| - hyde.ext.plugins.meta.MetaPlugin | |||
| - hyde.ext.plugins.auto_extend.AutoExtendPlugin | |||
| - hyde.ext.plugins.less.LessCSSPlugin | |||
| - hyde.ext.plugins.blockdown.BlockdownPlugin | |||
| @@ -51,6 +51,7 @@ class Config(Expando): | |||
| layout_root='layout', | |||
| media_url='/media', | |||
| site_url='/', | |||
| not_found='404.html', | |||
| plugins = [] | |||
| ) | |||
| conf = dict(**default_config) | |||
| @@ -0,0 +1,123 @@ | |||
| """ | |||
| Contains classes and utilities for serving a site | |||
| generated from hyde. | |||
| """ | |||
| import os | |||
| import urlparse | |||
| import urllib | |||
| from SimpleHTTPServer import SimpleHTTPRequestHandler | |||
| from BaseHTTPServer import HTTPServer | |||
| from hyde.fs import File, Folder | |||
| from hyde.site import Site | |||
| from hyde.generator import Generator | |||
| import logging | |||
| logger = logging.getLogger('hyde.server') | |||
| import sys | |||
| logger.addHandler(logging.StreamHandler(sys.stdout)) | |||
| class HydeRequestHandler(SimpleHTTPRequestHandler): | |||
| """ | |||
| Serves files by regenerating the resource (or) | |||
| everything when a request is issued. | |||
| """ | |||
| def do_GET(self): | |||
| """ | |||
| Idenitfy the requested path. If the query string | |||
| contains `refresh`, regenerat the entire site. | |||
| Otherwise, regenerate only the requested resource | |||
| and serve. | |||
| """ | |||
| logger.info("Processing request:[%s]" % self.path) | |||
| result = urlparse.urlparse(self.path) | |||
| query = urlparse.parse_qs(result.query) | |||
| if 'refresh' in query: | |||
| self.server.regenerate() | |||
| del query['refresh'] | |||
| parts = tuple(result) | |||
| parts[4] = urllib.urlencode(query) | |||
| new_url = urlparse.urlunparse(parts) | |||
| logger.info('Redirecting...[%s]' % new_url) | |||
| self.redirect(new_url) | |||
| else: | |||
| try: | |||
| SimpleHTTPRequestHandler.do_GET(self) | |||
| except Exception, exception: | |||
| logger.error(exception.message) | |||
| site = self.server.site | |||
| res = site.content.resource_from_relative_path( | |||
| site.config.not_found) | |||
| self.redirect("/" + res.relative_deploy_path) | |||
| def translate_path(self, path): | |||
| """ | |||
| Finds the absolute path of the requested file by | |||
| referring to the `site` variable in the server. | |||
| """ | |||
| site = self.server.site | |||
| result = urlparse.urlparse(self.path) | |||
| logger.info("Trying to load file based on request:[%s]" % result.path) | |||
| path = result.path.lstrip('/') | |||
| res = site.content.resource_from_relative_path(path) | |||
| if not res: | |||
| logger.info("Cannot load file:[%s]" % path) | |||
| raise Exception("Cannot load file: [%s]" % path) | |||
| else: | |||
| self.server.generate_resource(res) | |||
| new_path = site.config.deploy_root_path.child( | |||
| res.relative_deploy_path) | |||
| return new_path | |||
| def redirect(self, path, temporary=True): | |||
| """ | |||
| Sends a redirect header with the new location. | |||
| """ | |||
| self.send_response(302 if temporary else 301) | |||
| self.send_header('Location', path) | |||
| self.end_headers() | |||
| class HydeWebServer(HTTPServer): | |||
| """ | |||
| The hyde web server that regenerates the resource, node or site when | |||
| a request is issued. | |||
| """ | |||
| def __init__(self, site, address, port): | |||
| self.site = site | |||
| self.site.load() | |||
| self.generator = Generator(self.site) | |||
| HTTPServer.__init__(self, (address, port), | |||
| HydeRequestHandler) | |||
| def __reinit__(self): | |||
| self.generator = Generator(self.site) | |||
| self.regenerate() | |||
| def regenerate(self): | |||
| """ | |||
| Regenerates the entire site. | |||
| """ | |||
| try: | |||
| logger.info('Regenerating the entire site') | |||
| self.generator.generate_all() | |||
| except Exception, exception: | |||
| logger.error('Error occured when regenerating the site [%s]' | |||
| % exception.message) | |||
| self.__reinit__() | |||
| def generate_resource(self, resource): | |||
| """ | |||
| Regenerates the entire site. | |||
| """ | |||
| try: | |||
| logger.info('Generating resource [%]' % resource) | |||
| self.generator.generate_resource(resource) | |||
| except Exception, exception: | |||
| logger.error('Error [%s] occured when generating the resource [%s]' | |||
| % (resource, repr(exception))) | |||
| self.__reinit__() | |||
| @@ -311,7 +311,7 @@ class Site(object): | |||
| def __init__(self, sitepath=None, config=None): | |||
| super(Site, self).__init__() | |||
| self.sitepath = Folder(str(sitepath)) | |||
| self.sitepath = Folder(Folder(sitepath).fully_expanded_path) | |||
| self.config = config if config else Config(self.sitepath) | |||
| self.content = RootNode(self.config.content_root_path, self) | |||
| self.plugins = [] | |||
| @@ -60,6 +60,13 @@ def test_path_expands_user(): | |||
| f = File("~/abc/def") | |||
| assert f.path == os.path.expanduser("~/abc/def") | |||
| def test_fully_expanded_path(): | |||
| f = File(__file__).parent | |||
| n = f.child_folder('../' + f.name) | |||
| e = Folder(n.fully_expanded_path) | |||
| assert n != e | |||
| assert f == e | |||
| def test_parent(): | |||
| f = File(__file__) | |||
| p = f.parent | |||
| @@ -77,6 +77,7 @@ class TestConfig(object): | |||
| assert hasattr(c, 'plugins') | |||
| assert len(c.plugins) == 0 | |||
| assert c.deploy_root_path == TEST_SITE_ROOT.child_folder('deploy') | |||
| assert c.not_found == '404.html' | |||
| def test_conf1(self): | |||
| c = Config(sitepath=TEST_SITE_ROOT, config_dict=yaml.load(self.conf1)) | |||