[cig-commits] r7839 - in cs/pythia/trunk/opal: components
controllers core core/handlers db/models sites views
leif at geodynamics.org
leif at geodynamics.org
Thu Aug 16 19:54:33 PDT 2007
Author: leif
Date: 2007-08-16 19:54:31 -0700 (Thu, 16 Aug 2007)
New Revision: 7839
Added:
cs/pythia/trunk/opal/core/responders.py
Modified:
cs/pythia/trunk/opal/components/ListEditor.py
cs/pythia/trunk/opal/components/WebComponent.py
cs/pythia/trunk/opal/controllers/DeletionController.py
cs/pythia/trunk/opal/controllers/UpdateController.py
cs/pythia/trunk/opal/core/context_processors.py
cs/pythia/trunk/opal/core/handlers/base.py
cs/pythia/trunk/opal/core/urlresolvers.py
cs/pythia/trunk/opal/db/models/manipulators.py
cs/pythia/trunk/opal/sites/WebSite.py
cs/pythia/trunk/opal/views/DetailView.py
Log:
Introduced class Responder. Switched to TreeURLResolver. URL
resolvers return a Responder instead of a view (callback,
callback_args, callback_kwargs) tuple. APPEND_SLASH is marked for
deletion; TreeURLResolver appends a slash depending on whether the
object being assessed is a "leaf" or not. Database queries now occur
during the URL resolver phase.
Modified: cs/pythia/trunk/opal/components/ListEditor.py
===================================================================
--- cs/pythia/trunk/opal/components/ListEditor.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/components/ListEditor.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -20,10 +20,10 @@
from ObjectEditor import ObjectEditor
- def __init__(self, **attrs):
- super(ListEditor, self).__init__(**attrs)
- self.allowEmpty = attrs.get('allowEmpty', True)
- self.SubEditor = attrs.get('SubEditor', self.ObjectEditor)
+ def __init__(self, allow_empty=True, SubEditor=None, **kwds):
+ super(ListEditor, self).__init__(**kwds)
+ self.allow_empty = allow_empty
+ self.SubEditor = SubEditor or self.ObjectEditor
def getView(self, request):
@@ -47,7 +47,7 @@
return self.genericObjectList(
request,
self.querySet(),
- allow_empty = self.allowEmpty,
+ allow_empty = self.allow_empty,
extra_context = self.context,
)
Modified: cs/pythia/trunk/opal/components/WebComponent.py
===================================================================
--- cs/pythia/trunk/opal/components/WebComponent.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/components/WebComponent.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -53,6 +53,14 @@
urlpatterns = []
+ def rootResponder(self):
+ return self
+
+
+ def subResponder(self, name):
+ raise http.Http404
+
+
# urlpatterns support
def include(self, urlconf_module):
@@ -108,20 +116,20 @@
return controller.response(request)
- def genericUpdateObject(self, request, model, query, post_save_redirect=None, follow=None, **kwds):
+ def genericUpdateObject(self, request, model, post_save_redirect=None, follow=None, **kwds):
controller = controllers.UpdateController(
post_redirect = post_save_redirect,
follow = follow,
)
- view = views.DetailView(model, query, controller = controller, **kwds)
+ view = views.DetailView(model, controller = controller, **kwds)
return controller.response(request)
- def genericDeleteObject(self, request, model, query, post_delete_redirect, **kwds):
+ def genericDeleteObject(self, request, model, post_delete_redirect, **kwds):
controller = controllers.DeletionController(
post_redirect = post_delete_redirect,
)
- view = views.DetailView(model, query, controller = controller, **kwds)
+ view = views.DetailView(model, controller = controller, **kwds)
return controller.response(request)
@@ -163,13 +171,13 @@
from opal.http import Http404
- def __xinit__(self, **attrs):
+ def __xinit__(self, slug, url, context=None, **kwds):
super(WebComponent, self).__init__()
- self.slug = attrs.get('slug')
- self.context = attrs.get('context', {})
- self.parentURL = attrs.get('url')
+ self.slug = slug
+ self.context = context or {}
+ self.parentURL = url
self.subcomponents = None
- self._initTreeNode(**attrs)
+ self._initTreeNode(url, **kwds)
def createSubcomponent(self, slug, **attrs):
@@ -226,11 +234,11 @@
# TreeNode protocol
- def _initTreeNode(self, **attrs):
+ def _initTreeNode(self, url, name=None, title=None, **kwds):
# this node
- self.name = attrs.get('name', self.slug)
- self.title = attrs.get('title', self.name.title())
- self.url = attrs['url'] + '/' + self.slug
+ self.name = name or self.slug
+ self.title = title or self.name.title()
+ self.url = url + '/' + self.slug
# children
self.isLeaf = False
Modified: cs/pythia/trunk/opal/controllers/DeletionController.py
===================================================================
--- cs/pythia/trunk/opal/controllers/DeletionController.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/controllers/DeletionController.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -41,7 +41,7 @@
def response(self, request):
if request.method == "POST":
- self.view.obj.delete()
+ self.view.model.delete()
if request.user.is_authenticated():
request.user.message_set.create(message="The %s was deleted." % self.model._meta.verbose_name)
return HttpResponseRedirect(self.post_redirect)
Modified: cs/pythia/trunk/opal/controllers/UpdateController.py
===================================================================
--- cs/pythia/trunk/opal/controllers/UpdateController.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/controllers/UpdateController.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -32,8 +32,8 @@
def newManipulator(self):
- obj = self.view.obj
- return self.model.ChangeManipulator(getattr(obj, obj._meta.pk.name), follow=self.follow)
+ model = self.view.model
+ return self.model.ChangeManipulator(getattr(model, model._meta.pk.name), follow=self.follow)
def successMessage(self):
@@ -42,8 +42,8 @@
def decorateResponse(self, response, request):
super(UpdateController, self).decorateResponse(response, request)
- obj = self.view.obj
- populate_xheaders(request, response, self.model, getattr(obj, obj._meta.pk.name))
+ model = self.view.model
+ populate_xheaders(request, response, self.model, getattr(model, model._meta.pk.name))
return
Modified: cs/pythia/trunk/opal/core/context_processors.py
===================================================================
--- cs/pythia/trunk/opal/core/context_processors.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/context_processors.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -43,7 +43,7 @@
return context_extras
def request(request):
- return {'request': request}
+ return {'xrequest': request} # 'xrequest', because 'request' is always 'None'. WTF?
# PermWrapper and PermLookupDict proxy the permissions system into objects that
# the template system can understand.
Modified: cs/pythia/trunk/opal/core/handlers/base.py
===================================================================
--- cs/pythia/trunk/opal/core/handlers/base.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/handlers/base.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -63,16 +63,17 @@
site = settings
resolver = site.urlResolver()
try:
- callback, callback_args, callback_kwargs = resolver.resolve(request.path)
+ responder = resolver.resolve(request)
- # Apply view middleware
- for middleware_method in self._view_middleware:
- response = middleware_method(request, callback, callback_args, callback_kwargs)
- if response:
- return response
+ if False: # There appears to be only one: XViewMiddleware
+ # Apply view middleware
+ for middleware_method in self._view_middleware:
+ response = middleware_method(request, callback, callback_args, callback_kwargs)
+ if response:
+ return response
try:
- response = callback(request, *callback_args, **callback_kwargs)
+ response = responder.response(request)
except Exception, e:
# If the view raised an exception, run it through exception
# middleware, and if the exception middleware returns a
@@ -83,13 +84,9 @@
return response
raise
- # Complain if the view returned None (a common error).
+ # Complain if the responder returned None (a common error).
if response is None:
- try:
- view_name = callback.func_name # If it's a function
- except AttributeError:
- view_name = callback.__class__.__name__ + '.__call__' # If it's a class
- raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
+ raise ValueError, "The responder %s didn't return an HttpResponse object." % responder
return response
except http.Http404, e:
Added: cs/pythia/trunk/opal/core/responders.py
===================================================================
--- cs/pythia/trunk/opal/core/responders.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/responders.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+# California Institute of Technology
+# (C) 2007 All Rights Reserved
+#
+# {LicenseText}
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+
+
+from opal import http
+
+
+class Responder(object):
+
+ def response(self, request):
+ raise NotImplemented
+
+ def isLeaf(self):
+ return not hasattr(self, 'subResponder')
+
+ def notFound(self):
+ raise http.Http404
+
+
+
+class ResponderChain(Responder):
+
+ def __init__(self, *responders):
+ self.responders = responders
+
+ def response(self, request):
+ return self.responders[0].response(request)
+
+ def subResponder(self, name):
+ for responder in self.responders:
+ try:
+ return responder.subResponder(name)
+ except http.Http404:
+ pass
+ self.notFound()
+
+chain = ResponderChain
+
+
+
+class DirectoryResponder(Responder):
+
+ def __init__(self, index, dct):
+ self.index = index
+ self.dct = dct
+
+ def response(self, request):
+ return self.index.response(request)
+
+ def subResponder(self, name):
+ try:
+ responder = self.dct[name]
+ except KeyError:
+ self.notFound()
+ return responder
+
+directory = DirectoryResponder
+
+
+
+class QueryDirectoryResponder(Responder):
+
+ def __init__(self, Model, Inquirer, subResponderFactory):
+ self.Model = Model
+ self.Inquirer = Inquirer
+ self.subResponderFactory = subResponderFactory
+ return
+
+ def subResponder(self, name):
+ from opal.core.exceptions import ObjectDoesNotExist
+ query = self.newQuery(name)
+ try:
+ obj = query.get(self.Model._default_manager.all())
+ except ObjectDoesNotExist:
+ raise http.Http404("No %s found matching the query '%s'." % (self.Model._meta.verbose_name, query))
+ return self.newSubResponder(obj)
+
+ def newQuery(self, key):
+ return self.Inquirer.newQuery(key)
+
+ def newSubResponder(self, obj):
+ return self.subResponderFactory(obj)
+
+queryDirectory = QueryDirectoryResponder
+
+
+
+class FunctionResponder(Responder):
+
+ def __init__(self, function, *args, **kwds):
+ self.function = function
+ self.args = args
+ self.kwds = kwds
+
+ def response(self, request):
+ return self.function(request, *self.args, **self.kwds)
+
+
+
+class MethodResponderContext(Responder):
+
+ def __init__(self, instance, method, *args, **kwds):
+ self.instance = instance
+ self.method = method
+ self.args = args
+ self.kwds = kwds
+
+ def response(self, request):
+ return self.method(self.instance, request, *self.args, **self.kwds)
+
+
+
+class MethodResponder(Responder):
+
+ def __init__(self, method, instance):
+ self.method = method
+ self.instance = instance
+
+ def response(self, request):
+ return self.method(self.instance, request)
+
+ def __call__(self, *args, **kwds):
+ return MethodResponderContext(self.instance, self.method, *args, **kwds)
+
+
+
+class ResponderMethodDescriptor(object):
+
+ def __init__(self, method):
+ self.method = method
+
+ def __get__(self, instance, cls=None):
+ return MethodResponder(self.method, instance)
+
+respondermethod = ResponderMethodDescriptor
+
+
+
+class Redirector(Responder):
+
+ def __init__(self, newUrl):
+ self.newUrl = newUrl
+
+ def response(self, request):
+ return http.HttpResponsePermanentRedirect(self.newUrl)
+
+
+
+# end of file
Modified: cs/pythia/trunk/opal/core/urlresolvers.py
===================================================================
--- cs/pythia/trunk/opal/core/urlresolvers.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/core/urlresolvers.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -7,11 +7,11 @@
(view_function, function_args, function_kwargs)
"""
-from opal.http import Http404
+from opal import http
from opal.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
import re
-class Resolver404(Http404):
+class Resolver404(http.Http404):
pass
class NoReverseMatch(Exception):
@@ -226,22 +226,41 @@
resolver = RegexURLResolver(r'^/', urlconf)
return '/' + resolver.reverse(viewname, *args, **kwargs)
+
class TreeURLResolver(object):
+
def __init__(self, root):
self.root = root
-
- def resolve(self, pathname):
- #raise Resolver404, {'tried': ['foo', 'bar', path], 'path': path + 'hey'}
- path = pathname.split('/')
- args = ()
- kwargs = {}
+
+
+ def resolve(self, request):
+ path = request.path.split('/')
node = self.root
- try:
- for name in path[:-1]:
- node = node.children[name]
- name = path[-1]
- if name != "":
- node = node.children[name]
- except KeyError:
- raise Http404
- return node.view().response, args, kwargs
+
+ tried = []
+ for name in path[1:-1]:
+ node = node.subResponder(name)
+ tried.append(node)
+ name = path[-1]
+
+ hasTrailingSlash = (name == "")
+ if hasTrailingSlash:
+ if node.isLeaf():
+ raise Resolver404, {'tried': tried, 'path': request.path}
+ else:
+ node = node.subResponder(name)
+ # Don't redirect POST requests -- otherwise, the POST data would be lost!
+ if not node.isLeaf() and request.method != 'POST':
+ return self.redirect(request, request.path + '/')
+
+ return node
+
+
+ def redirect(self, request, newUrl):
+ from opal.core.responders import Redirector
+ host = http.get_host(request)
+ if host:
+ newUrl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', host, newUrl)
+ if request.GET:
+ newUrl += '?' + request.GET.urlencode()
+ return Redirector(newUrl)
Modified: cs/pythia/trunk/opal/db/models/manipulators.py
===================================================================
--- cs/pythia/trunk/opal/db/models/manipulators.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/db/models/manipulators.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -26,7 +26,8 @@
self.base = base
def __get__(self, instance, model=None):
- if instance != None:
+ if instance != None and False:
+ # Why not? 2007-08-16 lcs
raise AttributeError, "Manipulator cannot be accessed via instance"
else:
if not self.man:
Modified: cs/pythia/trunk/opal/sites/WebSite.py
===================================================================
--- cs/pythia/trunk/opal/sites/WebSite.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/sites/WebSite.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -158,7 +158,7 @@
'opal.core.context_processors.auth',
'opal.core.context_processors.debug',
'opal.core.context_processors.i18n',
- #'opal.core.context_processors.request',
+ 'opal.core.context_processors.request',
])
TEMPLATE_CONTEXT_PROCESSORS.meta['tip'] = """List of processors used by RequestContext to populate the context. Each one should be a callable that takes the request object as its only parameter and returns a dictionary to add to the context."""
@@ -175,7 +175,7 @@
EMAIL_SUBJECT_PREFIX.meta['tip'] = """Subject-line prefix for email messages send with opal.core.mail.mail_admins or ...mail_managers. Make sure to include the trailing space."""
### I hate this one. It should depend upon the object being accessed.
- APPEND_SLASH = pyre.bool("append-slash", default=True)
+ APPEND_SLASH = pyre.bool("append-slash", default=False)
APPEND_SLASH.meta['tip'] = """Whether to append trailing slashes to URLs."""
PREPEND_WWW = pyre.bool("prepend-www", default=False)
@@ -401,7 +401,10 @@
self.ROOT_URLCONF = urlconf
- return urlresolvers.RegexURLResolver(r'^/', self.ROOT_URLCONF)
+ #return urlresolvers.RegexURLResolver(r'^/', self.ROOT_URLCONF)
+ app = self.ROOT_URLCONF
+ root = app.rootResponder()
+ return urlresolvers.TreeURLResolver(root)
# end of file
Modified: cs/pythia/trunk/opal/views/DetailView.py
===================================================================
--- cs/pythia/trunk/opal/views/DetailView.py 2007-08-16 21:39:25 UTC (rev 7838)
+++ cs/pythia/trunk/opal/views/DetailView.py 2007-08-17 02:54:31 UTC (rev 7839)
@@ -30,27 +30,15 @@
defaultTemplateNameTag = "detail"
- def __init__(self, model, query=None, template_name_field=None, **kwds):
+ def __init__(self, model, template_name_field=None, **kwds):
View.__init__(self, model, **kwds)
- self.query = query
- self._obj = None
self.template_name_field = template_name_field
return
- def _getObj(self):
- if self._obj is None and self.query:
- try:
- self._obj = self.query.get(self.model._default_manager.all())
- except ObjectDoesNotExist:
- raise Http404("No %s found matching the query '%s'" % (self.model._meta.verbose_name, self.query))
- return self._obj
- obj = property(_getObj)
-
-
def loadTemplate(self):
- if self.obj and self.template_name_field:
- template_name_list = [getattr(self.obj, self.template_name_field), self.template_name]
+ if self.model and self.template_name_field:
+ template_name_list = [getattr(self.model, self.template_name_field), self.template_name]
t = self.template_loader.select_template(template_name_list)
else:
t = super(DetailView, self).loadTemplate()
@@ -59,8 +47,8 @@
def contextDictionary(self, request):
dct = super(DetailView, self).contextDictionary(request)
- if self.obj:
- dct[self.template_object_name] = self.obj
+ if self.model:
+ dct[self.template_object_name] = self.model
return dct
More information about the cig-commits
mailing list