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