File Coverage

blib/lib/Class/IntrospectionMethods.pm
Criterion Covered Total %
statement 500 524 95.4
branch 189 246 76.8
condition 34 51 66.6
subroutine 78 84 92.8
pod 9 25 36.0
total 810 930 87.1


line stmt bran cond sub pod time code
1             # (X)Emacs mode: -*- cperl -*-
2              
3             # $Author: domi $
4             # $Date: 2004/12/13 12:19:43 $
5             # $Name: $
6             # $Revision: 1.5 $
7              
8             package Class::IntrospectionMethods;
9              
10             =head1 NAME
11              
12             Class::IntrospectionMethods - creates methods with introspection
13              
14             =head1 SYNOPSIS
15              
16             use Class::IntrospectionMethods qw/make_methods/;
17              
18             make_methods
19             (
20             parent,
21             global_catalog =>
22             {
23             name => 'metacat',
24             list =>
25             [
26             [qw/foo/] => f_cat,
27             [qw/bar baz/] => b_cat,
28             ],
29             }
30             new_with_init => 'new',
31             get_set => [ qw /foo bar baz / ];
32             ) ;
33              
34             =head1 DESCRIPTION
35              
36             This module provides:
37              
38             =over
39              
40             =item *
41              
42             A way to set up a lot of get/set method. These get/set methods can
43             access plain scalars, array, hash. These scalar, hash or array can be
44             tied (See L) with classes specified by the user. The element
45             of these arrays or hashes can be constrained to be object, tied
46             scalar.
47              
48             =item *
49              
50             A way to later query the object or class to retrieve the list of
51             methods (aka slots) created by this module.
52              
53             =item *
54              
55             A way to organize these slots in several catalogs.
56              
57             =item *
58              
59             When a slot contains object or tied scalars hashes or arrays, the
60             contained object can be queried for the container object.
61             In other words, the parent object (the one constructed by
62             C contains a child object in one of its
63             slots either as a plain object or an object hidden behind a tied
64             construct. C will provide the child
65             object a method to retrieve the parent object reference.
66              
67             =back
68              
69             For instance, you can use this module to create a tree where each node
70             or leaf is an object. In this case, this module provides methods to
71             navigate up the tree of objects with the installed "parent" method.
72              
73             In other words, this module provides special methods to enable the
74             user to navigate up or down a tree (or directed graph) using
75             introspection (to go down) and the "parent" method to go up.
76              
77             You may notice similarities between this module and
78             L. In fact this module was written from
79             Class::MethodMaker v1.08, but it does not provide most of the fancy
80             methods of Class::MethodMaker. Only scalar, array and hash
81             accessors (with their tied and objects variants) are provided.
82              
83             Originally, the introspection and "parent" functionalities were
84             implemented in Class::MethodMaker. Unfortunately, they were not
85             accepted by Class::MethodMaker's author since they did not fit his
86             own vision of his module (fair enough).
87              
88             The old API of L is provided as deprecated
89             methods. Using the new (and hopefully more consistent) API is
90             prefered.
91              
92             =cut
93              
94             # --------------------------------------------------------------
95              
96 15     15   271135 use strict;
  15         37  
  15         653  
97 15     15   81 use warnings ;
  15         29  
  15         2787  
98              
99             # Inheritance -------------------------
100              
101             #use AutoLoader;
102             #use vars qw( @ISA );
103             #@ISA = qw ( AutoLoader );
104              
105 15     15   80 use vars qw( $VERSION @ISA @EXPORT_OK);
  15         30  
  15         1670  
106              
107             require Exporter;
108             @ISA = qw(Exporter);
109             @EXPORT_OK = qw(make_methods set_obsolete_behavior set_parent_method_name);
110              
111             # Utility -----------------------------
112              
113             # Necessary for parent feature
114 15     15   85 use Scalar::Util qw(isweak weaken) ;
  15         26  
  15         2220  
115             use Class::IntrospectionMethods::Catalog
116 15     15   19904 qw/set_global_catalog set_method_info set_method_in_catalog/;
  15         43  
  15         1402  
117             use Class::IntrospectionMethods::Parent
118 15     15   11997 qw/set_parent_method_name graft_parent_method/ ;
  15         49  
  15         1667  
119              
120 15     15   107 use Carp qw( carp cluck croak );
  15         27  
  15         2963  
121              
122             my $obsolete_behavior = 'carp' ;
123             my $support_legacy = 0 ;
124             my $legacy_object_init = 'cmm_init' ;
125              
126             $VERSION = sprintf "%d.%03d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
127              
128             =head1 Transition from Class::MethodMaker
129              
130             This module was forked from Class::MethodMaker v1.08. To ease
131             migration from older project (which include a proprietary project of
132             mine) using Class::MethodMaker to Class::IntrospectionMethods, a
133             compatiblity mode is provided. (although some features of
134             L will not work. See
135             L for details)
136              
137             You can use the following function to finely tune the compatibility
138             behavior to either croak, carp (See L for details) or be silent.
139              
140             One note: I provide backward compatibility for Class::MethodMaker
141             v1.08 and the modification I made that were later refused. So you may
142             notice compatibility features that do not exist in Class::MethodMaker
143             v1.08.
144              
145             =head2 set_obsolete_behavior ( behavior, provide_legacy_method)
146              
147             C is either C, C or C. (default is
148             C).
149              
150             C is either 1 or 0. Default 0. When set to one,
151             this module will provide methods that were only available in the
152             modified version of Class::MethodMaker v1.08.
153              
154             =cut
155              
156             sub set_obsolete_behavior
157             {
158 15     15 1 406 ($obsolete_behavior, $support_legacy) = @_ ;
159 15         111 Class::IntrospectionMethods::Parent::set_obsolete_behavior (@_) ;
160 15         110 Class::IntrospectionMethods::Catalog::set_obsolete_behavior (@_) ;
161             }
162              
163             # internal
164             sub warn_obsolete
165             {
166 49 50   49 0 177 return if $obsolete_behavior eq 'skip' ;
167 15     15   88 no strict 'refs' ;
  15         30  
  15         16565  
168 0         0 $obsolete_behavior->(@_) ;
169             }
170              
171 499     499 0 1818 sub ima_method_maker { 1 };
172              
173             sub find_target_class {
174             # Find the class to add the methods to. I'm assuming that it would
175             # be the first class in the caller() stack that's not a subsclass of
176             # IntrospectionMethods. If for some reason a sub-class of
177             # IntrospectionMethods also wanted to use IntrospectionMethods it
178             # could redefine ima_method_maker to return a false value and then
179             # $class would be set to it.
180 168     168 0 189 my $class;
181 168         216 my $i = 0;
182 168         260 while (1)
183             {
184 667         3253 $class = (caller($i++))[0];
185 499         2128 last unless ( $class->isa('Class::IntrospectionMethods')
186             and
187 667 100 66     4205 &{$class->can ('ima_method_maker')} );
188             }
189 168         359 return $class;
190             }
191              
192             # -------------------------------------
193              
194             my %legacy_catalog ;
195              
196             my %default_user_options =
197             (
198             catalog_name => undef,
199              
200             # When set, any object stored in a slot (either plain, hashed or
201             # arrayed slot) will get a method to fetch the parent object.
202             provide_parent_method => 0 ,
203              
204             # method called after object creation to perform special
205             # initialisation. This specifies the default name
206             object_init_method => 'cim_init' ,
207              
208             #whether to autovivify object stored in slots
209             auto_vivify => 1
210             ) ;
211              
212             my $child_init = sub
213             {
214             my ($obj,$init_method) = @_ ;
215              
216             return unless defined $obj ;
217              
218             if (defined $init_method && $obj->can($init_method))
219             {
220             $obj->$init_method() ;
221             }
222             elsif ($support_legacy && $obj->can($legacy_object_init))
223             {
224             warn_obsolete("calling obsolete $legacy_object_init on ".ref($obj)) ;
225             $obj->$legacy_object_init() ;
226             }
227             } ;
228              
229             # set legacy catalog methods that were defined in modified version of
230             # Class::MethodMaker v1.08
231             sub set_legacy_methods
232             {
233 20     20 0 44 my $target_class = shift ;
234              
235             return
236             (
237 2 100   2   754 CMM_CATALOG_LIST =>
238             sub {my $p = ref $_[0] ? shift : $target_class;
239 2         10 $p->CMM_CATALOG_LEGACY()->all_catalog} ,
240              
241             CMM_CATALOG =>
242             sub {
243 12 100   12   1547 my $p = ref($_[0]) ? shift : $target_class;
244 12 100       54 my @catalog_names = scalar @_ ? @_ :
245             $p->CMM_CATALOG_LEGACY()->all_catalog ;
246 12         43 my @result = $p->CMM_CATALOG_LEGACY()->slot(@catalog_names);
247 12 100       87 return wantarray ? @result : \@result ;
248             },
249              
250             CMM_SLOT_CATALOG => sub
251             {
252 5 100   5   1657 my $p = ref $_[0] ? shift : $target_class;
253 5         8 my $slot = shift ;
254 5 100       19 $p->CMM_CATALOG_LEGACY()->change($slot, shift) if @_ ;
255 5         16 my @r = $p->CMM_CATALOG_LEGACY()->catalog($slot);
256 5         27 return $r[0] ; # legacy method can only return 1 item
257             } ,
258              
259 3 50   3   2549 CMM_SLOT_DETAIL =>
260             sub {my $p = ref $_[0] ? shift : $target_class;
261 3         11 my $res = $p->CMM_CATALOG_LEGACY()->info(shift);
262 3 50       24 return wantarray ? @$res : $res ;
263             }
264 20         395 ) ;
265             }
266              
267             sub make_methods
268             {
269 20     20 0 463 my (@args) = @_;
270              
271 20         80 my $target_class = find_target_class;
272              
273 20         90 my @legacy_catalog_methods = set_legacy_methods($target_class) ;
274              
275             # user option used through this call to make_methods. The copy is
276             # done to provide a closure.
277 20         158 my %user_options = %default_user_options ;
278              
279             # Each meta-method is responsible for calling install_methods() to
280             # get it's methods installed.
281 20         48 while (1)
282             {
283 88 100       644 my $meta_method = shift @args or last;
284              
285 68 100       582 if ($meta_method =~ /^-?parent$/ )
    50          
    100          
    100          
    100          
286             {
287 8         22 $user_options{provide_parent_method} = 1 ;
288             }
289             elsif ($meta_method =~ /^-?noparent$/ )
290             {
291 0         0 $user_options{provide_parent_method} = 0 ;
292             }
293             elsif ($meta_method =~ /^-?catalog$/)
294             {
295             # legacy mode
296 4 100 66     34 if ($support_legacy && not defined $legacy_catalog{$target_class})
297             {
298 3         13 warn_obsolete("-catalog is deprecated");
299 3         13 my @legacy = ( name => 'CMM_CATALOG_LEGACY',
300             list => [] ) ;
301 3         19 my %meth = (set_global_catalog($target_class, @legacy),
302             @legacy_catalog_methods) ;
303 3         25 install_methods (%meth) ;
304 3         13 $legacy_catalog{$target_class} = 1;
305             }
306 4         14 $user_options{catalog_name} = shift @args ;
307             }
308             elsif ($meta_method =~ /^-?nocatalog$/)
309             {
310 1         3 $user_options{catalog_name} = undef ;
311             }
312             elsif ($meta_method =~ /^-?global[_-]catalog$/i)
313             {
314 2         5 my $struct = shift @args;
315 2         15 my (%meth) = set_global_catalog($target_class, %$struct) ;
316 2         9 install_methods (%meth) ;
317 2         6 $legacy_catalog{$target_class} = 1;
318             }
319             else
320             {
321 53 50       168 my $arg = shift @args or
322             croak "make_methods: No arg for $meta_method";
323 53 100       224 my @args = ref($arg) eq 'ARRAY' ? @$arg : ($arg);
324 15     15   96 no strict 'refs' ;
  15         27  
  15         9536  
325             #print "Calling $meta_method\n";
326 53         275 $meta_method->(\%user_options,@args);
327             }
328             }
329             }
330              
331             sub store_slot_in_catalog
332             {
333 90     90 0 135 my $slot = shift ;
334 90         118 my $catalog_name = shift ;
335              
336 90         196 my $target_class = find_target_class;
337              
338 90         259 my @details = @_ ;
339 90         350 set_method_info($target_class, $slot, \@details) ;
340              
341 90 100       319 return unless defined $catalog_name ;
342              
343 17         57 set_method_in_catalog($target_class, $slot, $catalog_name) ;
344             }
345              
346             sub install_methods
347             {
348 58     58 0 382 my (%methods) = @_;
349              
350 15     15   86 no strict 'refs';
  15         32  
  15         102065  
351              
352 58         214 my $target_class = find_target_class;
353 58         125 my $package = $target_class . "::";
354              
355 58         76 my ($name, $code);
356 58         231 while (($name, $code) = each %methods)
357             {
358             # add the method unless it's already defined (which should only
359             # happen in the case of static methods, I think.)
360 670         1189 my $reftype = ref $code;
361 670 50       1372 if ( $reftype eq 'CODE' )
362             {
363 670 50       633 *{"$package$name"} = $code unless defined *{"$package$name"}{CODE};
  670         2979  
  670         3231  
364             }
365             else
366             {
367 0         0 croak "What do you expect me to do with this?: $code\n";
368             }
369             }
370             }
371              
372             =head1 CLASS INTROSPECTION
373              
374             Class::IntrospectionMethods provides a set of features that enable you
375             to query the available methods of a class. These methods can be
376             invoked as class methods or object methods. From now on, a class
377             created with Class::IntrospectionMethods will be called a CIMed class.
378              
379             The top-down introspection is triggered by the C
380             option.
381              
382             =head2 slot query: the global_catalog option
383              
384             When set, the C will invoke the
385             L
386             function. This function will use the parameters you passed to the
387             C option to install a new method in your class. E.g.,
388             this C option:
389              
390             package Foo::Bar ;
391             use Class::IntrospectionMethods qw/make_methods/;
392              
393             make_methods
394             (
395             global_catalog =>
396             {
397             name => 'metacat',
398             list => [
399             [qw/foo bar baz/] => foo_cat,
400             [qw/a b z/] => alpha_cat,
401             ],
402             },
403             )
404              
405             will enable you to call:
406              
407             &Foo::Bar::metacat->all_catalog ; # return alpha_cat foo_cat
408             my $obj = Foo::Bar-> new;
409             $obj -> metacat->all_catalog ; # also return alpha_cat foo_cat
410              
411             See L for:
412              
413             =over
414              
415             =item *
416              
417             The other informations you can retrieve through the global catalog.
418              
419             =item *
420              
421             How to move a slot from one catalog to another at run-time (only the
422             object catalog can be modified)
423              
424             =item *
425              
426             The distinction between the class catalog and the object catalog
427              
428             =back
429              
430             Note that IntrospectionMethods does not check whether the method
431             declared in global_catalog are actually created by
432             IntrospectionMethods or created elsewhere.
433              
434              
435             =head2 From slot to object: the parent option.
436              
437             If you use tied scalars (with the C or C method
438             types), or object method type, your tied scalars or objects may need
439             to call the parent CIMed object.
440              
441             For instance, if you want to implement error handling in your tied
442             scalar or objects that will call the parent CIMed object or display
443             error messages giving back to the user the slot name containing the
444             faulty object.
445              
446             So if you need to query the slot name, or index value (for C or
447             C method types), or be able to call the parent object, you can
448             use the C option when creating the parent CIMed class:
449              
450             package FOO ;
451             use Class::IntrospectionMethods
452             'parent' ,
453             object => [ foo => 'My::Class' ];
454              
455             Using this option will graft I attribute and its accessor
456             method. Be default, this attribute and accessor method will be named
457             C, but this can be changed with C.
458              
459             This attribute contains (and the accessor method will return) a
460             C object. This object
461             features methods C, C and C.
462             See L for
463             more details.
464              
465             =over
466              
467             =item C
468              
469             Reference of the parent object.
470              
471             =item C
472              
473             slot name to use to get the child object from the parent.
474              
475             =item C
476              
477             index value (for C method type) to use to get the child
478             object from the parent.
479              
480             =back
481              
482             When using the C<-parent> option, a C, C
483             and C methods are also grafted to the child's
484             class.
485              
486             Here is an example to retrieve a parent object :
487              
488             package FOO ;
489             use ExtUtils::testlib;
490             '-parent' ,
491             object_tie_hash =>
492             [
493             {
494             slot => 'bar',
495             tie_hash => ['MyHash'],
496             class => ['MyObj', 'a' => 'foo']
497             }
498             ],
499             new => 'new';
500              
501             package main;
502              
503             my $o = new X;
504              
505             my $obj = $o->a('foo') ;
506             my $p= $obj->metadad->parent; # $p is $o
507              
508             See L for further
509             details
510              
511             =head1 SUPPORTED METHOD TYPES
512              
513             =head2 new
514              
515             Creates a basic constructor.
516              
517             Takes a single string or a reference to an array of strings as its
518             argument. For each string creates a simple method that creates and
519             returns an object of the appropriate class.
520              
521             This method may be called as a class method, as usual, or as in instance
522             method, in which case a new object of the same class as the instance
523             will be created.
524              
525             =cut
526              
527             sub new
528             {
529 9     9 1 28 my ($user_options, @args) = @_;
530 9         21 my %methods;
531 9         30 foreach (@args)
532             {
533             $methods{$_} = sub
534             {
535 11     11   3821 my $class = shift;
536 11   33     87 $class = ref $class || $class;
537 11         61 bless {}, $class;
538 9         60 };
539             }
540 9         59 install_methods(%methods);
541             }
542              
543             =head2 new_with_init
544              
545             Creates a basic constructor which calls a method named C after
546             instantiating the object. The C method should be defined in the
547             class using IntrospectionMethods.
548              
549             Takes a single string or a reference to an array of strings as its
550             argument. For each string creates a simple method that creates an
551             object of the appropriate class, calls C on that object
552             propagating all arguments, before returning the object.
553              
554             This method may be called as a class method, as usual, or as in instance
555             method, in which case a new object of the same class as the instance
556             will be created.
557              
558             =cut
559              
560             sub new_with_init {
561 1     1 1 3 my ($user_options, @args) = @_;
562 1         2 my %methods;
563 1         3 foreach (@args) {
564 1         2 my $field = $_;
565             $methods{$field} = sub {
566 1     1   2 my $class = shift;
567 1   33     7 $class = ref $class || $class;
568 1         2 my $self = {};
569 1         2 bless $self, $class;
570 1         5 $self->init (@_);
571 1         9 return $self;
572 1         14 };
573             }
574 1         4 install_methods(%methods);
575             }
576              
577             # ----------------------------------------------------------------------------
578              
579             =head2 new_with_args
580              
581             Creates a basic constructor.
582              
583             Takes a single string or a reference to an array of strings as its
584             argument. For each string creates a simple method that creates and
585             returns an object of the appropriate class.
586              
587             This method may be called as a class method, as usual, or as in instance
588             method, in which case a new object of the same class as the instance
589             will be created.
590              
591             Constructor arguments will be stored as a key, value pairs in the
592             object. No check is done regarding the consistencies of the data
593             passed to the constructor and the accessor methods created.
594              
595             =cut
596              
597             sub new_with_args
598             {
599 1     1 1 3 my ($user_options, @args) = @_;
600 1         2 my %methods;
601 1         3 foreach (@args)
602             {
603             $methods{$_} = sub
604             {
605 1     1   1290 my $class = shift;
606 1         3 my @c_args = @_ ;
607 1   33     7 $class = ref $class || $class;
608 1         3 my $self = { @c_args };
609 1         5 bless $self, $class;
610 1         6 };
611             }
612 1         4 install_methods(%methods);
613             }
614              
615             =head2 get_set
616              
617             Takes a single string or a reference to an array of strings as its
618             argument. Each string specifies a slot, for which accessor methods are
619             created. E.g.
620              
621             get_set => 'foo',
622             get_set => [qw/foo bar/],
623              
624             The accessor methods are, by default:
625              
626             =over 4
627              
628             =item x
629              
630             If an argument is provided, sets a new value for x. This is true even
631             if the argument is undef (cf. no argument, which does not set.)
632              
633             Returns (new) value.
634              
635             Value defaults to undef.
636              
637             =item clear_x
638              
639             Sets value to undef. This is exactly equivalent to
640              
641             $foo->x (undef)
642              
643             No return.
644              
645             =back
646              
647             This is your basic get/set method, and can be used for slots
648             containing any scalar value, including references to non-scalar
649             data. Note, however, that IntrospectionMethods has meta-methods that
650             define more useful sets of methods for slots containing references to
651             lists, hashes, and objects.
652              
653             =cut
654              
655             sub get_set
656             {
657 11     11 1 33 my ($user_options, @args) = @_;
658 11         58 my @methods;
659              
660 11         49 foreach my $arg (@args)
661             {
662 28         45 my $slot = $arg ;
663              
664 28         77 store_slot_in_catalog
665             ($arg, $user_options->{catalog_name}, slot_type => 'scalar') ;
666              
667             push @methods, $arg =>
668             sub
669             {
670 20     20   672 my $self = shift;
671 20 100       50 if ( @_ ) {$self->{$slot} = shift;}
  6         30  
  14         117  
672             else {$self->{$slot};}
673 28         191 };
674             }
675              
676 11         39 install_methods (@methods);
677             }
678              
679             =head2 object
680              
681             Creates methods for accessing a slot that contains an object of a given
682             class.
683              
684             object => [
685             phooey => { class => 'Foo' },
686             [ qw / bar1 bar2 bar3 / ] => { class => 'Bar'},
687             foo => { class => 'Baz'
688             constructor_args => [ set => 'it' ]},
689             [qw/dog fox/] => { class => 'Fob',
690             constructor_args => [ sound => 'bark' ] },
691             cat => { class => 'Fob',
692             constructor_args => [ sound => 'miaow' ]}
693              
694             tiger => { class => 'Special',
695             init => 'my_init' # method to call after creation
696             }
697             ]
698              
699             The main argument is an array reference. The array should contain a
700             set of C<< slot_name => hash_ref >> pairs. C can be an
701             array ref if you want to specify several slots the same way.
702              
703             The hash ref sub-arguments are parsed thus:
704              
705             =over 4
706              
707             =item class
708              
709             The class name of the stored object.
710              
711             =item constructor_args
712              
713             A array ref containing arguments that are passed to the C
714             constructor.
715              
716             =item init_method
717              
718             Name of a initialisation method to call on the newly created object.
719             The method name defaults to C. In other words if the user
720             class feature a C method, this one will be called after
721             creation of the object.
722              
723             =back
724              
725             For each slot C, the following methods are created:
726              
727             =over 4
728              
729             =item x
730              
731             A get/set method.
732              
733             If supplied with an object of an appropriate type, will set set the slot
734             to that value.
735              
736             Else, if the slot has no value, then an object is created by calling
737             C on the appropriate class, passing in any supplied
738             arguments. These arguments may supersede the arguments passed with the
739             C parameters (See above).
740              
741             The stored object is then returned.
742              
743             =item delete_x
744              
745             Will destroy the object held by C.
746              
747             =item defined_x
748              
749             Will return true if C contains an object. False otherwise.
750              
751             =back
752              
753             =cut
754              
755             sub translate_object_args
756             {
757 4     4 0 19 my @old_args = @_ ;
758              
759 4         10 warn_obsolete( "Old style object arguments are deprecated. Check documentation");
760              
761             # translate old style api
762 4         5 my @new ;
763 4         11 while (@old_args)
764             {
765 6         10 my $obj_class = shift @old_args;
766              
767 6 50       15 my $list = shift @old_args or die "No slot names for obj_class";
768             # Allow a list of hashrefs.
769 6 100       18 my @list = ( ref($list) eq 'ARRAY' ) ? @$list : ($list);
770              
771 6         10 foreach my $obj_def (@list)
772             {
773 11         10 my (@name, @c_args);
774 11 100       21 if ( ref $obj_def eq 'HASH') # list of hash ref
775             {
776 3 50       16 my $slot = delete $obj_def->{slot}
777             or die "No slot defined in object hash ref";
778 3         17 push @new , $slot, {%$obj_def, class => $obj_class} ;
779             }
780             else
781             {
782 8         28 push @new, $obj_def => $obj_class ;
783             }
784             }
785             }
786 4         17 return @new ;
787             }
788              
789             sub object
790             {
791 6     6 1 17 my ($user_options, @old_args) = @_;
792 6         8 my %methods;
793              
794 6         13 my $may_be_class = $old_args[0] ;
795              
796             # test whether the package name exists or not.
797 6 100       13 my @args = defined * {$may_be_class.'::'} ?
  6         36  
798             translate_object_args(@old_args) : @old_args ;
799              
800             # new style API: list of hash ref
801 6         60 while (@args)
802             {
803 13         26 my $slot_item = shift @args ;
804              
805             # Allow a list ref
806 13 100       52 my @slot_list = ( ref($slot_item) ) ? @$slot_item : ($slot_item);
807              
808 13         20 my $arg0 = shift @args ;
809 13 100       70 my $href = ref $arg0 ? $arg0 : {class => $arg0};
810 13         25 my $c_args = $href->{constructor_args} ;
811 13         21 my $slot_av = $href->{auto_vivify} ;
812 13 50       36 my $av = defined $slot_av ? $slot_av : $user_options->{auto_vivify} ;
813 13         20 my $graft = $user_options->{provide_parent_method} ;
814              
815 13         23 foreach my $slot (@slot_list)
816             {
817             # these lexicals will be used in closures
818 14         22 my $type = $href->{class} ;
819 14 100       36 my @c_args = defined $c_args ? @$c_args : () ;
820 14   33     69 my $init_method = $href->{init_method}
821             || $user_options->{object_init_method};
822              
823             $methods{$slot} = sub
824             {
825 14     14   914 my ($self, @sub_args) = @_;
826              
827 14 100 66     54 if (not defined $self->{$slot} or scalar @sub_args > 0)
828             {
829 10         11 my $item = $sub_args[0];
830              
831 10 50 66     57 my $obj = (ref $item and UNIVERSAL::isa($item, $type)) ?
    100          
832             $item : $av ? $type->new(@c_args) : undef ;
833              
834 10 100 66     96 graft_parent_method($obj,$self, $slot)
835             if $graft && defined $obj;
836              
837 10         25 $child_init->($obj, $init_method) ;
838              
839             # store object
840 10         51 $self->{$slot} = $obj;
841             }
842              
843 14         61 return $self->{$slot};
844 14         83 };
845              
846 14 100       59 store_slot_in_catalog
847             (
848             $slot, $user_options->{catalog_name},
849             slot_type => 'scalar',
850             class => $type,
851             scalar @c_args ? (class_args => \@c_args) : ()
852             ) ;
853              
854             $methods{"delete_$slot"} = sub {
855 0     0   0 my ($self) = @_;
856 0         0 $self->{$slot} = undef;
857 14         86 };
858              
859             $methods{"defined_$slot"} = sub {
860 2     2   1214 my ($self) = @_;
861 2 100       21 return defined $self->{$slot} ? 1 : 0 ;
862 14         156 };
863             }
864             }
865 6         29 install_methods(%methods);
866             }
867              
868              
869             # ----------------------------------------------------------------------------
870              
871             =head2 tie_scalar
872              
873             Create a get/set method to deal with the tied scalar.
874              
875             Takes a list of pairs, where the first is the name of the slot (or an
876             array ref containing a list of slots), the second is an array
877             reference. The array reference takes the usual tie parameters.
878              
879             For instance if Enum and Boolean are tied scalar that accept default values,
880             you can have:
881              
882             tie_scalar =>
883             [
884             foo => [ 'Enum', enum => [qw/A B C/], default => 'B' ],
885             bar => [ 'Enum', enum => [qw/T0 T1/], default => 'T1'],
886             baz => ['Boolean', default => 0],
887             [qw/lots of slots/] => ['Boolean', default => 1],
888             ],
889              
890             Foreach slot C, tie_scalar install the following methods:
891              
892             =over
893              
894             =item tied_storage_xx
895              
896             Return the object tied behind the scalar. Auto-vivify if necessary.
897              
898             =back
899              
900             =cut
901              
902             sub tie_scalar
903             {
904 5     5 1 25 my ($user_options, @args) = @_;
905 5         12 my %methods;
906              
907 5         13 my $parent_method_closure = $user_options->{provide_parent_method} ;
908              
909 5         33 while ( my ($fieldr, $tie_args) = splice (@args, 0, 2))
910             {
911 6 100       39 my ($tie_class,@c_args)= ref($tie_args) ? @$tie_args : ($tie_args);
912              
913 6 50       26 croak "undefined tie class" unless defined $tie_class ;
914              
915 6 100       31 foreach my $field_elt (ref $fieldr ? @$fieldr : $fieldr)
916             {
917 8         15 my $field = $field_elt ; # safer with the closures below
918              
919             my $create_field = sub
920             {
921 5     5   8 my $self = shift ;
922             # directly tie the scalar held by self
923 5         43 my $obj = tie ($self->{$field}, $tie_class, @c_args);
924              
925 5 100       123 graft_parent_method($obj,$self,$field)
926             if $parent_method_closure ;
927 8         37 } ;
928              
929             $methods{$field} =
930             sub
931             {
932 9     9   3406 my $self = shift ;
933              
934 9 100       38 &$create_field($self) unless exists $self->{$field} ;
935              
936 9 100       23 if (@_)
937             {
938 3         110 $self->{$field} = $_[0] ;
939             # avoid reading $$ref which can be a tied ref
940 3         47 return $_[0] ;
941             }
942              
943 6         39 return $self->{$field} ;
944 8         99 };
945              
946             my $tied_storage_sub = sub
947             {
948 3     3   5 my $self = shift ;
949             # create the tied variable if necessary
950             # (i.e. accessor was not used before)
951 3 100       15 &$create_field($self) unless exists $self->{$field} ;
952              
953 3         19 return tied($self->{$field}) ;
954 8         39 };
955              
956             # first method provides name consistency with tie_tie_hash
957 8         51 $methods{"tied_storage_$field"} = $tied_storage_sub ;
958              
959 8         45 foreach my $deprecated ("tied_scalar_$field",
960             "tied_$field",
961             $field."_tied")
962             {
963             $methods{$deprecated} = sub
964             {
965 3     3   613 warn_obsolete("method $deprecated is deprecated") ;
966 3         14 return $tied_storage_sub->(@_) ;
967 24         238 } ;
968             }
969              
970             store_slot_in_catalog
971             (
972 8 100       53 $field, $user_options->{catalog_name},
973             slot_type => 'scalar',
974             tie_scalar => $tie_class,
975             scalar @c_args ? (tie_scalar_args => \@c_args) : ()
976             );
977             }
978              
979             }
980 5         26 install_methods(%methods);
981             }
982              
983              
984             sub _add_hash_methods {
985 28     28   55 my ($methods, $field, $create_hash) = @_ ;
986              
987 28 50       74 croak "Missing create_hash sub" unless defined $create_hash;
988              
989             $methods->{$field . "_keys"} =
990             sub {
991 3     3   703 my ($self) = @_;
992 3 50       13 &$create_hash($self,$field) unless defined $self->{$field} ;
993 3         16 return keys %{$self->{$field}} ;
  3         19  
994 28         184 };
995              
996             $methods->{$field . "_values"} =
997             sub {
998 2     2   645 my ($self) = @_;
999 2 50       11 &$create_hash($self,$field) unless defined $self->{$field} ;
1000 2         3 values %{$self->{$field}} ;
  2         26  
1001 28         158 };
1002              
1003             $methods->{$field . "_exists"} =
1004             sub {
1005 0     0   0 my ($self) = shift;
1006 0         0 my ($key) = @_;
1007             return
1008 0   0     0 exists $self->{$field} && exists $self->{$field}{$key};
1009 28         196 };
1010              
1011             $methods->{$field . "_delete"} =
1012             sub {
1013 2     2   5 my ($self, @keys) = @_;
1014 2 50       9 &$create_hash($self,$field) unless defined $self->{$field} ;
1015 2         4 delete @{$self->{$field}}{@keys};
  2         9  
1016 28         178 };
1017              
1018             $methods->{$field . "_clear"} =
1019             sub {
1020 1     1   709 my $self = shift;
1021 1 50       6 &$create_hash($self,$field) unless defined $self->{$field} ;
1022 1         3 %{$self->{$field}} = ();
  1         4  
1023 28         255 };
1024              
1025             $methods->{$field . "_index"} =
1026             sub {
1027 0     0   0 my $self = shift;
1028 0         0 $self->$field(@_) ;
1029 28         157 };
1030              
1031             $methods->{$field . "_set"} =
1032             sub {
1033 3     3   8 my $self = shift;
1034 3 50       15 &$create_hash($self,$field) unless defined $self->{$field} ;
1035 3         7 %{$self->{$field}} = (@_);
  3         24  
1036 28         598 };
1037             }
1038              
1039             # ----------------------------------------------------------------------------
1040              
1041             =head2 hash
1042              
1043             Creates a group of methods for dealing with hash data stored in a
1044             slot.
1045              
1046             hash =>
1047             [
1048             'plain_hash1', 'plain_hash2',
1049             [qw/lot of plain hashes/] ,
1050             yet_another_plain_hash => {} ,
1051              
1052             my_tied_hash => {tied_hash => 'My_Tie_Hash' },
1053             my_tied_hash_with_args =>
1054             { tied_hash => [ 'My_Tie_Hash' , @my_args ] },
1055              
1056             my_hash_with_tied_storage => { tie_storage => 'MyTieScalar' },
1057             [qw/likewise_with_args likewise_with_other_args/] =>
1058             { tie_storage => [ 'MyTieScalar', @my_args] }
1059              
1060             my_tied_hash_with_tied_storage =>
1061             { tied_hash => 'My_Tie_Hash',tie_storage => 'MyTieScalar' },
1062              
1063             my_hash_with_object => { class_storage => 'MyClass' },
1064             my_hash_with_object_and_constructor_args =>
1065             { class_storage => [ 'MyClass' , @my_args ] },
1066              
1067             ]
1068              
1069              
1070             The C parameters are:
1071              
1072             =over
1073              
1074             =item *
1075              
1076             A string or a a reference to an array of strings. For each
1077             of these string, a hash based slot is created.
1078              
1079             =item *
1080              
1081             A hash ref who contains attributes attached to the slot(s) defined by
1082             the previous arguments. These attribute are used to specify the
1083             behavior of the hash attached to the slot or to specialize the hash
1084             values. See L for details on the possibles
1085             attributes.
1086              
1087             =back
1088              
1089             For each slot defined, creates:
1090              
1091             =over 4
1092              
1093             =item x
1094              
1095             Called with no arguments returns the hash stored in the slot, as a hash
1096             in a list context or as a reference in a scalar context.
1097              
1098             Called with one simple scalar argument it treats the argument as a key
1099             and returns the value stored under that key.
1100              
1101             Called with more than one argument, treats them as a series of key/value
1102             pairs and adds them to the hash.
1103              
1104             =item x_keys or x_index
1105              
1106             Returns the keys of the hash.
1107              
1108             =item x_values
1109              
1110             Returns the list of values.
1111              
1112             =item x_exists
1113              
1114             Takes a single key, returns whether that key exists in the hash.
1115              
1116             =item x_delete
1117              
1118             Takes a list, deletes each key from the hash.
1119              
1120             =item x_clear
1121              
1122             Resets hash to empty.
1123              
1124             =back
1125              
1126             =cut
1127              
1128             sub hash
1129             {
1130 12     12 1 72 my ($user_options, @args) = @_;
1131 12         29 my %methods;
1132              
1133             #print "hash called with\n", Dumper $user_options, Dumper \@args ;
1134              
1135 12         8025 require Tie::Hash::CustomStorage ;
1136              
1137 12         18099 my $parent_method_closure = $user_options->{provide_parent_method} ;
1138              
1139 12         54 while (@args)
1140             {
1141 24         53 my $hash = shift @args ;
1142 24 100       104 my @slot_hash = ( ref($hash) eq 'ARRAY' ) ? @$hash : ($hash);
1143              
1144 24 100       75 my $x_parm = ref $args[0] ? shift @args : undef ;
1145 24         88 my $init_meth = $user_options->{object_init_method} ;
1146             my $create_hash = sub
1147             {
1148 17     17   32 my ($self,$name) = @_ ;
1149 17         30 my %hash ;
1150 17 100       54 if (defined $x_parm)
1151             {
1152             my $init_obj = sub
1153             {
1154 14         1605 my ($l_obj,$l_idx) = @_ ;
1155 14 50       68 graft_parent_method($l_obj,$self,$name,$l_idx)
1156             if $parent_method_closure ;
1157 14         36 $child_init->($l_obj, $init_meth) ;
1158 15         87 } ;
1159              
1160 15         275 my $custom_tied_obj = tie %hash, 'Tie::Hash::CustomStorage', %$x_parm,
1161             init_object => $init_obj ;
1162              
1163 15 50       909 my $user_tied_obj = $custom_tied_obj->get_user_tied_hash_object
1164             if defined $custom_tied_obj;
1165 15 100 100     205 graft_parent_method($user_tied_obj,$self,$name)
1166             if defined $user_tied_obj and $parent_method_closure ;
1167             }
1168 17         58 $self->{$name} = \%hash ;
1169 24         275 };
1170              
1171             my $handle_value = sub
1172             {
1173 6     6   16 my ($self,$name,$key) = splice @_,0,3 ;
1174 6 50       135 return undef unless defined $key ;
1175              
1176             #print "assigning $_[0]\n";
1177 6 50       14 $self->{$name}{$key} = $_[0] if @_;
1178 6 50       41 return @_ ? $_[0] : $self->{$name}{$key};
1179 24         96 } ;
1180              
1181 24         50 foreach my $obj_def (@slot_hash)
1182             {
1183 28         123 my $name = $obj_def; # kept for closures
1184              
1185             $methods{$name} = sub
1186             {
1187 67     67   18031 my ($self, $key) = splice @_,0,2;
1188              
1189 67 100       267 &$create_hash($self,$name) unless defined $self->{$name} ;
1190              
1191 67 100       249 return wantarray ? %{$self->{$name}} : $self->{$name}
  1 100       16  
1192             unless defined $key;
1193              
1194 50 50       129 croak "hash cannot have more than 2 arg"
1195             if @_ >1 ;
1196              
1197 50 100       173 $self->{$name}{$key} = $_[0] if @_;
1198 50 100       1754 return @_ ? $_[0] : $self->{$name}{$key};
1199 28         136 };
1200              
1201             my $tied_hash_sub = sub
1202             {
1203 7     7   2412 my $self = shift ;
1204 7 100       29 $create_hash->($self,$name) unless defined $self->{$name} ;
1205 7         12 my $custom_tied_obj = tied(%{$self->{$name}}) ;
  7         15  
1206 7 50       23 return undef unless defined $custom_tied_obj ;
1207 7         32 return $custom_tied_obj->get_user_tied_hash_object ;
1208 28         102 } ;
1209              
1210 28 100 100     179 if (defined $x_parm and defined $x_parm->{tie_hash})
1211             {
1212 19         55 $methods{"tied_hash_$name"} = $tied_hash_sub ;
1213              
1214             $methods{"tied_$name"} =
1215             sub
1216             {
1217 3     3   544 warn_obsolete( "method tied_$name is deprecated") ;
1218 3         9 return $tied_hash_sub->(@_) ;
1219 19         104 } ;
1220             }
1221              
1222             my $tied_storage_sub = sub
1223             {
1224 6     6   8 my $self = shift ;
1225 6         10 my $idx = shift ;
1226 6 50       17 &$create_hash($self,$name) unless defined $self->{$name} ;
1227 6         13 &$handle_value($self,$name,$idx) ;
1228 6         1596 my $ref = $self->{$name} ;
1229 6         27 return tied(%$ref)->get_tied_storage_object($idx) ;
1230 28         115 } ;
1231              
1232 28 100 100     149 if (defined $x_parm and defined $x_parm->{tie_storage})
1233             {
1234 2         7 $methods{"tied_storage_$name"} = $tied_storage_sub ;
1235             $methods{"tied_scalar_$name"} = sub
1236             {
1237 6     6   4329 warn_obsolete( "method tied_scalar_$name is deprecated") ;
1238 6         20 return $tied_storage_sub->(@_) ;
1239 2         10 } ;
1240             }
1241              
1242 28         80 my @info = get_extended_info($x_parm) ;
1243              
1244 28         102 store_slot_in_catalog($name, $user_options->{catalog_name},
1245             slot_type => 'hash', @info);
1246              
1247 28         78 _add_hash_methods(\%methods, $name,$create_hash);
1248             }
1249             }
1250 12         125 install_methods(%methods);
1251             }
1252              
1253             sub get_extended_info
1254             {
1255 40     40 0 55 my $x_parm = shift ;
1256              
1257             #print Dumper $x_parm ;
1258              
1259 40         103 my @result = () ;
1260 40 100       111 return @result unless defined $x_parm ;
1261              
1262 34 100       98 if (defined $x_parm->{class_storage})
1263             {
1264 24         42 my $cs = $x_parm->{class_storage} ;
1265 24 100       93 my ($c,@args) = ref $cs ? @$cs : ($cs);
1266 24         42 push @result, class => $c ;
1267 24 100       107 push @result, class_args => \@args if @args ;
1268             }
1269              
1270 34 100       101 if (defined $x_parm->{tie_storage})
1271             {
1272 2         4 my $th = $x_parm->{tie_storage} ;
1273 2 50       8 my ($c,@args)= ref $th ? @$th : ($th);
1274 2         3 push @result, tie_storage => $c;
1275 2 50       7 push (@result, tie_storage_args => \@args) if scalar @args;
1276             }
1277              
1278 34   100     152 my $tie_index = $x_parm->{tie_hash} || $x_parm->{tie_array} ;
1279              
1280 34 100       95 if (defined $tie_index)
1281             {
1282 26 100       88 my ($c,@args)= ref $tie_index ? @$tie_index : ($tie_index);
1283 26         49 push @result, tie_index => $c;
1284 26 100       91 push (@result, tie_index_args => \@args) if scalar @args;
1285             }
1286              
1287 34         143 return @result ;
1288             }
1289              
1290              
1291             sub object_tie_hash
1292             {
1293 6     6 0 12 my ($user_options, @args) = @_;
1294              
1295 6         13 warn_obsolete( "object_tie_hash is deprecated. Please use hash instead");
1296              
1297 6         7 my @new ;
1298 6         17 while (@args)
1299             {
1300 9         13 my $hash = shift @args;
1301 9 50       30 my $slot = delete $hash->{slot}
1302             or croak "No slot names passef to object_tie_hash";
1303              
1304 9 50       42 $hash->{class_storage} = delete $hash->{class}
1305             or croak "No class passed to object_tie_hash";
1306              
1307 9         27 push @new, $slot, $hash ;
1308             }
1309              
1310 6         19 hash($user_options, @new ) ;
1311             }
1312              
1313              
1314             sub tie_hash
1315             {
1316 1     1 0 3 my ($user_options, @args) = @_;
1317              
1318 1         6 warn_obsolete( "tie_hash is deprecated. Please use hash instead");
1319              
1320 1         1 my @new ;
1321 1         5 while (@args)
1322             {
1323 2         5 my $slot = shift @args;
1324 2         5 my $hash = shift @args ;
1325              
1326 2 50       7 my $tie_class = $hash->{tie}
1327             or croak "tie_hash: missing tie parameter";
1328 2         4 my $tie_args = $hash->{args} ;
1329 2 50       14 my @tie_args = ref $tie_args ? @$tie_args : () ;
1330              
1331 2         14 push @new, $slot, { tie_hash => [ $tie_class, @tie_args] };
1332             }
1333              
1334 1         6 hash($user_options, @new ) ;
1335             }
1336              
1337             sub tie_tie_hash
1338             {
1339 1     1 0 3 my ($user_options, @args) = @_;
1340              
1341 1         3 warn_obsolete( "tie_tie_hash is deprecated. Please use hash instead");
1342              
1343 1         1 my @new ;
1344 1         3 while (@args)
1345             {
1346 4         5 my $hash = shift @args;
1347 4 50       11 my $slot = delete $hash->{slot}
1348             or croak "No slot names passef to object_tie_hash";
1349              
1350 4 100       13 $hash->{tie_storage} = delete $hash->{tie_scalar}
1351             if defined $hash->{tie_scalar};
1352              
1353 4         10 push @new, $slot, $hash ;
1354             }
1355              
1356             #print Dumper \@new ;
1357 1         9 hash($user_options, @new ) ;
1358             }
1359              
1360              
1361              
1362              
1363             sub list
1364             {
1365 2     2 0 7 warn_obsolete("list method is obsolete. Please use array");
1366 2         9 goto &array ;
1367             }
1368              
1369             sub _add_array_methods {
1370 12     12   27 my ($methods, $field, $create_array) = @_;
1371              
1372 12 50       31 croak "Create_array is missing" unless defined $create_array ;
1373              
1374 12         14 my %stock ;
1375              
1376             $stock{"pop"} =
1377             sub {
1378 3     3   362 my ($self) = @_;
1379 3 50       13 &$create_array($self,$field) unless defined $self->{$field} ;
1380 3         8 pop @{$self->{$field}}
  3         22  
1381 12         52 };
1382              
1383             $stock{"push"} =
1384             sub {
1385 5     5   346 my ($self, @values) = @_;
1386 5 100       24 &$create_array($self,$field) unless defined $self->{$field} ;
1387 5         7 push @{$self->{$field}}, @values;
  5         30  
1388 12         69 };
1389              
1390             $stock{"shift"} =
1391             sub {
1392 4     4   848 my ($self) = @_;
1393 4 50       19 &$create_array($self,$field) unless defined $self->{$field} ;
1394 4         6 shift @{$self->{$field}}
  4         33  
1395 12         58 };
1396              
1397             $stock{"unshift"} =
1398             sub {
1399 4     4   446 my ($self, @values) = @_;
1400 4 50       21 &$create_array($self,$field) unless defined $self->{$field} ;
1401 4         9 unshift @{$self->{$field}}, @values;
  4         30  
1402 12         48 };
1403              
1404             $stock{"splice"} =
1405             sub {
1406 1     1   4 my ($self, $offset, $len, @list) = @_;
1407 1 50       6 &$create_array($self,$field) unless defined $self->{$field} ;
1408 1         3 splice(@{$self->{$field}}, $offset, $len, @list);
  1         9  
1409 12         51 };
1410              
1411             $stock{"clear"} =
1412             sub {
1413 2     2   289 my ($self) = @_;
1414 2 50       10 &$create_array($self,$field) unless defined $self->{$field} ;
1415 2         5 @{$self->{$field}} = () ;
  2         22  
1416 12         77 };
1417              
1418             $stock{"count"} =
1419             sub {
1420 2     2   692 my ($self) = @_;
1421 2 50       11 &$create_array($self,$field) unless defined $self->{$field} ;
1422 2         5 return scalar @{$self->{$field}} ;
  2         8  
1423 12         43 };
1424              
1425             $stock{"storesize"} =
1426             sub {
1427 1     1   13510 my ($self,$size) = @_;
1428 1 50       12 &$create_array($self,$field) unless defined $self->{$field} ;
1429 1         3 $#{$self->{$field}} = $size - 1 ;
  1         11  
1430 12         48 };
1431              
1432             $stock{"index"} =
1433             sub {
1434 41     41   5284 my $self = shift;
1435 41         82 my (@indices) = @_;
1436 41 100       870 &$create_array($self,$field) unless defined $self->{$field} ;
1437 41         61 my @result = @{$self->{$field}}[@_] ;
  41         237  
1438 41 100       590 return $result[0] if @_ == 1;
1439 1 50       9 return wantarray ? @result : \@result;
1440 12         66 };
1441              
1442             $stock{set} =
1443             sub {
1444 4     4   8 my $self = shift;
1445 4         12 my @args = @_;
1446 4 100       203 croak "${field}_set expects an even number of fields\n"
1447             if @args % 2;
1448 3 50       12 &$create_array($self,$field) unless defined $self->{$field} ;
1449 3         19 while ( my ($index, $value) = splice @args, 0, 2 ) {
1450 3         17 $self->{$field}->[$index] = $value;
1451             }
1452 3         32 return @_ ;#/ 2; # required for object_list
1453 12         74 };
1454              
1455 12         62 foreach my $op (keys %stock)
1456             {
1457 120         164 my $meth = $stock{$op} ;
1458 120         278 $methods->{$field.'_'.$op} = $meth ;
1459             $methods->{$op.'_'.$field} = sub
1460             {
1461 13     13   2118 warn_obsolete("${op}_$field method is obsolete. Please use ${field}_$op");
1462 13         38 $meth->(@_) ;
1463 120         936 } ;
1464             }
1465             }
1466              
1467             =head2 array
1468              
1469             Creates several methods for dealing with slots containing array data.
1470              
1471             array =>
1472             [
1473             'plain_array1', 'plain_array2',
1474             [qw/lot of plain arrayes/] ,
1475             yet_another_plain_array => {} ,
1476              
1477             my_tied_array => {tied_array => 'My_Tie_Array' },
1478             my_tied_array_with_args =>
1479             { tied_array => [ 'My_Tie_Array' , @my_args ] },
1480              
1481             my_array_with_tied_storage => { tie_storage => 'MyTieScalar' },
1482             [qw/likewise_with_args likewise_with_other_args/] =>
1483             { tie_storage => [ 'MyTieScalar', @my_args] }
1484              
1485             my_tied_array_with_tied_storage =>
1486             { tied_array => 'My_Tie_Array',tie_storage => 'MyTieScalar' },
1487              
1488             my_array_with_object => { class_storage => 'MyClass' },
1489             my_array_with_object_and_constructor_args =>
1490             { class_storage => [ 'MyClass' , @my_args ] },
1491              
1492             ]
1493              
1494             The C parameters are:
1495              
1496             =over
1497              
1498             =item *
1499              
1500             A string or a a reference to an array of strings. For each
1501             of these string, a array based slot is created.
1502              
1503             =item *
1504              
1505             A array ref who contains attributes attached to the slot(s) defined by
1506             the previous arguments. These attribute are used to specify the
1507             behavior of the array attached to the slot or to specialize the array
1508             values. See L for details on the possible
1509             attributes.
1510              
1511             =back
1512              
1513             For each slot defined, creates:
1514              
1515             =over 4
1516              
1517             =item x
1518              
1519             This method returns the list of values stored in the slot. In an array
1520             context it returns them as an array and in a scalar context as a
1521             reference to the array. If any arguments are provided to this method,
1522             they I the current list contents.
1523              
1524             =item x_push
1525              
1526             =item x_pop
1527              
1528             =item x_shift
1529              
1530             =item x_unshift
1531              
1532             =item x_splice
1533              
1534             =item x_clear
1535              
1536             =item x_count
1537              
1538             Returns the number of elements in x.
1539              
1540             =item x_index
1541              
1542             Takes a list of indices, returns a list of the corresponding values.
1543              
1544             =item x_set
1545              
1546             Takes a list, treated as pairs of index => value; each given index is
1547             set to the corresponding value. No return.
1548              
1549             =back
1550              
1551             =cut
1552              
1553             sub array
1554             {
1555 8     8 1 23 my ($user_options, @args) = @_;
1556 8         12 my %methods;
1557              
1558             #print "array called with\n", Dumper $user_options, Dumper \@args ;
1559              
1560 8         4496 require Tie::Array::CustomStorage ;
1561              
1562 8         10935 my $parent_method_closure = $user_options->{provide_parent_method} ;
1563              
1564 8         30 while (@args)
1565             {
1566 10         22 my $hash = shift @args ;
1567 10 100       47 my @slot_hash = ( ref($hash) eq 'ARRAY' ) ? @$hash : ($hash);
1568              
1569 10 100       31 my $x_parm = ref $args[0] ? shift @args : undef ;
1570 10         19 my $init_meth = $user_options->{object_init_method} ;
1571             my $create_array = sub
1572             {
1573 11     11   27 my ($self,$name) = @_ ;
1574 11         18 my @array ;
1575 11 100       36 if (defined $x_parm)
1576             {
1577             my $init_obj = sub
1578             {
1579 24         659 my ($l_obj,$l_idx) = @_ ;
1580 24 100       84 graft_parent_method($l_obj,$self,$name,$l_idx)
1581             if $parent_method_closure ;
1582 24         70 $child_init->($l_obj, $init_meth) ;
1583 9         64 } ;
1584              
1585             #print $name,':', Dumper $x_parm ;
1586 9         112 my $custom_tied_obj = tie @array, 'Tie::Array::CustomStorage', %$x_parm,
1587             init_object => $init_obj ;
1588              
1589 9 50       609 my $user_tied_obj = $custom_tied_obj->get_user_tied_array_object
1590             if defined $custom_tied_obj;
1591 9 100 100     140 graft_parent_method($user_tied_obj,$self,$name)
1592             if defined $user_tied_obj and $parent_method_closure ;
1593             }
1594 11         87 $self->{$name} = \@array ;
1595 10         99 };
1596              
1597             my $handle_value = sub
1598             {
1599 0     0   0 my ($self,$name,$key) = splice @_,0,3 ;
1600 0 0       0 return undef unless defined $key ;
1601              
1602             #print "assigning $_[0]\n";
1603 0 0       0 $self->{$name}[$key] = $_[0] if @_;
1604 0 0       0 return @_ ? $_[0] : $self->{$name}[$key];
1605 10         46 } ;
1606              
1607 10         24 foreach my $obj_def (@slot_hash)
1608             {
1609 12         17 my $name = $obj_def; # kept for closures
1610              
1611             $methods{$name} = sub
1612             {
1613 30     30   15371 my $self = shift ;
1614              
1615 30 100       142 &$create_array($self,$name) unless defined $self->{$name} ;
1616              
1617 30 100       85 @{$self->{$name}} = @_ if @_;
  6         51  
1618 30 100       602 return wantarray ? @{$self->{$name}} : $self->{$name} ;
  6         38  
1619 12         63 };
1620              
1621             my $tied_array_sub = sub
1622             {
1623 1     1   2 my $self = shift ;
1624 1 50       3 $create_array->($self,$name) unless defined $self->{$name} ;
1625 1         3 my $custom_tied_obj = tied(@{$self->{$name}}) ;
  1         2  
1626 1 50       4 return undef unless defined $custom_tied_obj ;
1627 1         4 return $custom_tied_obj->get_user_tied_array_object ;
1628 12         42 } ;
1629              
1630 12 100 100     72 if (defined $x_parm and defined $x_parm->{tie_array})
1631             {
1632 7         19 $methods{"tied_array_$name"} = $tied_array_sub ;
1633              
1634             $methods{"tied_$name"} =
1635             sub
1636             {
1637 1     1   6 warn_obsolete( "method tied_$name is deprecated") ;
1638 1         3 return $tied_array_sub->(@_) ;
1639 7         40 } ;
1640             }
1641              
1642             my $tied_storage_sub = sub
1643             {
1644 0     0   0 my $self = shift ;
1645 0         0 my $idx = shift ;
1646 0 0       0 &$create_array($self,$name) unless defined $self->{$name} ;
1647 0         0 &$handle_value($self,$name,$idx) ;
1648 0         0 my $ref = $self->{$name} ;
1649 0         0 return tied(@$ref)->get_tied_storage_object($idx) ;
1650 12         68 } ;
1651              
1652 12 50 66     99 if (defined $x_parm and defined $x_parm->{tie_storage})
1653             {
1654 0         0 $methods{"tied_storage_$name"} = $tied_storage_sub ;
1655             $methods{"tied_scalar_$name"} = sub
1656             {
1657 0     0   0 warn_obsolete( "method tied_scalar_$name is deprecated") ;
1658 0         0 return $tied_storage_sub->(@_) ;
1659 0         0 } ;
1660             }
1661              
1662 12         48 my @info = get_extended_info($x_parm) ;
1663              
1664 12         42 store_slot_in_catalog($name, $user_options->{catalog_name},
1665             slot_type => 'array', @info );
1666              
1667 12         33 _add_array_methods(\%methods, $name, $create_array);
1668             }
1669             }
1670 8         99 install_methods(%methods);
1671             }
1672              
1673              
1674             sub tie_list
1675             {
1676 2     2 0 6 my ($user_options, @args) = @_;
1677 2         6 warn_obsolete( "tie_list is deprecated. Please use array instead");
1678              
1679 2         4 my @new ;
1680 2         6 while (@args)
1681             {
1682 3         7 my $slot = shift @args;
1683 3         5 my $tie_args = shift @args ;
1684              
1685 3         15 push @new, $slot, { tie_array => $tie_args };
1686             }
1687              
1688             #print Dumper \@new ;
1689 2         13 array($user_options, @new ) ;
1690             }
1691              
1692              
1693             sub object_list
1694             {
1695 2     2 0 4 my ($user_options, @args) = @_;
1696 2         7 warn_obsolete( "tie_list is deprecated. Please use array instead");
1697              
1698 2         4 my @new ;
1699 2         6 while (@args)
1700             {
1701 2         5 my $class = shift @args;
1702 2         4 my $item = shift @args ;
1703              
1704 2 100       14 my $slot = ref $item ? delete $item->{slot} : $item
    50          
1705             or croak "object_list: missing slot parameter";
1706              
1707 2 100       19 my @other = ref $item ? %$item : () ;
1708 2         11 push @new, $slot, { class_storage => $class, @other };
1709             }
1710              
1711             #print Dumper \@new ;
1712 2         8 array($user_options, @new ) ;
1713             }
1714              
1715              
1716             sub object_tie_list
1717             {
1718 2     2 0 5 my ($user_options, @args) = @_;
1719 2         8 warn_obsolete( "object_tie_list is deprecated. Please use array instead");
1720              
1721 2         3 my @new ;
1722 2         7 while (@args)
1723             {
1724 2         5 my $h = shift @args ;
1725              
1726 2 50       10 my $slot = delete $h->{slot}
1727             or croak "object_tie_list: missing slot parameter";
1728              
1729 2         6 $h->{class_storage} = delete $h->{class} ;
1730              
1731 2         9 push @new, $slot, $h;
1732             }
1733              
1734             #print Dumper \@new ;
1735 2         16 array($user_options, @new ) ;
1736             }
1737              
1738              
1739             =head1 EXAMPLES
1740              
1741             =head2 Creating an object tree
1742              
1743             You can simply create an object with Class::IntrospectionMethods using
1744             a CIMed class in an C method. For instance, if you want to
1745             create a model of a school clas and their students, you can write:
1746              
1747             Package School_class;
1748              
1749             use Class::IntrospectionMethods
1750             get_set => 'grade',
1751             hash =>
1752             [
1753             student => { class_storage => 'Student'}
1754             ],
1755             new => 'new' ;
1756              
1757             And here is the declaration of the Student class that is used in the
1758             C declararion :
1759              
1760             Package Student ;
1761             use Class::IntrospectionMethods
1762             get_set => 'age',
1763             new => 'new' ;
1764              
1765             Now you can use these lines to get and set the student attributes:
1766              
1767             my $son_class = School_class->new ;
1768             $son_class->grade('first') ;
1769             $son_class->student('Ginger')->age(22) ;
1770              
1771             my $ginger = $son_class->student('Ginger') ;
1772             print $ginger->age ;
1773              
1774             =head1 BUGS
1775              
1776             Z<>
1777              
1778             =head1 REPORTING BUGS
1779              
1780             Email the author.
1781              
1782             =head1 THANKS
1783              
1784             To Martyn J. Pearce for C and the enlightening
1785             discussion we had a while ago about parent and catalog.
1786              
1787             To Matthew Simon Cavalletto for the parameter translation idea that I
1788             pilfered from C.
1789              
1790             =head1 AUTHOR
1791              
1792             Current Maintainer: Dominique Dumont domi@komarr.grenoble.hp.com
1793              
1794             Original Authors: Martyn J. Pearce fluffy@cpan.org, Peter Seibel (Organic Online)
1795              
1796             Contributions from:
1797              
1798             Evolution Online Systems, Inc. http://www.evolution.com
1799             Matthew Persico
1800             Yitzchak Scott-Thoennes
1801              
1802             =head1 COPYRIGHT
1803              
1804             Copyright (c) 2004 Dominique Dumont. This program is free
1805             software; you can redistribute it and/or modify it under the same terms as
1806             Perl itself.
1807              
1808             Copyright (c) 2002, 2001, 2000 Martyn J. Pearce. This program is free
1809             software; you can redistribute it and/or modify it under the same terms as
1810             Perl itself.
1811              
1812             Copyright 1998, 1999, 2000 Evolution Online Systems, Inc. You may use
1813             this software for free under the terms of the MIT License. More info
1814             posted at http://www.evolution.com, or contact info@evolution.com
1815              
1816             Copyright (c) 1996 Organic Online. All rights reserved. This program is
1817             free software; you can redistribute it and/or modify it under the same
1818             terms as Perl itself.
1819              
1820             =head1 SEE ALSO
1821              
1822             C, C, C,
1823             "Object-Oriented Perl" by Damian
1824             Conway. C, C,
1825             C,
1826             C
1827              
1828             =cut