| 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 |
|
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; |