From d4faab842ebaaa1a5f23fc84aacca91b280637d5 Mon Sep 17 00:00:00 2001 From: Jordi Llonch Date: Sun, 31 May 2015 08:12:29 +1000 Subject: [PATCH 1/3] [WIP] hyde serve: livereload support --- hyde/engine.py | 6 +- hyde/server.py | 243 +++++++++++++++++++++++++---------------------- requirements.txt | 1 + setup.py | 3 +- 4 files changed, 139 insertions(+), 114 deletions(-) diff --git a/hyde/engine.py b/hyde/engine.py index 2dc0cdc6..6f348830 100644 --- a/hyde/engine.py +++ b/hyde/engine.py @@ -110,6 +110,8 @@ def gen(self, args): help='The configuration used to generate the site') @store('-d', '--deploy-path', dest='deploy', default=None, help='Where should the site be generated?') + @true('-o', '--open-browser', dest='open_url', default=False, + help='Open the site in your default browser') def serve(self, args): """ The serve command. Serves the site at the given @@ -119,8 +121,8 @@ def serve(self, args): sitepath = self.main(args) site = self.make_site(sitepath, args.config, args.deploy) from hyde.server import HydeWebServer - server = HydeWebServer(site, args.address, args.port) - self.logger.info("Starting webserver at [%s]:[%d]", args.address, args.port) + server = HydeWebServer(site, args.address, args.port, + open_url=args.open_url) try: server.serve_forever() except (KeyboardInterrupt, SystemExit): diff --git a/hyde/server.py b/hyde/server.py index 0223b3b1..5496e5ef 100644 --- a/hyde/server.py +++ b/hyde/server.py @@ -8,8 +8,8 @@ import urllib import traceback from datetime import datetime -from SimpleHTTPServer import SimpleHTTPRequestHandler -from BaseHTTPServer import HTTPServer + +import livereload from hyde.generator import Generator @@ -18,122 +18,143 @@ from commando.util import getLoggerWithNullHandler logger = getLoggerWithNullHandler('hyde.server') -class HydeRequestHandler(SimpleHTTPRequestHandler): - """ - Serves files by regenerating the resource (or) - everything when a request is issued. - """ - - def do_GET(self): - """ - Identify the requested path. If the query string - contains `refresh`, regenerat the entire site. - Otherwise, regenerate only the requested resource - and serve. - """ - self.server.request_time = datetime.now() - logger.debug("Processing request: [%s]" % self.path) - result = urlparse.urlparse(self.path) - query = urlparse.parse_qs(result.query) - if 'refresh' in query or result.query=='refresh': - self.server.regenerate() - if 'refresh' in query: - del query['refresh'] - parts = list(tuple(result)) - parts[4] = urllib.urlencode(query) - parts = tuple(parts) - new_url = urlparse.urlunparse(parts) - logger.info('Redirecting... [%s]' % new_url) - self.redirect(new_url) - else: - SimpleHTTPRequestHandler.do_GET(self) - - - 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(urllib.unquote(self.path).decode('utf-8')) - logger.debug("Trying to load file based on request: [%s]" % result.path) - path = result.path.lstrip('/') - res = None - if path.strip() == "" or File(path).kind.strip() == "": - deployed = site.config.deploy_root_path.child(path) - deployed = Folder.file_or_folder(deployed) - if isinstance(deployed, Folder): - node = site.content.node_from_relative_path(path) - res = node.get_resource('index.html') - elif hasattr(site.config, 'urlcleaner') and hasattr(site.config.urlcleaner, 'strip_extensions'): - for ext in site.config.urlcleaner.strip_extensions: - res = site.content.resource_from_relative_deploy_path(path + '.' + ext) - if res: - break - for ext in site.config.urlcleaner.strip_extensions: - new_path = site.config.deploy_root_path.child(path + '.' + ext) - if File(new_path).exists: - return new_path - else: - res = site.content.resource_from_relative_deploy_path(path) - - if not res: - logger.error("Cannot load file: [%s]" % path) - return site.config.deploy_root_path.child(path) - else: - self.server.generate_resource(res) - new_path = site.config.deploy_root_path.child( - res.relative_deploy_path) - return new_path - - def do_404(self): - """ - Sends a 'not found' response. - """ - site = self.server.site - if self.path != site.config.not_found: - self.redirect(site.config.not_found) - else: - res = site.content.resource_from_relative_deploy_path( - site.config.not_found) - - message = "Requested resource not found" - if not res: - logger.error( - "Cannot find the 404 template [%s]." - % site.config.not_found) - else: - f404 = File(self.translate_path(site.config.not_found)) - if f404.exists: - message = f404.read_all() - self.send_response(200, message) - - 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): +# class HydeRequestHandler(SimpleHTTPRequestHandler): +# """ +# Serves files by regenerating the resource (or) +# everything when a request is issued. +# """ +# +# def do_GET(self): +# """ +# Identify the requested path. If the query string +# contains `refresh`, regenerat the entire site. +# Otherwise, regenerate only the requested resource +# and serve. +# """ +# self.server.request_time = datetime.now() +# logger.debug("Processing request: [%s]" % self.path) +# result = urlparse.urlparse(self.path) +# query = urlparse.parse_qs(result.query) +# if 'refresh' in query or result.query=='refresh': +# self.server.regenerate() +# if 'refresh' in query: +# del query['refresh'] +# parts = list(tuple(result)) +# parts[4] = urllib.urlencode(query) +# parts = tuple(parts) +# new_url = urlparse.urlunparse(parts) +# logger.info('Redirecting... [%s]' % new_url) +# self.redirect(new_url) +# else: +# SimpleHTTPRequestHandler.do_GET(self) +# +# +# 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(urllib.unquote(self.path).decode('utf-8')) +# logger.debug("Trying to load file based on request: [%s]" % result.path) +# path = result.path.lstrip('/') +# res = None +# if path.strip() == "" or File(path).kind.strip() == "": +# deployed = site.config.deploy_root_path.child(path) +# deployed = Folder.file_or_folder(deployed) +# if isinstance(deployed, Folder): +# node = site.content.node_from_relative_path(path) +# res = node.get_resource('index.html') +# elif hasattr(site.config, 'urlcleaner') and hasattr(site.config.urlcleaner, 'strip_extensions'): +# for ext in site.config.urlcleaner.strip_extensions: +# res = site.content.resource_from_relative_deploy_path(path + '.' + ext) +# if res: +# break +# for ext in site.config.urlcleaner.strip_extensions: +# new_path = site.config.deploy_root_path.child(path + '.' + ext) +# if File(new_path).exists: +# return new_path +# else: +# res = site.content.resource_from_relative_deploy_path(path) +# +# if not res: +# logger.error("Cannot load file: [%s]" % path) +# return site.config.deploy_root_path.child(path) +# else: +# self.server.generate_resource(res) +# new_path = site.config.deploy_root_path.child( +# res.relative_deploy_path) +# return new_path +# +# def do_404(self): +# """ +# Sends a 'not found' response. +# """ +# site = self.server.site +# if self.path != site.config.not_found: +# self.redirect(site.config.not_found) +# else: +# res = site.content.resource_from_relative_deploy_path( +# site.config.not_found) +# +# message = "Requested resource not found" +# if not res: +# logger.error( +# "Cannot find the 404 template [%s]." +# % site.config.not_found) +# else: +# f404 = File(self.translate_path(site.config.not_found)) +# if f404.exists: +# message = f404.read_all() +# self.send_response(200, message) +# +# 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(livereload.Server): """ The hyde web server that regenerates the resource, node or site when a request is issued. """ - def __init__(self, site, address, port): + def __init__(self, site, address, port, open_url=False): self.site = site + self.address = address + self.port = port + + self.site.load() + self.generator = Generator(self.site) + self.map_extensions() + + self.site = site + self.address = address + self.port = port + self.open_url = open_url + self.site.load() self.generator = Generator(self.site) - self.request_time = datetime.strptime('1-1-1999', '%m-%d-%Y') - self.regeneration_time = datetime.strptime('1-1-1998', '%m-%d-%Y') - self.__is_shut_down = threading.Event() - self.__shutdown_request = False self.map_extensions() - HTTPServer.__init__(self, (address, port), - HydeRequestHandler) + + livereload.Server.__init__(self) + + + def serve_forever(self): + """ + Regenerates, adds watchers and starts serving the site + """ + self.regenerate() + + self.watch(str(self.site.config.content_root_path), self.regenerate) + + self.serve(host=self.address, port=self.port, + root=str(self.site.config.deploy_root_path), + open_url_delay=self.open_url) def map_extensions(self): """ @@ -144,9 +165,9 @@ def map_extensions(self): except AttributeError: extensions = {} - for extension, type in extensions.iteritems(): + for extension, _type in extensions.iteritems(): ext = "." + extension if not extension == 'default' else '' - HydeRequestHandler.extensions_map[ext] = type + HydeRequestHandler.extensions_map[ext] = _type def regenerate(self): """ diff --git a/requirements.txt b/requirements.txt index 99248ffe..c8f02c13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ Pygments==2.0.2 typogrify==2.0.7 smartypants==1.8.6 Jinja2==2.7.3 +livereload==2.4.0 \ No newline at end of file diff --git a/setup.py b/setup.py index b08329c6..50bce72a 100644 --- a/setup.py +++ b/setup.py @@ -124,7 +124,8 @@ def find_package_data( 'Pygments==2.0.2', 'typogrify==2.0.7', 'smartypants==1.8.6', - 'Jinja2==2.7.3' + 'Jinja2==2.7.3', + 'livereload=2.4.0' ), tests_require=( 'nose==1.3.6', From d478da521b8e039536c7d296ecb9ab74356813ea Mon Sep 17 00:00:00 2001 From: Jordi Llonch Date: Sun, 31 May 2015 08:27:55 +1000 Subject: [PATCH 2/3] updated documentation --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 67066d65..5f1db863 100644 --- a/README.rst +++ b/README.rst @@ -53,11 +53,12 @@ Generating the hyde site Serving the website ------------------- +Content changes will be updated automatically in your browser with the `Livereload extension`_ installed. + :: cd ~/test_site - hyde serve - open http://localhost:8080 + hyde serve -o Publishing the website ---------------------- @@ -168,3 +169,4 @@ Links .. _auzigog: https://github.com/auzigog .. _bootstrap framework: http://twitter.github.com/bootstrap/ .. _Hyde Powered Websites: https://github.com/hyde/hyde/wiki/Hyde-Powered +.. _Livereload extension: http://livereload.com/extensions/ \ No newline at end of file From 7ea43a789b60a5a8c30964a4d8814d0b429d14d0 Mon Sep 17 00:00:00 2001 From: Jordi Llonch Date: Mon, 1 Jun 2015 06:55:04 +1000 Subject: [PATCH 3/3] bug fixes & code improvements --- hyde/server.py | 5 +++-- setup.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hyde/server.py b/hyde/server.py index 5496e5ef..3c38ea52 100644 --- a/hyde/server.py +++ b/hyde/server.py @@ -150,10 +150,11 @@ def serve_forever(self): """ self.regenerate() - self.watch(str(self.site.config.content_root_path), self.regenerate) + self.watch(self.site.config.content_root_path.path, self.regenerate) + self.watch(self.site.config.layout_root_path.path, self.regenerate) self.serve(host=self.address, port=self.port, - root=str(self.site.config.deploy_root_path), + root=self.site.config.deploy_root_path.path, open_url_delay=self.open_url) def map_extensions(self): diff --git a/setup.py b/setup.py index 50bce72a..00bbf47a 100644 --- a/setup.py +++ b/setup.py @@ -125,7 +125,7 @@ def find_package_data( 'typogrify==2.0.7', 'smartypants==1.8.6', 'Jinja2==2.7.3', - 'livereload=2.4.0' + 'livereload==2.4.0' ), tests_require=( 'nose==1.3.6',