File Coverage

blib/lib/Class/IntrospectionMethods/Parent.pm
Criterion Covered Total %
statement 87 91 95.6
branch 22 30 73.3
condition n/a
subroutine 26 28 92.8
pod 2 4 50.0
total 137 153 89.5


line stmt bran cond sub pod time code
1             # $Author: domi $
2             # $Date: 2004/12/08 12:50:41 $
3             # $Name: $
4             # $Revision: 1.3 $
5              
6             package Class::IntrospectionMethods::Parent ;
7 16     16   60125 use strict ;
  16         30  
  16         602  
8 16     16   87 use warnings ;
  16         26  
  16         478  
9 16     16   220 use Carp ;
  16         23  
  16         1218  
10 16     16   981 use Storable qw/dclone/;
  16         3454  
  16         837  
11 16     16   91 use Data::Dumper ;
  16         32  
  16         1108  
12              
13             require Exporter;
14 16     16   86 use vars qw/$VERSION @ISA @EXPORT_OK $trace/ ;
  16         45  
  16         2318  
15             @ISA = qw(Exporter);
16             @EXPORT_OK = qw(set_parent_method_name graft_parent_method set_obsolete_behavior);
17              
18             $VERSION = sprintf "%d.%03d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
19              
20             $trace = 0;
21              
22 16     16   91 use vars qw( $VERSION );
  16         26  
  16         1381  
23              
24             =head1 NAME
25              
26             Class::IntrospectionMethods::Parent - Handles parent relationship for Class::IntrospectionMethods
27              
28             =head1 SYNOPSIS
29              
30             No synopsis. Directly used by Class::IntrospectionMethods
31              
32             =head1 DESCRIPTION
33              
34             This class handles parent relationship for Class::IntrospectionMethods.
35              
36             In other word, for any child object managed by
37             Class::IntrospectionMethods, it will :
38              
39             =over
40              
41             =item *
42              
43             Create a ParentInfo object that contains
44              
45             =over
46              
47             =item *
48              
49             the parent object ref (weakened by L C function)
50              
51             =item *
52              
53             The slot name containing the child
54              
55             =item *
56              
57             The index of the element containing the child if the slot is array or
58             hash based.
59              
60             =back
61              
62             =item *
63              
64             Install a function/method in child's class to retrieve the
65             ParentInfo object.
66              
67             =item *
68              
69             An attribute in child to store the ParentInfo's ref.
70              
71             =back
72              
73             By default, the name of the installed function and arribute is
74             C but this can be changed by calling
75             C.
76              
77             =cut
78              
79             my $obsolete_behavior = 'carp' ;
80             my $support_legacy = 0 ;
81              
82             sub warn_obsolete
83             {
84 39 50   39 0 185 return if $obsolete_behavior eq 'skip' ;
85 16     16   85 no strict 'refs';
  16         31  
  16         3993  
86 0         0 $obsolete_behavior->(@_) ;
87             }
88              
89             =head1 Exported functions
90              
91             =head2 set_parent_method_name( name )
92              
93             This function changes the name of the function and attribute names
94             installed by C. (C by default)
95              
96             =cut
97              
98             my $parent_method_name = 'cim_parent' ;
99             my $too_late_to_change_name = 0 ;
100              
101             sub set_parent_method_name
102             {
103 2 50   2 1 16 croak "set_parent_method_name must be called before graft_parent_method"
104             if $too_late_to_change_name ;
105 2         7 $parent_method_name = shift ;
106             }
107              
108             sub set_obsolete_behavior
109             {
110 16     16 0 907 $obsolete_behavior = shift;
111 16         54 $support_legacy = shift ;
112             }
113              
114             =head2 graft_parent_method( child, parent, slot, [index] )
115              
116             Creates the ParentInfo object, install the C function in
117             child's class, store the ParentInfo in child object, finally store
118             slot and index in ParentInfo object.
119              
120             =cut
121              
122             # this function is called anytime a child object is created
123             sub graft_parent_method
124             {
125 44     44 1 500 my ($child,$parent, $slot, $index) = @_ ;
126              
127 44         77 $too_late_to_change_name = 1;
128              
129 44 50       1369 croak "graft_parent_method error: cannot graft method if object is not based on HASH"
130             unless $child->isa('HASH') ;
131              
132 44         100 my $parent_class = ref($parent) ;
133              
134 44         102 my $subname = ref($child).'::'.$parent_method_name ;
135              
136 44 50       494 print "grafting child $subname with an accessor for parent $parent_class $parent\n".
    50          
    100          
137             (defined $slot ? "\tslot is $slot\n" : '') .
138             (defined $index ? "\tindex is $index\n" : '' )
139             if $trace ;
140              
141 16     16   379 no strict 'refs' ;
  16         34  
  16         7128  
142             *$subname = sub
143             {
144 31     31   1211 return shift -> {$parent_method_name} ;
145             }
146 44 100       377 unless $child -> can($parent_method_name) ;
147              
148 44         206 my $parent_obj = $child->{$parent_method_name} =
149             Class::IntrospectionMethods::ParentInfo
150             -> new( index_value => $index,
151             slot_name => $slot,
152             parent => $parent
153             ) ;
154              
155 44 100       128 if ($support_legacy)
156             {
157 42         219 tie $child->{CMM_SLOT_NAME} ,
158             'Class::IntrospectionMethods::ParentNameTie' ,
159             name => 'CMM_SLOT_NAME',
160             parent => $parent_obj , method => 'slot_name';
161              
162 42         181 tie $child->{CMM_INDEX_VALUE} ,
163             'Class::IntrospectionMethods::ParentNameTie',
164             name => 'CMM_INDEX_VALUE',
165             parent => $parent_obj, method => 'index_value' ;
166              
167 42         197 tie $child->{CMM_PARENT} ,
168             'Class::IntrospectionMethods::ParentNameTie',
169             name => 'CMM_PARENT',
170             parent => $parent_obj, method => 'parent' ;
171              
172 42         106 my $sub_slot_name = ref($child).'::CMM_SLOT_NAME' ;
173             *$sub_slot_name = sub
174             {
175 3     3   37 warn_obsolete ("CMM_SLOT_NAME method is deprecated") ;
176 3         37 my $po = shift ->$parent_method_name() ;
177 3 50       23 return defined $po ? $po->slot_name : undef;
178 42 100       271 } unless $child -> can($sub_slot_name) ;
179              
180 42         114 my $sub_index_name = ref($child).'::CMM_INDEX_VALUE' ;
181             *$sub_index_name = sub
182             {
183 3     3   24 warn_obsolete ("CMM_INDEX_VALUE method is deprecated") ;
184 3         16 my $po = shift ->$parent_method_name() ;
185 3 50       26 return defined $po ? $po->index_value :undef;
186 42 100       368 } unless $child -> can($sub_index_name) ;
187              
188 42         79 my $sub_parent = ref($child).'::CMM_PARENT' ;
189             *$sub_parent = sub
190             {
191 18     18   173 warn_obsolete ("CMM_PARENT method is deprecated") ;
192 18         59 my $po = shift ->$parent_method_name() ;
193 18 50       117 return defined $po ? ($po->parent(@_)) : (undef) ;
194 42 100       421 } unless $child -> can($sub_parent) ;
195             }
196             }
197              
198             =head1 ParentInfo class
199              
200             A ParentInfo object is created each time the C
201             function is called.
202              
203             When, needed, this object is retrieved by calling:
204              
205             $child->cim_parent
206              
207             The the following methods may be applied to retrive the informations
208             stored durung C call:
209              
210             =cut
211              
212             package Class::IntrospectionMethods::ParentInfo ;
213 16     16   141 use Scalar::Util qw(isweak weaken) ;
  16         27  
  16         4379  
