You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2013/12/16 23:16:17 UTC

[17/37] git commit: [#6942] Fix App.icon_url(), add ETag caching

[#6942] Fix App.icon_url(), add ETag caching

- Icon resource lookup needed MRO search to work properly with
  "inherited" static resources. Moved that logic out into
  App.has_resource() so it could be used by the EW resource
  registration code in config/resource.py.

- Added ETag caching for the new tool_icon_css so we get some
  browser caching. Unfortunately we can only cache it for the life
  of the server process since we can't tell if new tools have
  been added since the last server start.

- While adding ETag caching I noticed that other app-served files
  (like user and project icons) were not being cached by the browser,
  so I added caching for them too.

Signed-off-by: Tim Van Steenburgh <tv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/5164b74b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/5164b74b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/5164b74b

Branch: refs/heads/db/5424
Commit: 5164b74b100e03b7cbf7ec54a15cc6641c7e017e
Parents: c9064d3
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Fri Dec 13 04:00:03 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Dec 16 17:58:46 2013 +0000

----------------------------------------------------------------------
 Allura/allura/app.py                | 36 ++++++++++++++++++++++++--------
 Allura/allura/config/resources.py   | 23 +++++++-------------
 Allura/allura/controllers/static.py | 21 ++++++++++++-------
 Allura/allura/lib/app_globals.py    |  6 +++++-
 Allura/allura/lib/utils.py          |  3 +++
 5 files changed, 55 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5164b74b/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 957e880..0fa7556 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -356,16 +356,34 @@ class Application(object):
         :attr:`icons`.
 
         """
-        resource = cls.icons.get(size)
+        cached = getattr(cls, '_icon_url_cache', {}).get(size)
+        if cached is not None:
+            return cached
+
+        if not hasattr(cls, '_icon_url_cache'):
+            setattr(cls, '_icon_url_cache', {})
+
+        resource, url = cls.icons.get(size), ''
         if resource:
-            f = getattr(cls, '_icon_url_maker', None)
-            if not f:
-                f = (g.forge_static if pkg_resources.resource_exists(
-                    cls.__module__, os.path.join('nf', resource))
-                    else g.theme_href)
-                setattr(cls, '_icon_url_maker', f)
-            return f(resource)
-        return ''
+            resource_path = os.path.join('nf', resource)
+            url = (g.forge_static(resource) if cls.has_resource(resource_path)
+                    else g.theme_href(resource))
+        cls._icon_url_cache[size] = url
+        return url
+
+    @classmethod
+    def has_resource(cls, resource_path):
+        """Determine whether this Application has the resource pointed to by
+        ``resource_path``.
+
+        If the resource is not found for the immediate class, its parents
+        will be searched. The return value is the class that "owns" the
+        resource, or None if the resource is not found.
+
+        """
+        for klass in [o for o in cls.__mro__ if issubclass(o, Application)]:
+            if pkg_resources.resource_exists(klass.__module__, resource_path):
+                return klass
 
     def has_access(self, user, topic):
         """Return True if ``user`` can send email to ``topic``.

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5164b74b/Allura/allura/config/resources.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/resources.py b/Allura/allura/config/resources.py
index 1f1ce53..b289416 100644
--- a/Allura/allura/config/resources.py
+++ b/Allura/allura/config/resources.py
@@ -20,7 +20,6 @@ import logging
 
 import pkg_resources
 
-from allura.app import Application
 from allura.lib.helpers import iter_entry_points
 
 log = logging.getLogger(__name__)
@@ -34,22 +33,14 @@ def register_ew_resources(manager):
         'allura', pkg_resources.resource_filename('allura', 'public/nf'))
     for ep in iter_entry_points('allura'):
         try:
-            # Allow derived tools to "inherit" static resources from a parent.
-            module_name = ep.module_name
+            app = ep.load()
             resource_path = os.path.join('nf', ep.name.lower())
-            if not pkg_resources.resource_exists(module_name, resource_path):
-                for cls in [c for c in ep.load().__mro__[1:]
-                        if issubclass(c, Application)]:
-                    module_name = cls.__module__
-                    if pkg_resources.resource_exists(module_name, resource_path):
-                        break
-                else:
-                    continue
-
-            manager.register_directory(
-                'tool/%s' % ep.name.lower(),
-                pkg_resources.resource_filename(
-                    module_name, resource_path))
+            resource_cls = app.has_resource(resource_path)
+            if resource_cls:
+                manager.register_directory(
+                    'tool/%s' % ep.name.lower(),
+                    pkg_resources.resource_filename(
+                        resource_cls.__module__, resource_path))
         except ImportError:
             log.warning('Cannot import entry point %s', ep)
             raise

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5164b74b/Allura/allura/controllers/static.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/static.py b/Allura/allura/controllers/static.py
index 87687f0..c072b2f 100644
--- a/Allura/allura/controllers/static.py
+++ b/Allura/allura/controllers/static.py
@@ -15,16 +15,17 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-import os
-import mimetypes
-import pkg_resources
-from tg import expose, redirect, flash, config, validate, request, response
-from tg.decorators import with_trailing_slash, without_trailing_slash
+from cStringIO import StringIO
+
+from tg import expose
+from tg.decorators import without_trailing_slash
 from webob import exc
 
 from pylons import tmpl_context as c, app_globals as g
+from pylons.controllers.util import etag_cache
 from allura.lib import helpers as h
-from allura import model as M
+from allura.lib import utils
+
 
 class NewForgeController(object):
 
@@ -44,5 +45,9 @@ class NewForgeController(object):
 
     @expose()
     def tool_icon_css(self):
-        response.content_type = 'text/css'
-        return g.tool_icon_css
+        """Serve stylesheet containing icon urls for every installed tool.
+
+        """
+        etag_cache('tool_icon_css?' + str(g.server_start))
+        return utils.serve_file(StringIO(g.tool_icon_css),
+                'tool_icon_css', 'text/css', last_modified=g.server_start)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5164b74b/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 07b8e09..17e3b89 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -139,7 +139,7 @@ class Globals(object):
         self.__dict__ = self.__shared_state
         if self.__shared_state: return
         self.allura_templates = pkg_resources.resource_filename('allura', 'templates')
-
+        self.server_start = datetime.datetime.utcnow()
         # Setup SOLR
         self.solr_server = aslist(config.get('solr.server'), ',')
         # skip empty strings in case of extra commas
@@ -472,6 +472,10 @@ class Globals(object):
 
     @LazyProperty
     def tool_icon_css(self):
+        """Return a string of CSS containing class names and icon urls for
+        every installed tool.
+
+        """
         css = ''
         for tool_name in self.entry_points['tool']:
             for size in (24, 32, 48):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5164b74b/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 7d4c081..55b36f0 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -37,6 +37,7 @@ from formencode import Invalid
 from tg.decorators import before_validate
 from pylons import response
 from pylons import tmpl_context as c
+from pylons.controllers.util import etag_cache
 from paste.deploy.converters import asbool, asint
 from paste.httpheaders import CACHE_CONTROL, EXPIRES
 from webhelpers.html import literal
@@ -479,6 +480,8 @@ def take_while_true(source):
 
 def serve_file(fp, filename, content_type, last_modified=None, cache_expires=None, size=None, embed=True):
     '''Sets the response headers and serves as a wsgi iter'''
+    if filename and last_modified:
+        etag_cache('{0}?{1}'.format(filename, last_modified))
     pylons.response.headers['Content-Type'] = ''
     pylons.response.content_type = content_type.encode('utf-8')
     pylons.response.cache_expires = cache_expires or asint(tg.config.get('files_expires_header_secs', 60 * 60))