From 0c8391c38f7c1b2eeca6d7df93945ebc9ff69980 Mon Sep 17 00:00:00 2001 From: Marc Rijken Date: Tue, 16 Dec 2014 12:18:34 +0100 Subject: [PATCH 1/3] first attempt of bottom inclusions --- bowerstatic/core.py | 20 ++++++++++++----- bowerstatic/includer.py | 16 ++++++++++++++ bowerstatic/injector.py | 4 +++- bowerstatic/tests/test_injector.py | 35 ++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/bowerstatic/core.py b/bowerstatic/core.py index 520da94..4732730 100644 --- a/bowerstatic/core.py +++ b/bowerstatic/core.py @@ -18,18 +18,19 @@ def __init__(self, publisher_signature='bowerstatic', autoversion=None): self._renderer = Renderer() self.autoversion = autoversion or filesystem_second_autoversion - def components(self, name, path): + def components(self, name, path, bottom=False): if name in self._component_collections: raise Error("Duplicate name for components directory: %s" % name) - result = ComponentCollection(self, name, path=path) + result = ComponentCollection(self, name, path=path, bottom=bottom) self._component_collections[name] = result return result - def local_components(self, name, component_collection): + def local_components(self, name, component_collection, bottom=False): if name in self._component_collections: raise Error("Duplicate name for local components: %s" % name) result = ComponentCollection(self, name, - fallback_collection=component_collection) + fallback_collection=component_collection, + bottom=bottom) self._component_collections[name] = result return result @@ -63,12 +64,13 @@ def get_filename(self, bower_components_name, class ComponentCollection(object): - def __init__(self, bower, name, path=None, fallback_collection=None): + def __init__(self, bower, name, path=None, fallback_collection=None, bottom=False): self.bower = bower self.name = name self._resources = {} self.path = path self.fallback_collection = fallback_collection + self.bottom = bottom if path is not None: self._components = self.load_components(path) else: @@ -267,6 +269,10 @@ def url(self): def renderer(self, resource): return self.bower.renderer(resource) + @property + def bottom(self): + return self.component_collection.bottom + class Resource(object): def __init__(self, component, file_path, dependencies): @@ -290,3 +296,7 @@ def content(self): def renderer(self): return self.component.renderer(self) + + @property + def bottom(self): + return self.file_path.endswith('.js') and self.component.bottom diff --git a/bowerstatic/includer.py b/bowerstatic/includer.py index 57f82cb..55da7cf 100644 --- a/bowerstatic/includer.py +++ b/bowerstatic/includer.py @@ -37,6 +37,18 @@ def render(self): snippets = [inclusion.html() for inclusion in inclusions] return '\n'.join(snippets) + def render_top(self): + inclusions = topological_sort( + self._inclusions, lambda inclusion: inclusion.dependencies()) + snippets = [inclusion.html() for inclusion in inclusions if not inclusion.bottom] + return '\n'.join(snippets) + + def render_bottom(self): + inclusions = topological_sort( + self._inclusions, lambda inclusion: inclusion.dependencies()) + snippets = [inclusion.html() for inclusion in inclusions if inclusion.bottom] + return '\n'.join(snippets) + class Inclusion(object): def dependencies(self): @@ -71,3 +83,7 @@ def dependencies(self): def html(self): return self.renderer(self.resource) + + @property + def bottom(self): + return self.resource.bottom diff --git a/bowerstatic/injector.py b/bowerstatic/injector.py index 61d029b..005acf9 100644 --- a/bowerstatic/injector.py +++ b/bowerstatic/injector.py @@ -22,7 +22,9 @@ def __call__(self, request): body = response.body response.body = b'' body = body.replace( - b'', b''.join((inclusions.render().encode(), b''))) + b'', b''.join((inclusions.render_top().encode(), b''))) + body = body.replace( + b'', b''.join((inclusions.render_bottom().encode(), b''))) response.write(body) return response diff --git a/bowerstatic/tests/test_injector.py b/bowerstatic/tests/test_injector.py index 6415f64..a3065c2 100644 --- a/bowerstatic/tests/test_injector.py +++ b/bowerstatic/tests/test_injector.py @@ -542,6 +542,41 @@ def wsgi(environ, start_response): b'Hello!') +def test_injector_bottom(): + bower = bowerstatic.Bower() + + components = bower.components('components', os.path.join( + os.path.dirname(__file__), 'bower_components'), bottom=True) + + def wsgi(environ, start_response): + start_response('200 OK', [('Content-Type', 'text/html;charset=UTF-8')]) + include = components.includer(environ) + # going to pull in jquery-ui and jquery twice + include('jquery-ui') + include('jquery-ui-bootstrap') + return [b'Hello!'] + + injector = bower.injector(wsgi) + + c = Client(injector) + + response = c.get('/') + + assert response.body == ( + b'' + b'' + b'Hello!' + b'\n' + b'' + b'' + ) + def test_injector_no_head_to_inject(): bower = bowerstatic.Bower() From 90e56aae70dbcc74a12109a124cab0d7edf63e7d Mon Sep 17 00:00:00 2001 From: Marc Rijken Date: Tue, 16 Dec 2014 12:26:43 +0100 Subject: [PATCH 2/3] add documentation --- doc/integrating.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/integrating.rst b/doc/integrating.rst index b3cb93e..538a316 100644 --- a/doc/integrating.rst +++ b/doc/integrating.rst @@ -211,6 +211,21 @@ or:: include('static/favicon.ico', lambda resource: '') + +Including at the bottom +----------------------- + +For javascripts, it will sometimes be better to include the script at the bottom. To include all `*.js` files +of a component at the bottom, you have to add `bottom=True` when loading components:: + + components = bower.components('components', '/path/to/bower_components', + bottom=True) + +Local components do have the same parameter:: + + local = bower.local_components('local', components, bottom=True) + + URL structure ------------- From 916a2d6d80ef47cf79f6dbd67536ed11b8cdb71d Mon Sep 17 00:00:00 2001 From: Marc Rijken Date: Thu, 18 Dec 2014 00:31:34 +0100 Subject: [PATCH 3/3] remove duplicated code --- bowerstatic/includer.py | 19 +++++-------------- bowerstatic/injector.py | 4 ++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/bowerstatic/includer.py b/bowerstatic/includer.py index 55da7cf..a807ff4 100644 --- a/bowerstatic/includer.py +++ b/bowerstatic/includer.py @@ -31,22 +31,13 @@ def __init__(self): def add(self, inclusion): self._inclusions.append(inclusion) - def render(self): + def render(self, bottom=False): inclusions = topological_sort( self._inclusions, lambda inclusion: inclusion.dependencies()) - snippets = [inclusion.html() for inclusion in inclusions] - return '\n'.join(snippets) - - def render_top(self): - inclusions = topological_sort( - self._inclusions, lambda inclusion: inclusion.dependencies()) - snippets = [inclusion.html() for inclusion in inclusions if not inclusion.bottom] - return '\n'.join(snippets) - - def render_bottom(self): - inclusions = topological_sort( - self._inclusions, lambda inclusion: inclusion.dependencies()) - snippets = [inclusion.html() for inclusion in inclusions if inclusion.bottom] + snippets = [inclusion.html() for inclusion in inclusions + if (not bottom and not inclusion.bottom) + or (bottom and inclusion.bottom) + ] return '\n'.join(snippets) diff --git a/bowerstatic/injector.py b/bowerstatic/injector.py index 005acf9..22d4b01 100644 --- a/bowerstatic/injector.py +++ b/bowerstatic/injector.py @@ -22,9 +22,9 @@ def __call__(self, request): body = response.body response.body = b'' body = body.replace( - b'', b''.join((inclusions.render_top().encode(), b''))) + b'', b''.join((inclusions.render(bottom=False).encode(), b''))) body = body.replace( - b'', b''.join((inclusions.render_bottom().encode(), b''))) + b'', b''.join((inclusions.render(bottom=True).encode(), b''))) response.write(body) return response