214              
215             sub new
216             {
217 44     44   66 my $type = shift;
218 44         213 my $self = {@_ };
219              
220             # Necessary to avoid ghost object and memory leaks. See
221             # WeakRef module See also "Programming perl" 3rd edition
222             # page 266.
223 44         157 weaken ($self -> {parent}) ;
224 44         227 bless $self,$type ;
225             }
226              
227             =head2 index_value
228              
229             Returns the index value of the element containing the child object.
230             Returns undex if the Class::IntrospectionMethods slot is not hash or
231             array based.
232              
233             =cut
234              
235 7     7   42 sub index_value { return shift -> {index_value} ;}
236              
237             =head2 index_value
238              
239             Identical to index_value. This method may be preferred for hash based
240             slots. (This is just syntactical sugar).
241              
242             =cut
243              
244 0     0   0 sub key_name { return shift -> {index_value} ;}
245              
246              
247              
248             =head2 slot_name
249              
250             Returns the name of the IntrospectionMethods slot containing the child
251             object.
252              
253             =cut
254              
255 17     17   106 sub slot_name { return shift -> {slot_name} ;}
256              
257             =head2 parent
258              
259             Returns the parent object containing child.
260              
261             =cut
262              
263             sub parent
264             {
265 24     24   452 my $self = shift ;
266 24         37 my $parent = shift ;
267 24 100       69 if (defined $parent)
268             {
269             # Necessary to avoid ghost object and memory leaks. See
270             # WeakRef module See also "Programming perl" 3rd edition
271             # page 266.
272 2         10 weaken ($self -> {parent} = $parent) ;
273             }
274 24         143 return $self->{parent}
275             }
276              
277              
278             # This class is provided for backward compatibility for an older
279             # projet (the one that used a modified version of Class::MethodMaker)
280             # Do not use.
281              
282             package Class::IntrospectionMethods::ParentNameTie ;
283              
284             require Tie::Scalar;
285 16     16   98 use Carp ;
  16         30  
  16         931  
286 16     16   75 use vars qw/@ISA/ ;
  16         32  
  16         3589  
287              
288             @ISA = ('Tie::Scalar');
289              
290             sub TIESCALAR
291             {
292 126     126   202 my $type = shift;
293 126         394 my $self = { @_ } ;
294 126         407 bless $self, $type;
295             }
296              
297             sub FETCH
298             {
299 15     15   805 my $self = shift;
300 15         112 Class::IntrospectionMethods::Parent::warn_obsolete("Reading directly $self->{name} is deprecated");
301 15         33 my $m = $self->{method} ;
302 15         58 return $self->{parent}->$m(@_)
303             }
304              
305             sub STORE
306             {
307 0     0     my $self = shift;
308 0           croak "Writing directly to $self->{name} is forbidden";
309             }
310             1;
311              
312             __END__