NAME

Pixie::Complicity - making things play well with pixie

DESCRIPTION

Complicity: <<defintion>>

Rationale

For many objects, Pixie can and does store the object transparently with no assistance from the object's class. However, sometimes that's just not the case; most commonly in the case of classes that are implemented using XS, and which store their data off in some C structure that's inaccessible from Perl. Getting at such information without the complicity of the class in question would require Pixie to be, near as dammit, telepathic. And that's not going to happen any time soon.

So, we provide a set of methods in UNIVERSAL, which are used by Pixie in the process of storing and fetching objects. All you have to do is override a few of them in the class in question. (Remember, even if you're using a class from CPAN, the class's symbol table is always open, so you can cheat and add the helper methods anyway, we've chosen a method namespace (all methods begin with px_) which we hope doesn't clash with any classes that are out there, in the wild.

Example

Consider the Set::Object class. It's a very lovely class, implementing a delightfully fast set, with all the set operations you'd expect. However, in order to get the speed, it's been implemented using XS, and the Data::Dumper visible part of it is simply a scalar reference. So, if we want to use Set::Object in our project (and we do), we need to make it complicit with Pixie.

So, first we make sure that Pixie knows it's storable:

sub Set::Object::px_is_storable { 1 }

Then we think about how we're going to render the thing storable. The only important thing about a set, for our purposes, is the list of its members (and what do you know, Set::Object provides a members method to get at that). We'll press the 'memento' pattern into use. The idea is that we create a memento object which will store enough information about an object for that object to be recreated later. We set up Set::Object's px_freeze method to create that memento:

sub Set::Object::px_freeze {
    my $self = shift;
    return bless [ $self->members ], 'Memento::Set::Object';
}

Easy. For our next trick, we need to provide some way for a memento to be turned back into an object. Pixie guarantees to call px_thaw on every object that it retrieves from the data store, so, all we have to do is implement an appropriate px_thaw method in the memento class.

sub Memento::Set::Object::px_thaw {
    my $self = shift;
    return Set::Object->new(@$self);
}

And, as if by magic, Set::Objects can now be happily persisted within your Pixie.

The Complicit Methods

Pixie puts a lot of methods into UNIVERSAL, because that's where the behaviour makes the most sense. Some of these methods are useful to override when you need to help Pixie out with object storage; others are useful when you're writing the tools that use Pixie (but we haven't actually added many of those yet) and still others are almost certainly never going to be overridden by client code, but we'll document them just in case. We start with the 'storage helper' methods that you are most likely to override:

px_is_storable

A boolean method. By default, Pixie thinks only HASH and ARRAY based objects are storable. If you have a class that you want to make persistent, and it doesn't use one of these representations, then just add sub px_is_storable { 1 } to your class definition.

px_freeze

Called by Pixie on every object that it stores, px_freeze transforms an object into something a little more... storable. Remember, px_freeze operates on the 'real' object, not a copy. Generally you should create a new object in some memento class, dump the storable state into it and return the memento. (Of course, if px_thaw just gets rid of some cached computations, you might prefer to operate directly on the object).

px_thaw

Called by Pixie on every object that it retrieves from the store. Use this to turn memento objects back into the real thing.

NB: If your px_freeze blesses an object into a seperate memento class then remember to implement px_freeze in the memento class, not the source class.

px_is_immediate

Another boolean. Used by Pixie to know whether an object in this class should be immediately fetched in cases where Pixie would normally use a Pixie::Proxy object to provide deferred loading. You generally want to use this for objects that get accessed directly (you naughty encapsulation violator you), because a Pixie::Proxy only fetches the real thing when it notices a method call to the object.

px_as_rawstruct

Returns an unblessed HASH/ARRAY/SCALAR ref which is a shallow clone of the object in question.

Sometimes you can get away without having to write px_freeze and px_thaw. Say you have a hash based object, and some of its keys are the cached (large) results of an expensive computation, which can be entirely derived from the 'real' instance variables. So, to strip those out of the stored object, you could do the following:

sub px_as_rawstruct {
    my $self = shift;
    {@$self{grep !/^cached_/, keys %$self}}
}

Aren't hash slices lovely?

px_empty_new

Class method. Returns an empty object in the given class. The default implementation of this does $class->new(). We do this so that the class can 'know about' its instance (some classes like to initialize various static variables etc...) but, if your class's 'new' method doesn't cope with an empty argument list, you could override this method. (I'm thinking of adding a 'px_post_populating_hook' method, which would be called after pixie has populated an object. Useful for those classes whose 'new' methods require arguments and then call an init method to set up stuff based on the instance variables...)