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