Implemented action dispatch for prest
authorAaron Bentley <abentley@panoramicfeedback.com>
Wed, 21 Dec 2005 17:50:48 +0000 (12:50 -0500)
committerAaron Bentley <abentley@panoramicfeedback.com>
Wed, 21 Dec 2005 17:50:48 +0000 (12:50 -0500)
beweb/beweb/prest.py

index 9a6c3376ba1b438e5ff82b8fb4e1bcc871f8f465..97ab1d970e677e9f558bbb002d083be0230a7219 100644 (file)
@@ -3,9 +3,37 @@ import unittest
 """A pseudo-REST dispatching method in which only the noun comes from the path.
 The action performed will depend on kwargs.
 """
+
+class AmbiguousAction(Exception):
+    def __init__(self, actions):
+        Exception.__init__(self, "Supplied action is ambiguous.")
+        self.actions = actions
+    
+
+def provide_action(name, value):
+    def provider(func):
+        func._action_desc = (name, value)
+        return func
+    return provider
+
 class PrestHandler(object):
     def __init__(self):
         object.__init__(self)
+        self.actions = {}
+        for member in (getattr(self, m) for m in dir(self)):
+            if not hasattr(member, '_action_desc'):
+                continue
+            name, value = member._action_desc
+            if name not in self.actions:
+                self.actions[name] = {}
+            self.actions[name][value] = member
+
+    @classmethod
+    def add_action(klass, name, value, function):
+        if name not in klass.actions:
+            klass.actions[name] = {}
+        klass.actions[name][value] = function
+
 
     def decode(self, path, data=None):
         """Convert the path into a handler, a resource, data, and extra_path"""
@@ -23,12 +51,35 @@ class PrestHandler(object):
 
     def default(self, *args, **kwargs):
         child, resource, data, extra = self.decode([None,] + list(args))
-        child.dispatch(*([data, resource]+extra), **kwargs)
+        action = child.get_action(**kwargs)
+        new_args = ([data, resource]+extra)
+        if action is not None:
+            action(*new_args, **kwargs)
+        else:
+            child.dispatch(*new_args, **kwargs)
+
+    def get_action(self, **kwargs):
+        """Return the action requested by kwargs, if any.
+        
+        Raises AmbiguousAction if more than one action matches.
+        """
+        actions = []
+        for key in kwargs:
+            if key in self.actions:
+                if kwargs[key] in self.actions[key]:
+                    actions.append(self.actions[key][kwargs[key]])
+        if len(actions) == 0:
+            return None
+        elif len(actions) == 1:
+            return actions[0]
+        else:
+            raise AmbiguousAction(actions)
 
 
 class PrestTester(TestCase):
     def test_decode(self):
         class ProjectHandler(PrestHandler):
+            actions = {}
             def dispatch(self, project_data, project, *args, **kwargs):
                 self.project_id = project_data['project']
                 self.project_data = project_data
@@ -39,6 +90,14 @@ class PrestTester(TestCase):
             def instantiate(self, project):
                 return [project]
 
+            @provide_action('action', 'Save')
+            def save(self, project_data, project, *args, **kwargs):
+                self.action = "save"
+
+            @provide_action('behavior', 'Update')
+            def update(self, project_data, project, *args, **kwargs):
+                self.action = "update"
+            
         foo = PrestHandler()
         foo.project = ProjectHandler()
         handler, resource, data, extra = foo.decode([None, 'project', '83', 
@@ -51,6 +110,14 @@ class PrestTester(TestCase):
         self.assertEqual(foo.project.kwargs, {'a':'b', 'b':'97'})
         self.assertEqual(foo.project.project_data, {'project': '27'})
         self.assertEqual(foo.project.resource, ['27'])
+        foo.default(*['project', '27', 'extra'], **{'action':'Save', 'b':'97'})
+        self.assertEqual(foo.project.action, 'save')
+        foo.default(*['project', '27', 'extra'], 
+                    **{'behavior':'Update', 'b':'97'})
+        self.assertEqual(foo.project.action, 'update')
+        self.assertRaises(AmbiguousAction, foo.default, 
+                          *['project', '27', 'extra'], 
+                          **{'behavior':'Update', 'action':'Save', 'b':'97'})
                 
 def test():
     patchesTestSuite = unittest.makeSuite(PrestTester,'test')