0001from paste import httpexceptions
0002import event
0003
0004__all__ = ['public', 'ActionDispatch', 'PathDispatch']
0005
0006def public(func):
0007 func.public = True
0008 return func
0009
0010class MethodDispatch(object):
0011
0012 """
0013 This is an *abstract* class. It implements generic dispatching
0014 to servlet methods. See ``ActionDispatch`` and ``PathDispatch``
0015 for implementations.
0016
0017 Methods are considered public if their ``public`` attribute is
0018 true (you can use the ``@public`` decorator to set this) or if the
0019 method name is prefixed appropriately. (E.g., if ``prefix`` is
0020 ``action_`` then ``action_meth()`` will be considered the public
0021 method by the name ``meth`` -- the prefix is added automatically!)
0022 """
0023
0024 prefix = None
0025
0026 def __addtoclass__(self, attr, cls):
0027 if self.__class__ is MethodDispatch:
0028 raise NotImplementedError(
0029 "MethodDispatch is an abstract class, and cannot be "
0030 "used directly (use one of its subclasses)")
0031 cls.listeners.append(self.respond_event)
0032
0033 def respond_event(self, name, servlet, *args, **kw):
0034 if name == 'end_awake':
0035 result = self.find_method(servlet, *args, **kw)
0036 if result is None:
0037 return event.Continue
0038 else:
0039 return result
0040 return event.Continue
0041
0042 def get_method(self, servlet, action):
0043 if self.prefix:
0044 try:
0045 return (self.prefix + action,
0046 getattr(servlet, self.prefix + action))
0047 except AttributeError:
0048 pass
0049 try:
0050 return action, getattr(servlet, action)
0051 except AttributeError:
0052 pass
0053 return None, None
0054
0055 def valid_method(self, name, method):
0056 if getattr(method, 'public', False):
0057 return True
0058 if self.prefix and name.startswith(self.prefix):
0059 return True
0060 return False
0061
0062class ActionDispatch(MethodDispatch):
0063
0064 """
0065 This dispatches to a method based on a URL variable (GET or POST
0066 -- remember that you can also include GET variables in your form's
0067 ``action`` even if the form is being POSTed).
0068
0069 The URL variable indicates a method to run; if no variable is
0070 found then ``default_action`` is assumed (if you pass in a default
0071 action).
0072
0073 The URL variable is given with ``action_name`` and defaults to
0074 ``'_action_'``. You can pass in the action either as
0075 ``_action_=method_name`` or ``_action_method_name=anything``
0076 (useful with submit buttons, where the value is part of the UI).
0077 """
0078
0079 prefix = 'action_'
0080
0081 def __init__(self, action_name='_action_', default_action=None):
0082 self.action_name = action_name
0083 self.default_action = default_action
0084
0085 def find_method(self, servlet, ret_value, **kw):
0086 possible_actions = []
0087 for name, value in servlet.fields.items():
0088 if name == self.action_name:
0089 possible_actions.append(value)
0090 elif name.startswith(self.action_name):
0091 possible_actions.append(name[len(self.action_name):])
0092 if not possible_actions:
0093 if self.default_action:
0094 possible_actions = [self.default_action]
0095 else:
0096 return event.Continue
0097 if len(possible_actions) > 1:
0098 raise httpexceptions.HTTPBadRequest(
0099 "More than one action received: %s"
0100 % ', '.join(map(repr, possible_actions)))
0101 action = possible_actions[0]
0102 name, method = self.get_method(servlet, action)
0103 if name is None:
0104 raise httpexceptions.HTTPForbidden(
0105 "Action method not found: %r" % action)
0106 if not self.valid_method(name, method):
0107 raise httpexceptions.HTTPForbidden(
0108 "Method not allowed: %r" % action)
0109 return method()
0110
0111class PathDispatch(MethodDispatch):
0112
0113 """
0114 This dispatches to a method based on the ``PATH_INFO``. Thus
0115 ``/path/to/servlet/meth`` will call the ``meth`` method.
0116
0117 @@: This should probably adjust SCRIPT_NAME and PATH_INFO
0118 @@: Should these all use the same prefix?
0119 """
0120
0121 prefix = 'path_'
0122
0123 def find_method(self, servlet, ret_value, **kw):
0124 parts = servlet.path_parts
0125 if not parts:
0126 action = 'index'
0127 else:
0128 action = parts[0]
0129 servlet.path_parts = parts[1:]
0130 name, method = self.get_method(servlet, action)
0131 if name is None:
0132 raise httpexceptions.HTTPForbidden(
0133 "Method not found: %r" % action)
0134 if not self.valid_method(name, method):
0135 raise httpexceptions.HTTPForbidden(
0136 "Method not allowed: %r" % action)
0137 return method()