Skip to content
Permalink
 
 
Cannot retrieve contributors at this time
182 lines (155 sloc) 7.23 KB
"""Routes core extension."""
import os
from werkzeug import wrappers
from grow import extensions
from grow.collections import collection
from grow.common import timer
from grow.extensions import hooks
from grow.performance import docs_loader
from grow.pods import ui
from grow.routing import router as grow_router
from grow.server import handlers
class RoutesDevHandlerHook(hooks.DevHandlerHook):
"""Handle the dev handler hook."""
@staticmethod
def _create_response(pod, routes, title, concrete=True):
env = ui.create_jinja_env()
template = env.get_template('views/base.html')
kwargs = {
'pod': pod,
'partials': [{
'partial': 'routes',
'concrete': concrete,
'routes': routes,
}],
'title': title,
}
content = template.render(kwargs)
response = wrappers.Response(content)
response.headers['Content-Type'] = 'text/html'
return response
@staticmethod
def serve_routes_abstract(pod, _request, _matched, **_kwargs):
"""Handle the request for abstract routes."""
routes = pod.router.routes
return RoutesDevHandlerHook._create_response(
pod, routes, 'Pod Abstract Routes')
@staticmethod
def serve_routes_concrete(pod, _request, _matched, **_kwargs):
"""Handle the request for routes."""
router = grow_router.Router(pod)
router.use_simple()
router.add_all(concrete=True, use_cache=False)
routes = router.routes
return RoutesDevHandlerHook._create_response(
pod, routes, 'Pod Routes')
# pylint: disable=arguments-differ
def trigger(self, previous_result, routes, *_args, **_kwargs):
"""Execute dev handler modification."""
routes.add('/_grow/ui/tools/:tool', grow_router.RouteInfo(
'console', meta={
'handler': handlers.serve_ui_tool,
}))
routes.add('/_grow', grow_router.RouteInfo('console', meta={
'handler': handlers.serve_console,
}))
routes.add('/_grow/routes', grow_router.RouteInfo(
'console', meta={
'handler': RoutesDevHandlerHook.serve_routes_concrete,
}))
routes.add('/_grow/routes/concrete', grow_router.RouteInfo(
'console', meta={
'handler': RoutesDevHandlerHook.serve_routes_concrete,
}))
routes.add('/_grow/routes/abstract', grow_router.RouteInfo(
'console', meta={
'handler': RoutesDevHandlerHook.serve_routes_abstract,
}))
class RoutesDevFileChangeHook(hooks.DevFileChangeHook):
"""Handle the dev file change hook."""
@staticmethod
def _reset_routes(pod):
with timer.Timer() as router_time:
pod.router.routes.reset()
pod.router.add_all(concrete=False, use_cache=False)
# Trigger the dev handler hook.
pod.extensions_controller.trigger(
'dev_handler', pod.router.routes)
pod.logger.info('{} routes rebuilt in {:.3f} s'.format(
len(pod.router.routes), router_time.secs))
def trigger(self, previous_result, pod_path, *_args, **_kwargs):
"""Trigger the file change hook."""
pod = self.pod
basename = os.path.basename(pod_path)
ignore_doc = basename.startswith(collection.Collection.IGNORE_INITIAL)
if pod_path == '/{}'.format(pod.FILE_PODSPEC):
self._reset_routes(pod)
elif (pod_path.endswith(collection.Collection.BLUEPRINT_PATH)
and pod_path.startswith(collection.Collection.CONTENT_PATH)):
self._reset_routes(pod)
elif pod_path.startswith(collection.Collection.CONTENT_PATH) and not ignore_doc:
trigger_doc = pod.get_doc(pod_path)
col = trigger_doc.collection
base_docs = []
original_docs = []
trigger_docs = col.list_servable_document_locales(pod_path)
for dep_path in pod.podcache.dependency_graph.get_dependents(pod_path):
base_docs.append(pod.get_doc(dep_path))
original_docs += col.list_servable_document_locales(dep_path)
# Normally this would be part of the podcache extension.
# Needs to be between retrieving the original docs and flushing
# the cache to be able to compare paths.
for doc in base_docs:
pod.podcache.document_cache.remove(doc)
pod.podcache.collection_cache.remove_document_locales(doc)
# Force load the docs and fix locales.
docs_loader.DocsLoader.load(pod, base_docs, ignore_errors=True)
docs_loader.DocsLoader.fix_default_locale(
pod, base_docs, ignore_errors=True)
# The routing map should remain unchanged most of the time.
added_docs = []
removed_docs = []
for original_doc in original_docs:
# Removed documents should be removed.
if not original_doc.exists:
removed_docs.append(original_doc)
continue
updated_doc = pod.get_doc(
original_doc.pod_path, original_doc._locale_kwarg)
# When the serving path has changed, updated in routes.
if (updated_doc.has_serving_path()
and original_doc.get_serving_path() != updated_doc.get_serving_path()):
added_docs.append(updated_doc)
removed_docs.append(original_doc)
# If the locales change then we need to adjust the routes.
original_locales = set([str(l) for l in original_doc.locales])
updated_locales = set([str(l) for l in updated_doc.locales])
new_locales = updated_locales - original_locales
for locale in new_locales:
new_doc = pod.get_doc(original_doc.pod_path, locale)
if new_doc.has_serving_path() and new_doc not in added_docs:
added_docs.append(new_doc)
removed_locales = original_locales - updated_locales
for locale in removed_locales:
removed_doc = pod.get_doc(original_doc.pod_path, locale)
if removed_doc.has_serving_path():
if removed_doc not in removed_docs:
removed_docs.append(removed_doc)
# Check for new docs.
for trigger_doc in trigger_docs:
if trigger_doc.has_serving_path():
if not pod.router.routes.match(trigger_doc.get_serving_path()):
added_docs.append(trigger_doc)
if added_docs or removed_docs:
pod.router.reconcile_documents(
remove_docs=removed_docs, add_docs=added_docs)
if previous_result:
return previous_result
return None
# pylint: disable=abstract-method
class RoutesExtension(extensions.BaseExtension):
"""Extension for handling core routes functionality."""
@property
def available_hooks(self):
"""Returns the available hook classes."""
return [RoutesDevHandlerHook, RoutesDevFileChangeHook]