File Coverage

blib/lib/Pixie/Complicity.pm
Criterion Covered Total %
statement 30 30 100.0
branch 5 6 83.3
condition 1 3 33.3
subroutine 16 16 100.0
pod 0 10 0.0
total 52 65 80.0


line stmt bran cond sub pod time code
1             package Pixie::Complicity;
2              
3             our $VERSION = '2.08_02';
4              
5             $Pixie::Complicity::Loaded++;
6              
7             =head1 NAME
8              
9             Pixie::Complicity - making things play well with pixie
10              
11             =head1 DESCRIPTION
12              
13             Complicity: <>
14              
15             =head2 Rationale
16              
17             For many objects, Pixie can and does store the object transparently
18             with no assistance from the object's class. However, sometimes that's
19             just not the case; most commonly in the case of classes that are
20             implemented using XS, and which store their data off in some C
21             structure that's inaccessible from Perl. Getting at such information
22             without the complicity of the class in question would require Pixie to
23             be, near as dammit, telepathic. And that's not going to happen any
24             time soon.
25              
26             So, we provide a set of methods in UNIVERSAL, which are used by Pixie
27             in the process of storing and fetching objects. All you have to do is
28             override a few of them in the class in question. (Remember, even if
29             you're using a class from CPAN, the class's symbol table is always
30             open, so you can cheat and add the helper methods anyway, we've chosen
31             a method namespace (all methods begin with px_) which we hope doesn't
32             clash with any classes that are out there, in the wild.
33              
34             =head2 Example
35              
36             Consider the C class. It's a very lovely class,
37             implementing a delightfully fast set, with all the set operations
38             you'd expect. However, in order to get the speed, it's been
39             implemented using XS, and the Data::Dumper visible part of it is
40             simply a scalar reference. So, if we want to use Set::Object in our
41             project (and we do), we need to make it complicit with Pixie.
42              
43             So, first we make sure that Pixie knows it's storable:
44              
45             sub Set::Object::px_is_storable { 1 }
46              
47             Then we think about how we're going to render the thing storable. The
48             only important thing about a set, for our purposes, is the list of its
49             members (and what do you know, Set::Object provides a C
50             method to get at that). We'll press the 'memento' pattern into
51             use. The idea is that we create a memento object which will store
52             enough information about an object for that object to be recreated
53             later. We set up Set::Object's C method to create that
54             memento:
55              
56             sub Set::Object::px_freeze {
57             my $self = shift;
58             return bless [ $self->members ], 'Memento::Set::Object';
59             }
60              
61             Easy. For our next trick, we need to provide some way for a memento to
62             be turned back into an object. Pixie guarantees to call C on
63             every object that it retrieves from the data store, so, all we have to
64             do is implement an appropriate C method I
65             class>.
66              
67             sub Memento::Set::Object::px_thaw {
68             my $self = shift;
69             return Set::Object->new(@$self);
70             }
71              
72             And, as if by magic, Set::Objects can now be happily persisted within
73             your Pixie.
74              
75             =head2 The Complicit Methods
76              
77             Pixie puts a lot of methods into UNIVERSAL, because that's where the
78             behaviour makes the most sense. Some of these methods are useful to
79             override when you need to help Pixie out with object storage; others
80             are useful when you're writing the tools that I Pixie (but we
81             haven't actually added many of those yet) and still others are almost
82             certainly never going to be overridden by client code, but we'll
83             document them just in case. We start with the 'storage helper' methods
84             that you are most likely to override:
85              
86             =over 4
87              
88             =item px_is_storable
89              
90             A boolean method. By default, Pixie thinks only HASH and ARRAY based
91             objects are storable. If you have a class that you want to make
92             persistent, and it doesn't use one of these representations, then just
93             add C to your class definition.
94              
95             =item px_freeze
96              
97             Called by Pixie on every object that it stores, C transforms
98             an object into something a little more... storable. Remember,
99             px_freeze operates on the 'real' object, not a copy. Generally you
100             should create a new object in some memento class, dump the storable
101             state into it and return the memento. (Of course, if px_thaw just gets
102             rid of some cached computations, you might prefer to operate directly
103             on the object).
104              
105             =item px_thaw
106              
107             Called by Pixie on every object that it retrieves from the store. Use
108             this to turn memento objects back into the real thing.
109              
110             NB: If your C blesses an object into a seperate memento class
111             then remember to implement C in the memento class, not the
112             source class.
113              
114             =item px_is_immediate
115              
116             Another boolean. Used by Pixie to know whether an object in this class
117             should be immediately fetched in cases where Pixie would normally use
118             a Pixie::Proxy object to provide deferred loading. You generally want
119             to use this for objects that get accessed directly (you naughty
120             encapsulation violator you), because a L only fetches the
121             real thing when it notices a method call to the object.
122              
123             =item px_as_rawstruct
124              
125             Returns an unblessed HASH/ARRAY/SCALAR ref which is a shallow clone of
126             the object in question.
127              
128             Sometimes you can get away without having to write C and
129             C. Say you have a hash based object, and some of its keys
130             are the cached (large) results of an expensive computation, which can
131             be entirely derived from the 'real' instance variables. So, to strip
132             those out of the stored object, you could do the following:
133              
134             sub px_as_rawstruct {
135             my $self = shift;
136             {@$self{grep !/^cached_/, keys %$self}}
137             }
138              
139             Aren't hash slices lovely?
140              
141             =item px_empty_new
142              
143             Class method. Returns an empty object in the given class. The default
144             implementation of this does C<$class-Enew()>. We do this so that the
145             class can 'know about' its instance (some classes like to initialize
146             various static variables etc...) but, if your class's 'new' method
147             doesn't cope with an empty argument list, you could override this
148             method. (I'm thinking of adding a 'px_post_populating_hook' method,
149             which would be called after pixie has populated an object. Useful for
150             those classes whose 'new' methods require arguments and then call an
151             init method to set up stuff based on the instance variables...)
152              
153             =back
154              
155             =cut
156              
157             package UNIVERSAL;
158              
159 23     23   30263 use Scalar::Util qw/ blessed weaken reftype isweak /;
  23         57  
  23         1735  
160 23     23   122 use strict;
  23         43  
  23         11625  
161              
162             sub px_is_storable {
163 30     30 0 1096 my $self = shift;
164              
165 30         652 reftype($self) =~ /^(?:HASH|ARRAY)/;
166             }
167              
168             sub px_class {
169 1     1 0 943 my $class = shift;
170 1   33     10 return ref($class) || $class
171             }
172              
173 2     2 0 1124 sub px_oid { $_[0]->PIXIE::oid };
174              
175 25     25 0 1533 sub px_freeze { shift }
176 2     2 0 1107 sub px_thaw { shift }
177 2     2 0 1192 sub px_is_immediate { }
178 1     1 0 1000 sub px_in_rootset { 1 }
179              
180             sub px_as_rawstruct {
181 30     30 0 6805 my $self = shift;
182 30         118 my $type = reftype($self);
183              
184 30 100       124 if ($type eq 'HASH') {
    100          
    50          
185 25         279 return { %$self };
186             }
187             elsif ($type eq 'ARRAY') {
188 4         43 return [ @$self ];
189             }
190             elsif ($type eq 'SCALAR') {
191 1         3 my $scalar = $$self;
192 1         5 return \$scalar;
193             }
194             }
195              
196             sub px_empty_new {
197 3     3 0 1677 my $class = shift;
198 3         15 $class->new;
199             }
200              
201             sub px_do_final_restoration {
202 2     2 0 2715 my $self = shift;
203 2         14 Pixie->get_the_current_pixie->make_new_object($self, ref($self));
204             }
205              
206              
207             # 'Internal' methods
208              
209              
210 1     1   1407 sub _px_extraction_freeze { Pixie->get_the_current_pixie->extraction_freeze(@_) }
211 1     1   1356 sub _px_extraction_thaw { Pixie->get_the_current_pixie->extraction_thaw(@_) }
212 29     29   3124 sub _px_insertion_freeze { Pixie->get_the_current_pixie->insertion_freeze(@_) }
213 20     20   1427 sub _px_insertion_thaw { Pixie->get_the_current_pixie->insertion_thaw(@_) }
214              
215             1;