A real world example of Adaption

Imagine that you are about to give a presentation at a meeting. You’ve brought your laptop, and the room has a projector and a cable to plug your laptop into the projector. However, the cable you brought won’t plug into the projector because your cable only interfaces with a DVI port, and the projector only interfaces with a VGA port.

So you ask of the room, “does anyone have a DVI to VGA adapter?”

An adapter? Let’s state the problem of the projector and the laptop in Grok. Of course it’s impossible to implement a hardware problem in software (duh!) so for this example we won’t have any implementation details:

import grok
from zope.interface import Interface

class IVGAPort(Interface):
    "Video connection using VGA"

    def connectToVGA(cable):
        "Connect to a VGA video cable"

class IDVIPort(Interface):
    "Video connection using DVI"

    def connectToDVI(cable):
        "Connect to a DVI video cable"

class DVIToVGAAdapter(grok.Adapter):
    "Use your DVI laptop with a VGA projector"
    grok.adapts(IDVIPort)
    grok.provides(IVGAPort)

    def connectToVGA(cable):
        # self.context will be a reference to the laptop object

def plugLaptopIntoProjector(laptop, projector):
    "Display your presentation on the wall"

    vga_capable = IVGAPort(laptop, None)
    if vga_capable is None:
        print "Uh oh, looks like you forget to bring an adapter."
    else:
        vga_capable.connectToVGA(projector)

A common idiom for asking the Zope 3 component architecture to perform adaptation is to instantiate a new Interface with the object that you wish to adapt as an argument. We did this in the ‘plugLaptopIntoProjecter’ function with the line:

vga_capable = IVGAPort(laptop, None)

The vga_capable variable is now an object that provides the IVGAPort. If the laptop already implements IVGAPort, then this will be the laptop object itself. If the laptop provides IDVIPort, then the DVIToVGAAdapter will be used to extend IDVIPort to provide IVGAPort. The second optional argument (None) will be returned if the laptop doesn’t provide an IVGAPort and no suitable adapter can be found. If adaptation was successful, you shouldn’t care if you have a Laptop object or a DVIToVGAAdapter object returned - your only concern is that you have an object that provides the requested IVGAPort interface that provides the necessary connectToVGA(projector) method.

The DVI to VGA Adapter

Let’s review the DVIToVGAAdapter class that we created.

class DVIToVGAAdapter(grok.Adapter):
    "Use your DVI laptop with a VGA projector"
    grok.adapts(IDVIPort)
    grok.provides(IVGAPort)

    def connectToVGA(cable):
        # self.context will be a reference to the laptop object

When you inhert from grok.Adapter, Grok will do two things. One is to register your Adapter with the Zope 3 Component Architecture so that it can look-up the right adapter for the requested interface. The second is to reference the object that is being adapted to an attribute named context. In Grok, the Adapter base class that you inherit from is very simple and looks like this:

class Adapter(object):

    def __init__(self, context):
        self.context = context

This is just a convention used to save some typing. If you wish, you may supply your own constructor to call the object being adapted something specific to your adapter. For example, in the above example, we could add the following constructor to our DVIToVGAAdapter Class:

def __init__(self, laptop):
    self.laptop = laptop

Adaptation is about requiring an object which provides one interface, and extending it so that you get a new interface. We declare that we require an object that provides an IDVIPort interface - this is declared with the directive grok.adapts(IDVIPort). We are extending the IDVIPort interface to provide a new interface, IVGAPort - this is declared with the directive grok.provides(IVGAPort). Finally, the adapter is responsible for fulfilling the interface that it declares it implements. This implementation will typically use self.context attribute - which is the object that is being adapted.

It is possible to use either grok.provides(IVGAPort) or grok.implements(IVGAPort) directives for the DVIToVGAAdapter. In a more complex example it’s possible that an adapter provides multiple interfaces, but only one interface is meant to be used for the purposes of adaptation. The grok.provides directive explicitly declares the interface to be used for adaptation, however if an Adapter only declares that it implements a single