[registry|ajaxcontroller] the @ajaxcontroller decorator ought to return a compatible object (closes #2385155)
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Fri, 08 Jun 2012 16:47:07 +0200
changeset 8437 c9ab72f0645d
parent 8436 150191e45ee5
child 8438 2c79c29193e7
[registry|ajaxcontroller] the @ajaxcontroller decorator ought to return a compatible object (closes #2385155)
cwvreg.py
web/test/unittest_views_basecontrollers.py
web/views/ajaxcontroller.py
--- a/cwvreg.py	Thu Jun 07 18:33:53 2012 +0200
+++ b/cwvreg.py	Fri Jun 08 16:47:07 2012 +0200
@@ -256,6 +256,12 @@
                       key=lambda x: x.cw_propval('order'))
 
 
+def related_appobject(obj, appobjectattr='__appobject__'):
+    """ adapts any object to a potential appobject bound to it
+    through the __appobject__ attribute
+    """
+    return getattr(obj, appobjectattr, obj)
+
 
 class ETypeRegistry(CWRegistry):
 
@@ -272,6 +278,7 @@
         self.clear_caches()
 
     def register(self, obj, **kwargs):
+        obj = related_appobject(obj)
         oid = kwargs.get('oid') or obj.__regid__
         if oid != 'Any' and not oid in self.schema:
             self.error('don\'t register %s, %s type not defined in the '
@@ -537,6 +544,20 @@
     def itervalues(self):
         return (value for key, value in self.items())
 
+    def load_module(self, module):
+        """ variation from the base implementation:
+        apply related_appobject to the automatically registered objects
+        """
+        self.info('loading %s from %s', module.__name__, module.__file__)
+        if hasattr(module, 'registration_callback'):
+            module.registration_callback(self)
+            return
+        for objname, obj in vars(module).iteritems():
+            if objname.startswith('_'):
+                continue
+            self._load_ancestors_then_object(module.__name__,
+                                             related_appobject(obj))
+
     def reset(self):
         CW_EVENT_MANAGER.emit('before-registry-reset', self)
         super(CWRegistryStore, self).reset()
@@ -552,6 +573,17 @@
                 self.register_property(key, **propdef)
         CW_EVENT_MANAGER.emit('after-registry-reset', self)
 
+    def register_all(self, objects, modname, butclasses=()):
+        butclasses = set(related_appobject(obj)
+                         for obj in butclasses)
+        objects = [related_appobject(obj) for obj in objects]
+        super(CWRegistryStore, self).register_all(objects, modname, butclasses)
+
+    def register_and_replace(self, obj, replaced):
+        obj = related_appobject(obj)
+        replaced = related_appobject(replaced)
+        super(CWRegistryStore, self).register_and_replace(obj, replaced)
+
     def set_schema(self, schema):
         """set instance'schema and load application objects"""
         self._set_schema(schema)
@@ -624,6 +656,7 @@
         If `clear` is true, all objects with the same identifier will be
         previously unregistered.
         """
+        obj = related_appobject(obj)
         super(CWRegistryStore, self).register(obj, *args, **kwargs)
         # XXX bw compat
         ifaces = use_interfaces(obj)
--- a/web/test/unittest_views_basecontrollers.py	Thu Jun 07 18:33:53 2012 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Fri Jun 08 16:47:07 2012 +0200
@@ -695,38 +695,44 @@
         @ajaxfunc
         def foo(self, x, y):
             return 'hello'
-        self.assertTrue(issubclass(foo, AjaxFunction))
-        self.assertEqual(foo.__regid__, 'foo')
-        self.assertEqual(foo.check_pageid, False)
-        self.assertEqual(foo.output_type, None)
+        self.assertEqual(foo(object, 1, 2), 'hello')
+        appobject = foo.__appobject__
+        self.assertTrue(issubclass(appobject, AjaxFunction))
+        self.assertEqual(appobject.__regid__, 'foo')
+        self.assertEqual(appobject.check_pageid, False)
+        self.assertEqual(appobject.output_type, None)
         req = self.request()
-        f = foo(req)
+        f = appobject(req)
         self.assertEqual(f(12, 13), 'hello')
 
     def test_ajaxfunc_checkpageid(self):
-        @ajaxfunc( check_pageid=True)
+        @ajaxfunc(check_pageid=True)
         def foo(self, x, y):
-            pass
-        self.assertTrue(issubclass(foo, AjaxFunction))
-        self.assertEqual(foo.__regid__, 'foo')
-        self.assertEqual(foo.check_pageid, True)
-        self.assertEqual(foo.output_type, None)
+            return 'hello'
+        self.assertEqual(foo(object, 1, 2), 'hello')
+        appobject = foo.__appobject__
+        self.assertTrue(issubclass(appobject, AjaxFunction))
+        self.assertEqual(appobject.__regid__, 'foo')
+        self.assertEqual(appobject.check_pageid, True)
+        self.assertEqual(appobject.output_type, None)
         # no pageid
         req = self.request()
-        f = foo(req)
+        f = appobject(req)
         self.assertRaises(RemoteCallFailed, f, 12, 13)
 
     def test_ajaxfunc_json(self):
         @ajaxfunc(output_type='json')
         def foo(self, x, y):
             return x + y
-        self.assertTrue(issubclass(foo, AjaxFunction))
-        self.assertEqual(foo.__regid__, 'foo')
-        self.assertEqual(foo.check_pageid, False)
-        self.assertEqual(foo.output_type, 'json')
+        self.assertEqual(foo(object, 1, 2), 3)
+        appobject = foo.__appobject__
+        self.assertTrue(issubclass(appobject, AjaxFunction))
+        self.assertEqual(appobject.__regid__, 'foo')
+        self.assertEqual(appobject.check_pageid, False)
+        self.assertEqual(appobject.output_type, 'json')
         # no pageid
         req = self.request()
-        f = foo(req)
+        f = appobject(req)
         self.assertEqual(f(12, 13), '25')
 
 
--- a/web/views/ajaxcontroller.py	Thu Jun 07 18:33:53 2012 +0200
+++ b/web/views/ajaxcontroller.py	Fri Jun 08 16:47:07 2012 +0200
@@ -283,11 +283,21 @@
                 if data is None:
                     raise RemoteCallFailed(self._cw._('pageid-not-found'))
             return self.serialize(implementation(self, *args, **kwargs))
+
     AnAjaxFunc.__name__ = implementation.__name__
     # make sure __module__ refers to the original module otherwise
     # vreg.register(obj) will ignore ``obj``.
     AnAjaxFunc.__module__ = implementation.__module__
-    return AnAjaxFunc
+    # relate the ``implementation`` object to its wrapper appobject
+    # will be used by e.g.:
+    #   import base_module
+    #   @ajaxfunc
+    #   def foo(self):
+    #       return 42
+    #   assert foo(object) == 42
+    #   vreg.register_and_replace(foo, base_module.older_foo)
+    implementation.__appobject__ = AnAjaxFunc
+    return implementation
 
 
 def ajaxfunc(implementation=None, selector=yes(), output_type=None,