tapestry.py   [plain text]



"""
Woven object collections.

THIS MODULE IS HIGHLY EXPERIMENTAL AND MAY BE DEPRECATED SOON.
"""

from __future__ import nested_scopes

__version__ = "$Revision: 1.2 $"[11:-2]


import warnings
warnings.warn("The tapestry module is deprecated. Use page instead.", DeprecationWarning, 1)

# System Imports
import sys
import os

# Twisted Imports

from twisted.internet.defer import Deferred

from twisted.web.resource import Resource, IResource
from twisted.web.static import redirectTo, addSlash, File, Data
from twisted.web.server import NOT_DONE_YET
from twisted.web import util

from twisted.python.reflect import qual
from twisted.python import components

# Sibling Imports
from twisted.web.woven.view import View

_ChildJuggler = util.DeferredResource

class ModelLoader(Resource):
    """Resource for loading models.  (see loadModel)
    """
    def __init__(self, parent, templateFile=None):
        Resource.__init__(self)
        self.parent = parent
        self.templateFile = templateFile

    def modelClass(self, other):
        return other

    def getChild(self, path, request):
        d = self.loadModel(path, request)
        templateFile = (self.templateFile or self.__class__.__name__+'.html')
        d.addCallback(
            lambda result: self.parent.makeView(self.modelClass(result),
                                                templateFile, 1))
        return util.DeferredResource(d)

    def loadModelNow(self, path, request):
        """Override this rather than loadModel if your model-loading is
        synchronous.
        """
        raise NotImplementedError("%s.loadModelNow" % (reflect.qual(self.__class__)))

    def loadModel(self, path, request):
        """Load a model, for the given path and request.

        @rtype: L{Deferred}
        """
        from twisted.internet.defer import execute
        return execute(self.loadModelNow, path, request)


from twisted.web import microdom
from twisted.web import domhelpers

class TapestryView(View):
    tapestry = None
    parentCount = 0
    def lookupTemplate(self, request):
        fullFile = os.path.join(self.templateDirectory, self.templateFile)
        document = microdom.parse(open(fullFile))
        if self.tapestry:
            return self.tapestry.templateMutate(document, self.parentCount)
        return document

class Tapestry(Resource):
    """
    I am a top-level aggregation of Woven objects: a full `site' or
    `application'.
    """
    viewFactory = TapestryView
    def __init__(self, templateDirectory, viewFactory=None, metaTemplate=None):
        """
        Create a tapestry with a specified template directory.
        """
        Resource.__init__(self)
        self.templateDirectory = templateDirectory
        if viewFactory is not None:
            self.viewFactory = viewFactory
        if metaTemplate:
            self.metaTemplate = microdom.parse(open(
                os.path.join(templateDirectory, metaTemplate)))
        else:
            self.metaTemplate = None

    def templateMutate(self, document, parentCount=0):
        if self.metaTemplate:
            newDoc = self.metaTemplate.cloneNode(1)
            if parentCount:
                dotdot = parentCount * '../'
                for ddname in 'href', 'src', 'action':
                    for node in domhelpers.findElementsWithAttribute(newDoc, ddname):
                        node.setAttribute(ddname, dotdot + node.getAttribute(ddname))
            ttl = domhelpers.findNodesNamed(newDoc, "title")[0]
            ttl2 = domhelpers.findNodesNamed(document, "title")[0]
            ttl.childNodes[:] = []
            for n in ttl2.childNodes:
                ttl.appendChild(n)
            body = domhelpers.findElementsWithAttribute(newDoc, "class", "__BODY__")[0]
            body2 = domhelpers.findNodesNamed(document, "body")[0]
            ndx = body.parentNode.childNodes.index(body)
            body.parentNode.childNodes[ndx:ndx+1] = body2.childNodes
            for n in body2.childNodes:
                n.parentNode = body.parentNode
            f = open("garbage.html", "wb")
            f.write(newDoc.toprettyxml())
            return newDoc
        return document

    def makeView(self, model, name, parentCount=0):
        v = self.viewFactory(model, name)
        v.parentCount = parentCount
        v.templateDirectory = self.templateDirectory
        v.tapestry = self
        v.importViewLibrary(self)
        return v

    def getSubview(self, request, node, model, viewName):
        mod = sys.modules[self.__class__.__module__]
        # print "I'm getting a subview", mod, viewName

        # try just the name
        vm = getattr(mod, viewName, None)
        if vm:
            return vm(model)

        # try the name + a V
        vn2 = "V"+viewName.capitalize()
        vm = getattr(mod, vn2, None)
        if vm:
            return vm(model)

        vm = getattr(self, 'wvfactory_'+viewName, None)
        if vm:
            return vm(request, node, model)

    def render(self, request):
        return redirectTo(addSlash(request), request)

    def getChild(self, path, request):
        if path == '': path = 'index'
        path = path.replace(".","_")
        cm = getattr(self, "wchild_"+path, None)
        if cm:
            p = cm(request)
            if isinstance(p, Deferred):
                return util.DeferredResource(p)
            adapter = components.getAdapter(p, IResource, None)
            if adapter is not None:
                return adapter
        # maybe we want direct support for ModelLoader?
        # cl = getattr(self, "wload_"+path, None) #???
        return Resource.getChild(self, path, request)