Commits

imbolc committed ba42aa1

static files versioning

Comments (0)

Files changed (7)

 from decorators import basic_auth, anticache, view
 from template import render_to_string, render
 from util import cached_property, cached_function
-import signal
+from static import static_url
+import signal
 # -*- coding: utf-8 -*-
-import wsgi
 
 
 class Cfg(object):
     ROUTING_QUEUE = ['pysi.auto_routing']  # очередь функций роутинга
     JINJA2_EXTENSIONS = ['jinja2.ext.with_']
 
+    STATIC_PATH = 'static'
+    STATIC_URL_PREFIX  = '/static/'
+    STATIC_VERSION_CACHING = True  # кэшировать посчитанные версионные хеши
+
     def set_defaults(self, **kwargs):
         '''
         Добавляем дефолтные атрибуты
 # -*- coding: utf-8 -*-
-'''
-Роутинг
-'''
 import urllib
 import datetime
 
     m.add_rule('/user/<int:id>/', 'user1', 'user')
     m.add_rule('/user/<int:id>/<str:name>/', 'user2', 'user')
     m.add_rule('/user/<*:slug>', 'user3', 'user')
-    
+
     from om.pp import pp
-    #~ pp(m.queue)
-    #~ pp(m.rev)
-    #~ return
-    
+    pp(m.queue)
+    pp(m.rev)
+    return
+
     print m.find('/user/41/---/')
     print m.reverse('user', id=1)
     print m.reverse('user', id=1, name='pysi', x=4)
                 return url
         assert 0, 'Could not find url for "%s" witch kwargs: %s' % (urlname,
             ', '.join('%s %s' % (k, type(v)) for k, v in kwargs.iteritems()))
-            
+
 def match_const(var):
     def const_dir(dir):
         return dir == var, None
     def str_dir(dir):
         return True, urllib.unquote_plus(dir)
     return str_dir
-    
+
 def match_int(var):
     def int_dir(dir):
         if dir.isdigit():
                 # забыли закрывающий слеш?
                 func, kwargs = urls.find(rq.path + '/')
                 if func:
-                    return wsgi.redirect(rq.path + '/')        
+                    return wsgi.redirect(rq.path + '/')
             wsgi.abort(404)
         return func(rq, **kwargs)
-        
+
     def add_urls(*rules):
         ''' Добавление правил списком'''
         for rule in rules:
             urls.add_rule(*rule)
-            
+
     def url4(*args, **kwargs):
         return urls.reverse(*args, **kwargs)
-        
+
     def add_apps(apps):
         '''
         Добавление приложений
             app_name = '%s.%s' % (mod, 'views')
             __import__(app_name)
             urls.url_prefix = None
-    
+
+import os
+import logging
+import hashlib
+
+from config import cfg
+
+
+log = logging.getLogger(__name__)
+VERSION_CACHE = {}  # {path : version_hash}
+
+
+def static_url(path, caching=None, url_prefix=None):
+    path = path.lstrip('/')
+    if caching is None:
+        caching = cfg.STATIC_VERSION_CACHING
+    if url_prefix is None:
+        url_prefix = cfg.STATIC_URL_PREFIX
+    ret = url_prefix + path
+    version_hash = get_version(path, caching)
+    if version_hash:
+        ret += "?v=" + version_hash
+    return ret
+
+def get_version(path, caching):
+    if caching:
+        try:
+            return VERSION_CACHE[path]
+        except KeyError:
+            pass
+    log.debug(u'Calculate version hash for static file %s' % path)
+    abs_path = os.path.join(cfg.STATIC_PATH, path)
+    try:
+        f = open(abs_path, "rb")
+        ret = hashlib.md5(f.read()).hexdigest()[:5]
+        f.close()
+    except Exception:
+        log.error(u'Could not open static file %r', path)
+        ret = None
+    if caching:
+        VERSION_CACHE[path] = ret
+    return ret
                 assert 0, 'template not found: %s' % path
         path = os.path.normpath(os.path.join(os.getcwd(), f.name))
         self.mtimes[path] = os.path.getmtime(path)
-        return (f.read().decode('utf-8'), path, 
+        return (f.read().decode('utf-8'), path,
             lambda: self.mtimes[path] == os.path.getmtime(path))
 file_loader = FileLoader().load
 import datetime
 import traceback
 
-import pysi
-
 
 WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
-MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 
+MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
     'Sep', 'Oct', 'Nov', 'Dec']
 
 
     line = '-' * len(head)
     return '\n%s\n%s\n%s\n%s' % (line, head, line,
         ''.join(traceback.format_exception(*sys.exc_info())[1:]))
-            
+
 def anticache_headers():
     expires = http_date(datetime.datetime.utcnow())
     return {
     week = WEEKDAYS[dt.weekday()]
     month = MONTHS[dt.month - 1]
     return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (week, dt.day, month,
-        dt.year, dt.hour, dt.minute, dt.second)
+        dt.year, dt.hour, dt.minute, dt.second)
 from util import cached_property, make_traceback, obj_from_str, list_obj_from_str
 from config import cfg
 from exceptions import HttpError, BasicAuth
-from routing import auto_routing, add_apps, url4
+from routing import add_apps, url4
 from multidict import MultiDict, HeaderDict
+from static import static_url
 
 
 class Request(object):
 
     @cached_property
     def context(self):
-        res = {'rq': self, 'url4': url4, 'cfg': cfg}
+        res = {'rq': self, 'url4': url4, 'cfg': cfg, 'static_url': static_url}
         for func in cfg.CONTEXT_PROCESSORS:
             res.update(func(self))
         return res
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.