[uiprops] test and fix reloading of modified css files; update c-c newcube; deprecates config.has_resource.
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 05 May 2010 10:22:11 +0200
changeset 5466 b5af2ac0c43c
parent 5454 76b828dc3b9f
child 5467 57372dbfd114
[uiprops] test and fix reloading of modified css files; update c-c newcube; deprecates config.has_resource.
devtools/__init__.py
devtools/devctl.py
skeleton/data/external_resources.tmpl
skeleton/uiprops.py.tmpl
test/unittest_vregistry.py
web/propertysheet.py
web/test/unittest_propertysheet.py
web/webconfig.py
--- a/devtools/__init__.py	Mon May 03 14:17:45 2010 +0200
+++ b/devtools/__init__.py	Wed May 05 10:22:11 2010 +0200
@@ -181,10 +181,6 @@
     def available_languages(self, *args):
         return ('en', 'fr', 'de')
 
-    def ext_resources_file(self):
-        """return instance's external resources file"""
-        return join(self.apphome, 'data', 'external_resources')
-
     def pyro_enabled(self):
         # but export PYRO_MULTITHREAD=0 or you get problems with sqlite and threads
         return True
--- a/devtools/devctl.py	Mon May 03 14:17:45 2010 +0200
+++ b/devtools/devctl.py	Wed May 05 10:22:11 2010 +0200
@@ -595,7 +595,7 @@
         exclude = SKEL_EXCLUDE
         if self['layout'] == 'simple':
             exclude += ('sobjects.py*', 'precreate.py*', 'realdb_test*',
-                        'cubes.*', 'external_resources*')
+                        'cubes.*', 'uiprops.py*')
         copy_skeleton(skeldir, cubedir, context, exclude=exclude)
 
     def _ask_for_dependencies(self):
--- a/skeleton/data/external_resources.tmpl	Mon May 03 14:17:45 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-# -*- shell-script -*-
-###############################################################################
-#
-# put here information about external resources used by your components,
-# or to overides existing external resources configuration
-#
-###############################################################################
-
-# CSS stylesheets to include in HTML headers
-# uncomment the line below to use template specific stylesheet
-# STYLESHEETS = DATADIR/cubes.%(cubename)s.css
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/skeleton/uiprops.py.tmpl	Wed May 05 10:22:11 2010 +0200
@@ -0,0 +1,15 @@
+###############################################################################
+#
+# Put here information about external resources / styles used by your cube,
+# or to overides existing UI properties.
+#
+# Existing properties are available through the `sheet` dictionary available
+# in the global namespace. You also have access to a `data` function which
+# will return proper url for resources in the 'data' directory.
+#
+# /!\ this file should not be imported /!\
+###############################################################################
+
+# CSS stylesheets to include in HTML headers
+# uncomment the line below to use template specific stylesheet
+# STYLESHEETS = sheet['STYLESHEETS'] + [data('cubes.%(cubename)s.css')]
--- a/test/unittest_vregistry.py	Mon May 03 14:17:45 2010 +0200
+++ b/test/unittest_vregistry.py	Wed May 05 10:22:11 2010 +0200
@@ -56,21 +56,25 @@
 
 
     def test_load_subinterface_based_appobjects(self):
-        self.vreg.reset()
         self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
         # check progressbar was kicked
         self.failIf(self.vreg['views'].get('progressbar'))
+        # we've to emulate register_objects to add custom MyCard objects
+        path = [join(BASE, 'entities', '__init__.py'),
+                join(BASE, 'web', 'views', 'iprogress.py')]
+        filemods = self.vreg.init_registration(path, None)
+        for filepath, modname in filemods:
+            self.vreg.load_file(filepath, modname)
         class MyCard(Card):
             __implements__ = (IMileStone,)
-        self.vreg.reset()
         self.vreg._loadedmods[__name__] = {}
         self.vreg.register(MyCard)
-        self.vreg.register_objects([join(BASE, 'entities', '__init__.py'),
-                                    join(BASE, 'web', 'views', 'iprogress.py')])
+        self.vreg.initialization_completed()
         # check progressbar isn't kicked
         self.assertEquals(len(self.vreg['views']['progressbar']), 1)
 
     def test_properties(self):
+        self.vreg.reset()
         self.failIf('system.version.cubicweb' in self.vreg['propertydefs'])
         self.failUnless(self.vreg.property_info('system.version.cubicweb'))
         self.assertRaises(UnknownProperty, self.vreg.property_info, 'a.non.existent.key')
--- a/web/propertysheet.py	Mon May 03 14:17:45 2010 +0200
+++ b/web/propertysheet.py	Wed May 05 10:22:11 2010 +0200
@@ -47,12 +47,12 @@
         self._ordered_propfiles.append(fpath)
 
     def need_reload(self):
+        for rid, (adirectory, rdirectory, mtime) in self._cache.items():
+            if os.stat(osp.join(rdirectory, rid))[-2] > mtime:
+                del self._cache[rid]
         for fpath, mtime in self._propfile_mtime.iteritems():
             if os.stat(fpath)[-2] > mtime:
                 return True
-        for rid, (directory, mtime) in self._cache.items():
-            if os.stat(osp.join(directory, rid))[-2] > mtime:
-                del self._cache[rid]
         return False
 
     def reload(self):
@@ -70,7 +70,7 @@
             return self._cache[rid][0]
         except KeyError:
             cachefile = osp.join(self._cache_directory, rid)
