Ext.Direct remoting in Zope

With ExtJS 3.0 came Ext.Direct, an excellent library for remoting server-side methods to the client side. We had already made the decision to switch to Ext with the revamped Zenoss UI, so I'd been working with Ext quite a lot; in the upcoming 2.5 release, there's a brand-new event console that makes heavy use of Ext.Direct. After learning its ins and outs with all that work, I decided to write a Python version of the server-side component to make it easier to use; I then went further and created a Zope 2 and 3 compatible component that makes it trivial. extdirect is the result. Here's some brief instruction on how to use it.


In a nutshell, Ext.Direct allows one to create a client-side API that maps to a server-side API. One creates a "router" on the server, available at a given URL, that can accept requests specifying a method name, route them to the correct method, and return the result. As a brief, non-working example (ignoring framework), if the server-side router provided some methods:

class Router(object):
    def uppercase(self, word):
        return word.upper()
    def lowercase(self, word):
        return word.lower()

And that class were available at some URL, Ext.Direct lets you define a client-side provider that allows you to call those methods, without mapping each method to a separate URL:

function callback(result) {
    ... do something ...
}
Remote.Router.uppercase({word:'ext.direct'}, callback);
Remote.Router.lowercase({word:'EXT.DIRECT'}, callback);

Ideally, the server would be able to create the provider definition by inspecting the class, generating JavaScript that could be inserted into a template. extdirect provides a router base class and a function that can generate its provider definition.

I'll focus on the Zope-specific implementation, because it makes it so easy, but the base class can be used in any framework. I'm thinking I'll add a Django implementation next.

So, here's how to hook up a router in Zope, assuming extdirect is installed and available:
  1. Define your class. DirectRouter is a subclass of BrowserView, so use self.context and self.request at will. Let's say this goes into utils.py:


    from extdirect.zope import DirectRouter
    
    class Utils(DirectRouter):
        
        def contextid(self):
            return self.context.id
    
        def uppercase(self, word):
            return word.upper()
    

    Make sure to return something JSON-serializable.


  2. Register the class as an Ext.Direct router in ZCML. This step both registers a view for the router and registers a viewlet containing the JavaScript required to create the client-side provider (against the included extdirect viewlet manager):

    <configure xmlns="http://namespaces.zope.org/browser">
    
         <include package="extdirect.zope" file="meta.zcml"/>
    
           <directRouter 
               class=".utils.Utils" 
               name="remote_utils" 
               namespace="Remote"
               />
    
    </configure>
    The class, of course, refers to the DirectRouter subclass you defined in step 1. The name is like a normal BrowserView name, and will be the URL at which your router will be available. The namespace is the namespace in the client at which the methods will be accessible, and can be longer (if you already have a namespace set up, you could do "Zenoss.server.remote" or whatever you like). This provider will be available at [namespace].[class name], so in this case, Remote.Utils.
  3. Include the viewlet manager in a page template. You have two options here. If you've already got Ext loaded through other means (say it's the framework you're using on the site), you can use:
    <tal:block tal:content="structure provider:extdirect"/>
    If you're not using Ext already, and don't want to, you can use a stripped-down version including only those components necessary for Ext.Direct, which is included in extdirect and registered as a resource and a viewlet:
    <tal:block tal:content="structure provider:extdirect+direct.js"/>
  4. Call methods at will. That's all. You can now call methods on that namespace and class:
    Remote.Utils.contextid({}, callback);
    Remote.Utils.uppercase({word:'A word'}, callback);
    
And that's all there is to it. It couldn't be simpler. Your router is available at ./remote_utils, but Ext.Direct handles all the communication so you don't ever really need to use it yourself. Further details of the base class are available at the project page on PyPI. Code is hosted on Google Code.

0 comments

Post a Comment