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