-            self.debug('caching processed css %s/%s into %s',
+            self.debug('caching processed %s/%s into %s',
                        rdirectory, rid, cachefile)
             rcachedir = osp.dirname(cachefile)
             if not osp.exists(rcachedir):
@@ -83,13 +83,14 @@
                 content = self.compile(content)
             except ValueError, ex:
                 self.error("can't process %s/%s: %s", rdirectory, rid, ex)
+                adirectory = rdirectory
             else:
                 stream = file(cachefile, 'w')
                 stream.write(content)
                 stream.close()
-                rdirectory = self._cache_directory
-            self._cache[rid] = (rdirectory, os.stat(sourcefile)[-2])
-            return rdirectory
+                adirectory = self._cache_directory
+            self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile)[-2])
+            return adirectory
 
     def compile(self, content):
         return self._percent_rgx.sub('%%', content) % self
--- a/web/test/unittest_propertysheet.py	Mon May 03 14:17:45 2010 +0200
+++ b/web/test/unittest_propertysheet.py	Wed May 05 10:22:11 2010 +0200
@@ -1,12 +1,21 @@
+import os
 from os.path import join, dirname
+from shutil import rmtree
+
 from logilab.common.testlib import TestCase, unittest_main
+
 from cubicweb.web.propertysheet import *
 
 DATADIR = join(dirname(__file__), 'data')
+CACHEDIR = join(DATADIR, 'uicache')
+
 class PropertySheetTC(TestCase):
 
+    def tearDown(self):
+        rmtree(CACHEDIR)
+
     def test(self):
-        ps = PropertySheet(None, datadir_url='http://cwtest.com')
+        ps = PropertySheet(CACHEDIR, datadir_url='http://cwtest.com')
         ps.load(join(DATADIR, 'sheet1.py'))
         ps.load(join(DATADIR, 'sheet2.py'))
         # defined by sheet1
@@ -20,6 +29,21 @@
                                               'http://cwtest.com/mycube.css'])
         self.assertEquals(ps.compile('a {bgcolor: %(bgcolor)s; size: 1%;}'),
                           'a {bgcolor: #FFFFFF; size: 1%;}')
+        self.assertEquals(ps.process_resource(DATADIR, 'pouet.css'),
+                          CACHEDIR)
+        self.failUnless('pouet.css' in ps._cache)
+        self.failIf(ps.need_reload())
+        os.utime(join(DATADIR, 'sheet1.py'), None)
+        self.failUnless('pouet.css' in ps._cache)
+        self.failUnless(ps.need_reload())
+        self.failUnless('pouet.css' in ps._cache)
+        ps.reload()
+        self.failIf('pouet.css' in ps._cache)
+        self.failIf(ps.need_reload())
+        ps.process_resource(DATADIR, 'pouet.css') # put in cache
+        os.utime(join(DATADIR, 'pouet.css'), None)
+        self.failIf(ps.need_reload())
+        self.failIf('pouet.css' in ps._cache)
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/webconfig.py	Mon May 03 14:17:45 2010 +0200
+++ b/web/webconfig.py	Wed May 05 10:22:11 2010 +0200
@@ -26,6 +26,7 @@
 from warnings import warn
 
 from logilab.common.decorators import cached
+from logilab.common.deprecation import deprecated
 
 from cubicweb.toolsutils import read_config
 from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options, merge_options
@@ -253,29 +254,30 @@
             user = unicode(user)
         return user, passwd
 
-    def has_resource(self, rid):
-        """return true if an external resource is defined"""
-        return bool(self.uiprops.get(rid))
-
-    @cached
     def locate_resource(self, rid):
         """return the directory where the given resource may be found"""
         return self._fs_locate(rid, 'data')
 
-    @cached
     def locate_doc_file(self, fname):
         """return the directory where the given resource may be found"""
         return self._fs_locate(fname, 'wdoc')
 
-    def _fs_locate(self, rid, rdirectory):
+    @cached
+    def _fs_path_locate(self, rid, rdirectory):
         """return the directory where the given resource may be found"""
         path = [self.apphome] + self.cubes_path() + [join(self.shared_dir())]
         for directory in path:
             if exists(join(directory, rdirectory, rid)):
-                if rdirectory == 'data' and rid.endswith('.css'):
-                    return self.uiprops.process_resource(join(directory, rdirectory),
-                                                         rid)
-                return join(directory, rdirectory)
+                return directory
+
+    def _fs_locate(self, rid, rdirectory):
+        """return the directory where the given resource may be found"""
+        directory = self._fs_path_locate(rid, rdirectory)
+        if directory is None:
+            return None
+        if rdirectory == 'data' and rid.endswith('.css'):
+            return self.uiprops.process_resource(join(directory, rdirectory), rid)
+        return join(directory, rdirectory)
 
     def locate_all_files(self, rid, rdirectory='wdoc'):
         """return all files corresponding to the given resource"""
@@ -319,11 +321,11 @@
         libuiprops = join(self.shared_dir(), 'data', 'uiprops.py')
         self.uiprops.load(libuiprops)
         for path in reversed([self.apphome] + self.cubes_path()):
-            self._load_ui_properties(join(path, 'data'))
+            self._load_ui_properties(path)
         self._load_ui_properties(self.apphome)
 
     def _load_ui_properties(self, path):
-        resourcesfile = join(path, 'external_resources')
+        resourcesfile = join(path, 'data', 'external_resources')
         if exists(resourcesfile):
             warn('[3.9] %s file is deprecated, use an uiprops.py file'
                  % resourcesfile, DeprecationWarning)
@@ -367,3 +369,8 @@
     def static_file_del(self, rpath):
         if self.static_file_exists(rpath):
             os.remove(join(self.static_directory, rpath))
+
+    @deprecated('[3.9] use _cw.uiprops.get(rid)')
+    def has_resource(self, rid):
+        """return true if an external resource is defined"""
+        return bool(self.uiprops.get(rid))