line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#{{{ POD |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Class::Entangle - Functions to entangle an object. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 DESCRIPTION |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Class::Entangle is names after Quantum Entanglement, which is where 2 |
12
|
|
|
|
|
|
|
partacles are entangled in such a way that events occuring to one occur to the |
13
|
|
|
|
|
|
|
other as well, even if they are seperated by great distance. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Using Class::Entangle you can pull an 'entanglement' from a class. This |
16
|
|
|
|
|
|
|
entanglement contains a list of class properties, which includes subroutines, |
17
|
|
|
|
|
|
|
variables, and handles. This entanglement can be used to create entanglers, |
18
|
|
|
|
|
|
|
which in turn can be used to entangle a class. |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
An entangler is a new class definition that contains definitions for subroutines, |
21
|
|
|
|
|
|
|
variables, and handles that match another classes. When you define an entangler |
22
|
|
|
|
|
|
|
you tell it how you want it to entangle each type. Subroutines are defined by |
23
|
|
|
|
|
|
|
providing a callback routine. Variables and handers are defined using tie. |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Once you have an entangler for a class you can use that class as you would your |
26
|
|
|
|
|
|
|
original class. If the class is an object defenition then you can use the |
27
|
|
|
|
|
|
|
entangle() function to create an instance of the entangler against an existing |
28
|
|
|
|
|
|
|
object. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Note: You probably don't want to use construction methods through the entangler |
31
|
|
|
|
|
|
|
class, it will return whatever the constructor for the original class returns. |
32
|
|
|
|
|
|
|
This will return a new instance of the original class. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 SYNOPSIS |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#!/usr/bin/perl |
37
|
|
|
|
|
|
|
use strict; |
38
|
|
|
|
|
|
|
use warnings; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
use Class::Entangle; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# First define a class to entangle, as well as a Tie::Scalar class for |
43
|
|
|
|
|
|
|
# handling the scalars. |
44
|
|
|
|
|
|
|
{ |
45
|
|
|
|
|
|
|
package MyClass; |
46
|
|
|
|
|
|
|
use strict; |
47
|
|
|
|
|
|
|
use warnings; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub new { |
50
|
|
|
|
|
|
|
my $class = shift; |
51
|
|
|
|
|
|
|
$class = ref $class if ref $class; |
52
|
|
|
|
|
|
|
return bless {}, $class; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub subA { |
56
|
|
|
|
|
|
|
my $self = shift; |
57
|
|
|
|
|
|
|
my ( $in ) = @_; |
58
|
|
|
|
|
|
|
return "subA($in)"; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
our $scalarA = "scalar"; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
{ |
65
|
|
|
|
|
|
|
package MyClass::TieScalar; |
66
|
|
|
|
|
|
|
require Tie::Scalar; |
67
|
|
|
|
|
|
|
our @ISA = qw(Tie::Scalar); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub TIESCALAR { |
70
|
|
|
|
|
|
|
my $class = shift; |
71
|
|
|
|
|
|
|
my ( $varname ) = @_; |
72
|
|
|
|
|
|
|
bless( { varname => $varname }, $class ); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub FETCH { |
76
|
|
|
|
|
|
|
my $self = shift; |
77
|
|
|
|
|
|
|
my $varname = $self->{ varname }; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
no strict 'refs'; |
80
|
|
|
|
|
|
|
return ${ "MyClass\::$varname" }; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub STORE { |
84
|
|
|
|
|
|
|
my $self = shift; |
85
|
|
|
|
|
|
|
my $varname = $self->{ varname }; |
86
|
|
|
|
|
|
|
my ( $value ) = @_; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
no strict 'refs'; |
89
|
|
|
|
|
|
|
return ${ "MyClass\::$varname" } = $value; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $one = MyClass->new; |
94
|
|
|
|
|
|
|
my $entanglement = Class::Entangle::entanglement( $one ); |
95
|
|
|
|
|
|
|
# $entangler will contain the class name of the entangler. |
96
|
|
|
|
|
|
|
my $entangler = Class::Entangle::entangler( |
97
|
|
|
|
|
|
|
CODE => sub { |
98
|
|
|
|
|
|
|
my $entangle = shift; |
99
|
|
|
|
|
|
|
my $subname = shift; |
100
|
|
|
|
|
|
|
if ( $entangle ) { |
101
|
|
|
|
|
|
|
return $entangle->{ entangled }->$subname( @_ ); |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
else { |
104
|
|
|
|
|
|
|
return &{ "Test\::TestClass\::$subname" }->( @_ ); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
}, |
107
|
|
|
|
|
|
|
SCALAR => 'MyClass::TieScalar', |
108
|
|
|
|
|
|
|
); |
109
|
|
|
|
|
|
|
$entangle = Class::Entangle::entangle( |
110
|
|
|
|
|
|
|
$entangler, |
111
|
|
|
|
|
|
|
entangled => $test, |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# prints: 'subA(a)' |
115
|
|
|
|
|
|
|
print $entangle->subA( 'a' ); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# prints: 'HaHa, I am messing with $MyClass::scalarA!' |
118
|
|
|
|
|
|
|
no strict 'refs'; |
119
|
|
|
|
|
|
|
${ "$entangler\::scalarA" } = 'HaHa, I am messing with MyClass::scalarA'; |
120
|
|
|
|
|
|
|
print ${ "$entangler\::scalarA" } |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 EXPORTED FUNCTIONS |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=over 4 |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=cut |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
#}}} |
129
|
|
|
|
|
|
|
package Class::Entangle; |
130
|
1
|
|
|
1
|
|
4390
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
131
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
34
|
|
132
|
|
|
|
|
|
|
|
133
|
1
|
|
|
1
|
|
13
|
use Exporter 'import'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
133
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
our @EXPORT = qw/ entanglement entangler entangle /; |
136
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
my %DEFINED = ( |
139
|
|
|
|
|
|
|
code => {}, |
140
|
|
|
|
|
|
|
loaded => {}, |
141
|
|
|
|
|
|
|
used => {}, |
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item entanglement( $object ) |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Only argument is the object to create an entanglement for. Returns an |
147
|
|
|
|
|
|
|
entanglement. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Note: Currently entanglements are simple hashes, this is subject to change, |
150
|
|
|
|
|
|
|
always get the entanglement returned by this function and use it directly, do |
151
|
|
|
|
|
|
|
not write code that treats it directly as a hash. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=cut |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub entanglement { |
156
|
1
|
|
|
1
|
1
|
2309
|
my ( $object ) = @_; |
157
|
1
|
|
33
|
|
|
4
|
my $class = ref $object || $object; |
158
|
|
|
|
|
|
|
|
159
|
1
|
50
|
|
|
|
3
|
unless ( $DEFINED{ $class }) { |
160
|
1
|
|
|
|
|
1
|
my %defs; |
161
|
|
|
|
|
|
|
|
162
|
1
|
|
|
1
|
|
6
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
209
|
|
163
|
1
|
|
|
|
|
1
|
for my $item ( $class, @{ "$class\::ISA" }) { |
|
1
|
|
|
|
|
5
|
|
164
|
2
|
|
|
|
|
4
|
my %set = _class_properties( $item ); |
165
|
2
|
|
|
|
|
8
|
while ( my ( $prop, $value ) = each %set ) { |
166
|
7
|
100
|
|
|
|
7
|
$defs{ $prop } = { %{ $defs{ $prop } || {}}, %$value }; |
|
7
|
|
|
|
|
48
|
|
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
1
|
|
|
|
|
3
|
for my $key ( keys %defs ) { |
171
|
5
|
|
|
|
|
4
|
$defs{ $key } = [ keys %{ $defs{ $key } }]; |
|
5
|
|
|
|
|
14
|
|
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
1
|
|
|
|
|
5
|
$DEFINED{ $class } = { class => $class, %defs }; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
1
|
|
|
|
|
482
|
return $DEFINED{ $class }; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _class_properties { |
181
|
2
|
|
|
2
|
|
4
|
my ( $class ) = @_; |
182
|
1
|
|
|
1
|
|
4
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
510
|
|
183
|
|
|
|
|
|
|
|
184
|
2
|
|
|
|
|
2
|
my %defs; |
185
|
2
|
|
|
|
|
3
|
while( my ( $name, $ref ) = each %{ $class . "::" }) { |
|
20
|
|
|
|
|
84
|
|
186
|
18
|
100
|
|
|
|
39
|
next if $name =~ m/::$/; |
187
|
17
|
100
|
|
|
|
16
|
next if grep { $_ eq $name } (qw/BEGIN END/); |
|
34
|
|
|
|
|
74
|
|
188
|
|
|
|
|
|
|
|
189
|
14
|
100
|
|
|
|
13
|
$defs{ CODE }->{ $name }++ if defined &{$ref}; |
|
14
|
|
|
|
|
32
|
|
190
|
14
|
100
|
|
|
|
15
|
$defs{ HASH }->{ $name }++ if defined %{$ref}; |
|
14
|
|
|
|
|
31
|
|
191
|
14
|
100
|
|
|
|
12
|
$defs{ ARRAY }->{ $name }++ if defined @{$ref}; |
|
14
|
|
|
|
|
32
|
|
192
|
14
|
100
|
|
|
|
13
|
$defs{ SCALAR }->{ $name }++ if defined ${$ref}; |
|
14
|
|
|
|
|
29
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# IO have no sigil to use, have to dig into the glob. |
195
|
14
|
100
|
|
|
|
12
|
$defs{ IO }->{ $name }++ if defined *{$ref}{IO}; |
|
14
|
|
|
|
|
49
|
|
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
2
|
|
|
|
|
9
|
return %defs; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item entangler( $entanglement, %params ) |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
The first arguement should be an entanglement as returned by entanglement(). |
204
|
|
|
|
|
|
|
All additional params should be key => value pairs. All the following are acceptible: |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=over 4 |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
variation => 'default' - When you create an entangler it creates a class |
209
|
|
|
|
|
|
|
definition. This defenition contains the variation name you pass in here. If |
210
|
|
|
|
|
|
|
you don't pass in a variation 'default' is used. You can only define an |
211
|
|
|
|
|
|
|
entangler class against an entanglement once for each variation. If you want to |
212
|
|
|
|
|
|
|
have a different entangler for the same object you must provide a variation |
213
|
|
|
|
|
|
|
name. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
CODE => sub { ... } - Define the subroutine callback to use on all class |
216
|
|
|
|
|
|
|
subroutines. The first 2 parameters should always be shifted off before passing |
217
|
|
|
|
|
|
|
@_ to the entangled class. The first is either the entagle object if the sub |
218
|
|
|
|
|
|
|
was called as an object method, the entangler class if it was called as a class |
219
|
|
|
|
|
|
|
method, or undef if not called as a method. The second parameter is always the |
220
|
|
|
|
|
|
|
name of the sub that was called. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
HASH, ARRAY, SCALAR, IO => 'Tie::MyTie' - Specify the class to tie the specific |
223
|
|
|
|
|
|
|
variable type to. If you are writing a Tie for a specific variable you should |
224
|
|
|
|
|
|
|
know that the call to Tie looks like this: |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# $ref is "$entangler\::$item" |
227
|
|
|
|
|
|
|
# $tieclass is the class you passed to 'HASH =>' |
228
|
|
|
|
|
|
|
# $item is the name of the variable. |
229
|
|
|
|
|
|
|
tie( %$ref, $tieclass, $item ); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=back 4 |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=cut |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub entangler { |
236
|
2
|
|
|
2
|
1
|
1551
|
my $entanglement = shift; |
237
|
2
|
100
|
|
|
|
12
|
my %params = (@_ > 1 ) ? @_ : ( CODE => shift( @_ )); |
238
|
|
|
|
|
|
|
|
239
|
2
|
|
|
|
|
3
|
my $class = $entanglement->{ class }; |
240
|
2
|
|
100
|
|
|
11
|
my $variation = delete( $params{ variation } ) || 'default'; |
241
|
|
|
|
|
|
|
|
242
|
2
|
|
|
|
|
6
|
my $defined = $DEFINED{ loaded }->{ $class }->{ $variation }; |
243
|
|
|
|
|
|
|
|
244
|
2
|
50
|
33
|
|
|
8
|
warn( |
245
|
|
|
|
|
|
|
"Attempt to redefine '$class' variation '$variation'\n", |
246
|
|
|
|
|
|
|
"You can only define a variation once, if you are using a\n", |
247
|
|
|
|
|
|
|
"different subroutine callback, or different variable tie\n", |
248
|
|
|
|
|
|
|
"classes, then things will not behave as you expect.\n", |
249
|
|
|
|
|
|
|
) if ( $defined and keys %params ); |
250
|
|
|
|
|
|
|
|
251
|
2
|
50
|
|
|
|
9
|
_define_entangler( $entanglement, $variation, %params ) unless ( $defined ); |
252
|
|
|
|
|
|
|
|
253
|
2
|
|
|
|
|
8
|
return $DEFINED{ loaded }->{ $class }->{ $variation }; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub _define_entangler { |
257
|
2
|
|
|
2
|
|
4
|
my ( $entanglement, $variation, %params ) = @_; |
258
|
2
|
|
|
|
|
3
|
my $class = $entanglement->{ class }; |
259
|
2
|
|
33
|
|
|
8
|
my $entangleclass = ( delete $params{ entangleclass } ) || |
260
|
|
|
|
|
|
|
"Class\::_Entangle\::$class\::$variation"; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
warn( |
263
|
|
|
|
|
|
|
"Warning, redefining entangleclass: $entangleclass\n", |
264
|
|
|
|
|
|
|
"This is almost certainly not what you want.\n" |
265
|
2
|
50
|
|
|
|
7
|
) if $DEFINED{ used }->{ $entangleclass }; |
266
|
|
|
|
|
|
|
|
267
|
2
|
|
|
|
|
5
|
$DEFINED{ used }->{ $entangleclass }++; |
268
|
|
|
|
|
|
|
|
269
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
290
|
|
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Take care of the subs |
272
|
2
|
50
|
|
|
|
6
|
if( my $ref = delete $params{ 'CODE' }) { |
273
|
2
|
|
|
|
|
2
|
for my $sub ( @{ $entanglement->{ 'CODE' }}) { |
|
2
|
|
|
|
|
5
|
|
274
|
8
|
|
|
|
|
51
|
*{ $entangleclass . '::' . $sub } = sub { |
275
|
8
|
|
|
8
|
|
4480
|
my $first = $_[0]; |
276
|
8
|
|
33
|
|
|
21
|
$first = ref $first || $first; |
277
|
8
|
50
|
|
|
|
19
|
my $self = shift if $first eq $entangleclass; |
278
|
8
|
|
50
|
|
|
36
|
$ref->( $self || undef, $sub, @_ ); |
279
|
|
|
|
|
|
|
} |
280
|
8
|
|
|
|
|
46
|
} |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# Take care of the variables and handles |
284
|
2
|
|
|
|
|
8
|
while ( my ( $type, $tieclass ) = each %params ) { |
285
|
1
|
|
|
|
|
2
|
for my $item ( @{ $entanglement->{ $type }}) { |
|
1
|
|
|
|
|
2
|
|
286
|
4
|
|
|
|
|
7
|
my $ref = $entangleclass . '::' . $item; |
287
|
4
|
50
|
|
|
|
11
|
tie( %$ref, $tieclass, $item ) if $type eq 'HASH'; |
288
|
4
|
50
|
|
|
|
8
|
tie( @$ref, $tieclass, $item ) if $type eq 'ARRAY'; |
289
|
4
|
50
|
|
|
|
40
|
tie( $$ref, $tieclass, $item ) if $type eq 'SCALAR'; |
290
|
4
|
50
|
|
|
|
34
|
tie( *{ $ref }, $tieclass, $item ) if $type eq 'IO'; |
|
0
|
|
|
|
|
0
|
|
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
2
|
|
|
|
|
7
|
$DEFINED{ loaded }->{ $class }->{ $variation } = $entangleclass; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=item entangle( $entangler, %object_params ) |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
First param should be an entangler class as returned by entangler(). |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
All additional parameters will be directly put into the hashref that is blessed |
302
|
|
|
|
|
|
|
as an object of type $entangler. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=cut |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub entangle { |
307
|
2
|
|
|
2
|
1
|
8
|
my $entangler = shift; |
308
|
2
|
|
|
|
|
9
|
return bless( { @_ }, $entangler ); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
1; |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
#{{{ End Pod |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
__END__ |