File Coverage

blib/lib/classes.pm
Criterion Covered Total %
statement 501 764 65.5
branch 188 348 54.0
condition 71 160 44.3
subroutine 38 72 52.7
pod 14 14 100.0
total 812 1358 59.7


line stmt bran cond sub pod time code
1             package classes;
2              
3             # $Id: classes.pm 147 2008-03-08 16:04:33Z rmuhle $
4              
5             our $VERSION = '0.944';
6 38     38   555652 use 5.006_001;
  24         97  
  24         1075  
7 30     30   2407 use Scalar::Util 'reftype', 'blessed'; # standard from 5.8.1
  24         42  
  24         3372  
8              
9             # keep false alarms quiet
10 30     30   2706 use strict; no strict 'refs'; no warnings;
  24     30   48  
  24     25   1806  
  30         2727  
  24         40  
  24         697  
  25         622  
  24         40  
  24         2154  
11              
12             sub classes (@); *define = \&classes::classes;
13             sub load;
14              
15             # fastest PERL_VERSION constant (from Schwern's CLASS module)
16 25     25   2327 BEGIN{ *PERL_VERSION = eval "sub () { $] } " }
17              
18             # do not change
19             $classes::ok_class_name
20             = qr/^(?!(?:
21             B$| # binary module
22             _$
23             )
24             )
25             (?i:(?:[a-z_]\w*\:\:)*[a-z_]\w*)$/xo;
26             $classes::ok_attr_name = qr/^(?i:[a-z_]\w*)$/xo;
27              
28             # this one changable via tag
29             $classes::def_base_exception = 'X::classes::traceable';
30              
31             ######################################################################
32              
33             sub import {
34 162     162   2828 my $package = shift;
35 162         202 my (@class_declarations, $dynamic);
36 162         293 my $caller = caller;
37              
38             # use classes DECLARATION;
39 162 100       508 if ( !ref $_[0] ) {
40 160 50       470 push @_, 1 if @_ == 1;
41 160         748 push @class_declarations, {@_};
42             }
43              
44             # use classes {DECLARATION}, {DECLARATION};
45             else {
46 2         4 @class_declarations = @_;
47             }
48              
49             # implied name
50 162         310 map { $_->{'caller'} = $caller } @class_declarations;
  162         500  
51              
52 162         378 classes::classes(@class_declarations);
53              
54 162         440908 return $package;
55             }
56              
57             ######################################################################
58             # Called by import() itself to define classes at compile-time.
59             # Exported into anything that calls 'use classes'.
60             sub classes (@) {
61 431     431 1 890 my @args = @_;
62 431         664 my $tags;
63              
64             # classes { DECLARATION };
65 431 100       1226 if ( ref $args[0] eq 'HASH' ) {
    50          
66 426         525 $tags = $args[0];
67 426 100       1012 map { classes $_ } @args if @args > 1; # recurse
  240         1353  
68             }
69              
70             # classes DECLARATION;
71             elsif ( @args > 1 ) {
72 5         15 $tags = {@args};
73             }
74              
75             #-----------------------------------------------------------------
76              
77 431         634 my ($class, $type, $inherits, $extends,
78             $throws, $exceptions, $attrs, $methods,
79             $class_attrs, $class_attrs_ro, $class_methods, $attrs_ro,
80             $new_m, $init_m, $clone_m, $caller,
81             $mixes, $mixes_def, $class_mixes, $dump_m,
82             $base_exception, $unqualified, $pkg_mixes, $needs,
83             $pkg_methods, $needs, $attrs_pr, $justahash,
84             $class_attrs_pr, $noaccessors, $def_base_exception,
85             );
86              
87 431         8021 my $lookup = {
88             caller => \$caller,
89             name => \$class,
90             attrs => \$attrs,
91             attrs_ro => \$attrs_ro,
92             attrs_pr => \$attrs_pr,
93             class_attrs => \$class_attrs,
94             class_attrs_ro => \$class_attrs_ro,
95             class_attrs_pr => \$class_attrs_pr,
96             mixes => \$mixes,
97             class_mixes => \$class_mixes,
98             pkg_mixes => \$pkg_mixes,
99             mixes_def => \$mixes_def,
100             methods => \$methods,
101             class_methods => \$class_methods,
102             pkg_methods => \$pkg_methods,
103             throws => \$throws,
104             needs => \$needs,
105             extends => \$extends,
106             inherits => \$inherits,
107             type => \$type,
108             exceptions => \$exceptions,
109             base_exception => \$base_exception,
110             new => \$new_m,
111             init => \$init_m,
112             clone => \$clone_m,
113             dump => \$dump_m,
114             def_base_exception => \$def_base_exception,
115             unqualified => \$unqualified,
116             noaccessors => \$noaccessors,
117             justahash => \$justahash,
118             };
119              
120 431         2193 while ( my ( $tag, $val ) = each %$tags ) {
121 1360 50       11701 my $ref = $lookup->{$tag}
122             or X::Usage->throw( "$tag ??" );
123 1360         4724 $$ref = $val;
124             }
125              
126 431   33     1276 $class ||= $caller || caller;
      66        
127 431 50       3961 X::InvalidName->throw("name=>'$class'")
128             if $class !~ $classes::ok_class_name; # main ok
129              
130             #-----------------------------------------------------------------
131             # add CLASS and $CLASS constants (from Michael Schwern's CLASS)
132              
133 431         814 my $const_CLASS = $class . '::CLASS';
134 431 100       3327 if (!$$const_CLASS) {
135 406         946 *$const_CLASS = \$class;
136 406         463 if ( PERL_VERSION >= 5.008 ) {
137 406     0   2164 *$const_CLASS = sub () {$class};
  0         0  
138             }
139             else {
140             *$const_CLASS = eval " sub () { q[$class] } ";
141             }
142             }
143              
144             #-----------------------------------------------------------------
145              
146 431         880 my $class_decl = $class . '::DECL';
147 431 100       2065 if (!*$class_decl{CODE}) {
148 406         1469 $$class_decl->{'name'} = $class;
149 406   50     1892 $$class_decl->{'type'} ||= 'static';
150 406         418 if ( PERL_VERSION >= 5.008 ) {
151 406     0   1832 *$class_decl = sub () { $$class_decl };
  0         0  
152             }
153             else {
154             *$class_decl = eval 'sub () { $' . $class_decl . '}';
155             }
156             }
157              
158             #-----------------------------------------------------------------
159              
160             # setup the special MIXIN method and var
161 431         1256 my $class_MIXIN = $class . '::MIXIN';
162 431 100       2400 if (!*$class_MIXIN{CODE}) {
163 406         814 $$class_MIXIN = undef;
164 406         427 if ( PERL_VERSION >= 5.008 ) {
165 406     0   2144 *$class_MIXIN = sub () { $$class_MIXIN };
  0         0  
166             }
167             else {
168             *$class_MIXIN = eval 'sub () { $' . $class_MIXIN . '}';
169             }
170             }
171              
172             #-----------------------------------------------------------------
173              
174 431 100       917 $classes::def_base_exception = $def_base_exception
175             if $def_base_exception;
176              
177             #-----------------------------------------------------------------
178              
179             # keep using a type that has already been declared
180 431 100       723 if ($type) {
181 37 50 100     399 X::Usage->throw( "type=>'$type' ??\n"
      66        
182             . " type => 'static'|'dynamic'|'mixable'\n" )
183             if ! ( $type eq 'static'
184             || $type eq 'dynamic'
185             || $type eq 'mixable' );
186 37         128 $$class_decl->{'type'} = $type;
187             }
188             else {
189 394         980 $type = $$class_decl->{'type'};
190             }
191              
192             #-----------------------------------------------------------------
193             # export a 'classes' function/statement into caller
194              
195 431 100       1064 *{ $class . '::classes' } = \&classes::classes
  3         12  
196             if $type eq 'dynamic';
197              
198             #-----------------------------------------------------------------
199              
200 431         737 my $class_super = $class . '::SUPER';
201 431 100       1919 if (!*$class_super{CODE}) {
202 406         413 if ( PERL_VERSION >= 5.008 ) {
203 406     0   1740 *$class_super = sub () { ${ $class.'::ISA' }[0] };
  0         0  
  0         0  
204             }
205             else {
206             *$class_super = eval 'sub () { $'.$class.'::ISA[0] }';
207             }
208             }
209              
210             #-----------------------------------------------------------------
211              
212 431 100       1344 _define_mixins( $class, $mixes, $mixes_def, '')
213             if defined $mixes;
214 431 50       851 _define_mixins( $class, $class_mixes, $mixes_def, 'class_' )
215             if defined $class_mixes;
216 431 50       804 _define_mixins( $class, $pkg_mixes, $mixes_def, 'pkg_' )
217             if defined $pkg_mixes;
218              
219             #-----------------------------------------------------------------
220             # same as 'throws' for now
221              
222             # 'needs' IS DEPRECATED, use perl 'use' instead, too many
223             # compilers/builders depend on 'use' to identify needed modules
224 431 50       783 if ($needs) {
225 0         0 my $is_ref = ref $needs;
226              
227             # needs => 'SomeOtherMod',
228 0 0       0 if ( !$is_ref ) {
229 0         0 $needs = [$needs];
230 0         0 $is_ref = 'ARRAY';
231             }
232              
233 0 0       0 X::Usage->throw( <<'EOM' ) if $is_ref ne 'ARRAY';
234              
235             DEPRECATED, use perl 'use' instead, too many compilers/builders
236             depend on 'use' to identify which modules are needed
237             EOM
238              
239 0         0 for my $pkg (@$needs) {
240 0         0 classes::load($pkg);
241 0         0 push @{ $$class_decl->{'needs'} }, $pkg;
  0         0  
242             }
243             } # needs
244              
245              
246             #-----------------------------------------------------------------
247              
248 431 100       782 if ($throws) {
249 1         3 my $is_ref = ref $throws;
250              
251             # throws => 'X::Usage',
252 1 50       3 if ( !$is_ref ) {
253 1         3 $throws = [$throws];
254 1         2 $is_ref = 'ARRAY';
255             }
256              
257 1 50       9 X::Usage->throw( <<'EOM' ) if $is_ref ne 'ARRAY';
258              
259             throws => 'X::Usage',
260             throws => [ 'X::Usage', 'X::Mine::NotFound' ],
261             EOM
262              
263 1         2 for my $exc (@$throws) {
264 1         2 classes::load($exc);
265 1         1 push @{ $$class_decl->{'throws'} }, $exc;
  1         5  
266             }
267             } # throws
268              
269             #-----------------------------------------------------------------
270            
271 431 100       718 if ($base_exception) {
272              
273             # base_exception => {name=>'X::MyBase',},
274 1 50       4 if (ref $base_exception) {
275 0         0 classes::classes($base_exception);
276 0         0 $base_exception = $base_exception->{'name'};
277             }
278              
279             # base_exception => 'X::class',
280             # base_exception => 'Exception::Class',
281             else {
282 1         4 classes::load($base_exception);
283             }
284              
285 1         4 $$class_decl->{'base_exception'} = $base_exception;
286             }
287              
288             #-----------------------------------------------------------------
289              
290 431 100       913 if ($exceptions) {
291 3         7 my $is_ref = ref $exceptions;
292 3         7 my $usage = <<'EOM';
293              
294             exceptions => 'X::MyClass::Own1',
295             exceptions => [
296             'X::MyClass::Own1',
297             'X::MyClass::Own2',
298             { name=>'X::MyClass::EOF', attrs=>['file'], ... },
299             ],
300             EOM
301              
302             # go with base exception class if one is declared
303 3         7 my $base_x_class = $base_exception;
304            
305             # otherwise define X::MyClass default base
306 3 100       12 if (!$base_x_class) {
307 2         5 $base_x_class = "X::$class";
308              
309             # but create only if hasn't been created already
310 2 50       3 if (!%{$base_x_class.'::'}) {
  2         14  
311 2         10 classes::classes(
312             name => $base_x_class,
313             extends => $classes::def_base_exception,
314             );
315             }
316             }
317              
318 3 50       15 if ($is_ref ne 'ARRAY') {
319 3         9 $exceptions = [$exceptions];
320 3         7 $is_ref = 'ARRAY';
321             }
322              
323 3         10 for my $exc (@$exceptions) {
324 3         6 my $exc_is_ref = ref $exc;
325              
326             # exceptions => ['X::Name']
327 3 50       17 if (!$exc_is_ref) {
    0          
328 3 50       4 X::NameUnavailable->throw([$exc]) if %{$exc.'::'};
  3         24  
329 3         10 classes::classes(name=>$exc, extends=>$base_x_class);
330              
331             }
332              
333             # exceptions => [{name=>'X::Name',attrs=>['file']}]
334             elsif ($exc_is_ref eq 'HASH') {
335 0         0 my $name = $exc->{'name'};
336 0   0     0 $exc->{'extends'} ||= $base_x_class;
337 0         0 X::NameUnavailable->throw([$name])
338 0 0       0 if %{$name.'::'};
339 0         0 classes::classes($exc);
340             }
341            
342             # bad
343             else {
344 0         0 X::Usage->throw($exceptions . $usage);
345             }
346              
347 3         6 push @{ $$class_decl->{'exceptions'} }, $exc;
  3         20  
348             }
349              
350             } # exceptions
351              
352             #-----------------------------------------------------------------
353              
354             # extends trumps inherits
355 431 100       860 if ($extends) {
356 305 50 33     1297 X::Usage->throw(
357             "extends=>'parent' OR inherits=>[qw( Parent1 Parent2 )]"
358             )
359             if $inherits || ref $extends;
360 305         721 classes::load($extends);
361 305         462 @{$class . '::ISA'} = ($extends);
  305         9180  
362 305         1128 $$class_decl->{'extends'} = $extends;
363 305         759 delete $$class_decl->{'inherits'};
364             }
365              
366             #-----------------------------------------------------------------
367              
368 431 100       832 if ($inherits) {
369 6         12 my $is_ref = ref $inherits;
370 6         8 my $usage = <<'EOM';
371              
372             extends => 'SuperClass',
373             inherits => 'SuperClass',
374             inherits => [qw( Class1 Class2 )],
375             EOM
376              
377 6 100       15 if (!$is_ref) {
378 5         12 $inherits = [$inherits];
379 5         15 $is_ref = 'ARRAY';
380             }
381              
382 6 50 33     34 X::Usage->throw($usage)
383             if !$is_ref || $is_ref ne 'ARRAY';
384              
385 6         13 for my $parent (@$inherits) {
386 7 50       79 next if $class->isa($parent);
387 7         17 classes::load($parent);
388 7         10 push @{ $class . '::ISA' }, $parent;
  7         83  
389 7         21 push @{ $$class_decl->{'inherits'} }, $parent;
  7         30  
390             }
391              
392             } # inherits
393              
394             #-----------------------------------------------------------------
395              
396 431 100       842 if ($class_attrs_ro) {
397 5         11 my $is_ref = ref $class_attrs_ro;
398 5         8 my $usage = <<'EOM';
399              
400             class_attrs_ro => [qw( attr1 attr2 )],
401             class_attrs_ro => {
402             attr0 => undef,
403             attr1 => 1,
404             attr2 => 'scalar',
405             attr3 => \$scalar_ref,
406             attr4 => [ 'array', 'ref' ],
407             attr5 => { 'hash' => 'ref' },
408             attr6 => sub { ... },
409             attr7 => qr/^Get the idea(?:\?|\!)$/o,
410             attr8 => 0,
411             attr9 => '',
412             },
413             EOM
414 5 100       17 if ( $is_ref eq 'ARRAY' ) {
    50          
415 4         10 for my $name (@$class_attrs_ro) {
416 4 50 33     33 X::InvalidName->throw(
417             "class_attrs_ro=>['$name']"
418             ) if !$name || $name !~ $classes::ok_attr_name;
419 4         12 _define_class_attr( $class, $name => undef, 'ro' );
420             }
421             }
422             elsif ( $is_ref eq 'HASH' ) {
423 1         6 while ( my ( $name, $i_value ) = each %$class_attrs_ro ) {
424 1 50 33     8 X::InvalidName->throw(
425             "class_attrs_ro=>['$name']"
426             ) if !$name || $name !~ $classes::ok_attr_name;
427 1         4 _define_class_attr( $class, $name => $i_value, 'ro' );
428             }
429             }
430             else {
431 0         0 X::Usage->throw($usage);
432             }
433             } # class_attrs_ro
434              
435             #-----------------------------------------------------------------
436              
437 431 100       836 if ($class_attrs_pr) {
438 5         12 my $is_ref = ref $class_attrs_pr;
439 5         7 my $usage = <<'EOM';
440              
441             class_attrs_pr => [qw( attr1 attr2 )],
442             class_attrs_pr => {
443             attr0 => undef,
444             attr1 => 1,
445             attr2 => 'scalar',
446             attr3 => \$scalar_ref,
447             attr4 => [ 'array', 'ref' ],
448             attr5 => { 'hash' => 'ref' },
449             attr6 => sub { ... },
450             attr7 => qr/^Get the idea(?:\?|\!)$/o,
451             attr8 => 0,
452             attr9 => '',
453             },
454             EOM
455 5 50       11 if ( $is_ref eq 'ARRAY' ) {
    0          
456 5         13 for my $name (@$class_attrs_pr) {
457 5 50 33     40 X::InvalidName->throw(
458             "class_attrs_pr=>['$name']"
459             ) if !$name || $name !~ $classes::ok_attr_name;
460 5         16 _define_class_attr( $class, $name => undef, 'pr' );
461             }
462             }
463             elsif ( $is_ref eq 'HASH' ) {
464 0         0 while ( my ( $name, $i_value ) = each %$class_attrs_pr ) {
465 0 0 0     0 X::InvalidName->throw(
466             "class_attrs_pr=>['$name']"
467             ) if !$name || $name !~ $classes::ok_attr_name;
468 0         0 _define_class_attr( $class, $name => $i_value, 'pr' );
469             }
470             }
471             else {
472 0         0 X::Usage->throw($usage);
473             }
474             } # class_attrs_pr
475              
476             #-----------------------------------------------------------------
477              
478 431 100       979 if ($class_attrs) {
479 42         105 my $is_ref = ref $class_attrs;
480 42         75 my $usage = <<'EOM';
481              
482             class_attrs => [qw( attr1 attr2 )],
483             class_attrs => {
484             attr0 => undef,
485             attr1 => 1,
486             attr2 => 'scalar',
487             attr3 => \$scalar_ref,
488             attr4 => [ 'array', 'ref' ],
489             attr5 => { 'hash' => 'ref' },
490             attr6 => sub { ... },
491             attr7 => qr/^Get the idea(?:\?|\!)$/o,
492             attr8 => 0,
493             attr9 => '',
494             },
495             EOM
496              
497 42 100       200 if ( $is_ref eq 'ARRAY' ) {
    50          
498 2         5 for my $name (@$class_attrs) {
499 2 50 33     20 X::InvalidName->throw(
500             "class_attrs=>['$name']"
501             ) if !$name || $name !~ $classes::ok_attr_name;
502 2         8 _define_class_attr( $class, $name => undef );
503             }
504             }
505             elsif ( $is_ref eq 'HASH' ) {
506 40         199 while ( my ( $name, $i_value ) = each %$class_attrs ) {
507 136 50 33     1000 X::InvalidName->throw(
508             "class_attrs=>['$name']"
509             ) if !$name || $name !~ $classes::ok_attr_name;
510 136         314 _define_class_attr( $class, $name => $i_value );
511             }
512             }
513             else {
514 0         0 X::Usage->throw($usage);
515             }
516              
517             } # class_attrs
518              
519             #-----------------------------------------------------------------
520              
521 431 100       684 if ($justahash) {
522 1         8 $$class_decl->{'justahash'} = $justahash;
523 1         4 $noaccessors = 1;
524 1         3 $unqualified = 1;
525 1   50     10 $new_m ||= 'classes::new_fast';
526             }
527             else {
528 430 100       747 $$class_decl->{'unqualified'} = $unqualified if $unqualified;
529 430 100       951 $$class_decl->{'noaccessors'} = $noaccessors if $noaccessors;
530             }
531              
532             #-----------------------------------------------------------------
533              
534 431 100       791 if ($attrs_ro) {
535 29         80 my $is_ref = ref $attrs_ro;
536 29         79 my $usage = <<'EOM';
537              
538             attrs_ro => [qw( attr1 attr2 )],
539             EOM
540 29 50       144 if ( $is_ref eq 'ARRAY' ) {
541 29         79 for my $name (@$attrs_ro) {
542 269 50 33     1659 X::InvalidName->throw(
543             "attrs_ro=>['$name']"
544             ) if !$name || $name !~ $classes::ok_attr_name;
545 269         597 _define_attr( $class, $name, 'ro',
546             $unqualified, $noaccessors );
547             }
548             }
549             else {
550 0         0 X::Usage->throw($usage);
551             }
552              
553             } # attrs_ro
554              
555             #-----------------------------------------------------------------
556              
557 431 100       1159 if ($attrs_pr) {
558 4         8 my $is_ref = ref $attrs_pr;
559 4         5 my $usage = <<'EOM';
560              
561             attrs_pr => [qw( attr1 attr2 )],
562             EOM
563 4 50       12 if ( $is_ref eq 'ARRAY' ) {
564 4         6 for my $name (@$attrs_pr) {
565 4 50 33     28 X::InvalidName->throw(
566             "attrs_pr=>['$name']"
567             ) if !$name || $name !~ $classes::ok_attr_name;
568 4         10 _define_attr( $class, $name, 'pr',
569             $unqualified, $noaccessors );
570             }
571             }
572             else {
573 0         0 X::Usage->throw($usage);
574             }
575              
576             } # attrs_pr
577             #-----------------------------------------------------------------
578              
579 431 100       977 if ($attrs) {
580 47         127 my $is_ref = ref $attrs;
581 47         101 my $usage = <<'EOM';
582              
583             attrs => [qw( attr1 attr2 )],
584             EOM
585 47 50       167 if ( $is_ref eq 'ARRAY' ) {
586 47         1105 for my $name (@$attrs) {
587 120 50 33     871 X::InvalidName->throw( "attrs=>['$name']" )
588             if !$name || $name !~ $classes::ok_attr_name;
589 120         366 _define_attr( $class, $name, '',
590             $unqualified, $noaccessors );
591             }
592             }
593             else {
594 0         0 X::Usage->throw($usage);
595             }
596              
597             } # attrs
598              
599             #-----------------------------------------------------------------
600              
601 431 100       986 if ($class_methods) {
602 34         99 my $is_ref = ref $class_methods;
603 34         57 my $usage = <<'EOM';
604              
605             class_methods => [ 'method1', 'method2' ],
606             class_methods => {
607             method1 => 'method1',
608             method2 => 'local_method',
609             method3 => 'Extern::library::method',
610             method4 => 'ABSTRACT',
611             method5 => | 'EMPTY',
612             method6 => sub { ... } | $code_ref | \&some::method,
613             },
614             EOM
615 34 100       151 if ( $is_ref eq 'ARRAY' ) {
    50          
616 2         5 for my $name (@$class_methods) {
617 2         7 _define_method( $class, $name => $name, 'class_' );
618             }
619             }
620             elsif ( $is_ref eq 'HASH' ) {
621 32         184 while ( my ( $name, $method ) = each %$class_methods ) {
622 81         132 my $is_ref = ref $method;
623 81         188 _define_method( $class, $name => $method, 'class_');
624             }
625             }
626             else {
627 0         0 X::Usage->throw($usage);
628             }
629             } # class_methods
630              
631             #---------------------------------------------------------------
632              
633 431 50       851 if ($pkg_methods) {
634 0         0 my $is_ref = ref $pkg_methods;
635 0         0 my $usage = <<'EOM';
636              
637             pkg_methods => [ 'method1', 'method2' ],
638             pkg_methods => {
639             method1 => 'method1',
640             method2 => 'local_method',
641             method3 => 'Extern::library::method',
642             method4 => 'ABSTRACT',
643             method5 => | 'EMPTY',
644             method6 => sub { ... } | $code_ref | \&some::method,
645             },
646             EOM
647 0 0       0 if ( $is_ref eq 'ARRAY' ) {
    0          
648 0         0 for my $name (@$pkg_methods) {
649 0         0 _define_method( $class, $name => $name, 'pkg_' );
650             }
651             }
652             elsif ( $is_ref eq 'HASH' ) {
653 0         0 while ( my ( $name, $method ) = each %$pkg_methods ) {
654 0         0 my $is_ref = ref $method;
655 0         0 _define_method( $class, $name => $method, 'pkg_');
656             }
657             }
658             else {
659 0         0 X::Usage->throw($usage);
660             }
661              
662             # allows pkg_methods to be imported on request
663 0         0 my $class_decl = $class . '::DECL';
664 0         0 *{$class.'::import'} = sub {
665 0     0   0 my $package = shift;
666 0         0 my $caller = caller;
667              
668             # use MyPackage qw(my_pkg_method another_function);
669 0 0 0     0 if ($_[0] and $_[0] ne ':all') {
670 0         0 for my $method (@_) {
671 0 0       0 X::NotPkgMethod->throw([$method])
672             if !$$class_decl->{'pkg_methods'}->{$method};
673 0         0 *{$caller.'::'.$method} = \&{$class.'::'.$method};
  0         0  
  0         0  
674             }
675             }
676            
677             # use MyPackage ':all';
678             else {
679 0         0 for my $method (keys %{$$class_decl->{'pkg_methods'}}){
  0         0  
680 0         0 *{$caller.'::'.$method} = \&{$class.'::'.$method};
  0         0  
  0         0  
681             }
682             }
683 0         0 return $package;
684 0         0 };
685              
686             } # pkg_methods
687              
688             #---------------------------------------------------------------
689              
690 431 100       826 if ($methods) {
691 82         176 my $is_ref = ref $methods;
692 82         128 my $usage = <<'EOM';
693              
694             methods => [ 'method1', 'method2' ],
695             methods => {
696             method1 => 'method1',
697             method2 => 'local_method',
698             method3 => 'Extern::library::method',
699             method4 => 'ABSTRACT',
700             method5 => || 'EMPTY',
701             method6 => sub { ... } || $code_ref || \&some::method,
702             },
703             EOM
704 82 100       294 if ( $is_ref eq 'ARRAY' ) {
    50          
705 51         119 for my $name (@$methods) {
706 52         166 _define_method( $class, $name => $name, '' );
707             }
708             }
709             elsif ( $is_ref eq 'HASH' ) {
710 31         158 while ( my ( $name, $method ) = each %$methods ) {
711 79         162 _define_method( $class, $name => $method, '' );
712             }
713             }
714             else {
715 0         0 X::Usage->throw($usage);
716             }
717              
718             } # methods
719              
720             #---------------------------------------------------------------
721             # new, clone, initialize, and dump shortcuts
722              
723 431 100       999 _define_method( $class, 'initialize' => $init_m, '' ) if $init_m;
724 431 100       849 _define_method( $class, 'clone' => $clone_m, '' ) if $clone_m;
725 431 100       977 _define_method( $class, 'dump' => $dump_m, '' ) if $dump_m;
726 431 100       955 _define_method( $class, 'new' => $new_m, 'class_' ) if $new_m;
727              
728 431         3209 return $class;
729             }
730              
731             ####################################################################
732              
733             sub load {
734 363     363 1 493 my $pkg = shift;
735              
736 363 50       2347 X::InvalidName->throw( [$pkg] )
737             if $pkg !~ $classes::ok_class_name;
738              
739             # from 'base', don't bother loading if have VERSION,
740 363         413 my $vglob = ${$pkg.'::'}{VERSION};
  363         1329  
741 363 100 100     1849 return $pkg if $vglob && *$vglob{SCALAR};
742              
743             # unlike 'base' that does a 'require', we 'use' instead to
744 107         553 local $SIG{__DIE__} = 'IGNORE';
745 24     24   11531 eval "use $pkg";
  0     24   0  
  0     24   0  
  24         10304  
  0         0  
  0         0  
  24         10322  
  0         0  
  0         0  
  107         10802  
746              
747             # unlike 'base' that does a 'require', we 'use' instead to
748             # propogate exception as is unless "can't locate", which is
749             # expected when the base pkg is defined in same file, etc.
750             # had to remove the '^' initial match because of braindead
751             # idiot programs like Indigo's perl2exe that stick a bunch
752             # of irrelevant text before the actual expected error string
753 107 50 33     1812 die if $@ && $@ !~ /Can't locate .*? at \(eval /o;
754              
755             # problem if the pkg doesn't have any var or sub symbols in it
756             # milage may vary when loading pkgs that use DynaLoader (ugh)
757 107 50       163 X::Empty->throw([$pkg]) if !%{$pkg.'::'};
  107         613  
758              
759             # for 'use base' compatibility, if the loaded pkg didn't have
760             # a VERSION of its own give it one so we don't load it again
761 107         493 ${$pkg.'::VERSION'} = "-1, set by classes.pm"
  107         809  
762 107 50       199 if !defined ${$pkg.'::VERSION'};
763              
764 107         395 return $pkg;
765             }
766              
767              
768             ####################################################################
769              
770             sub _define_mixins {
771 48     48   108 my ($class, $mixes, $mixes_def, $type) = @_;
772 48         98 my $is_ref = ref $mixes;
773 48         118 my $class_DECL = $class.'::DECL';
774 48         145 my $class_MIXIN = $class.'::MIXIN';
775              
776 48         84 my $usage = <<'EOM';
777              
778             [class_|pkg_]mixes => 'Module',
779             [class_|pkg_]mixes => { Module => ... },
780             [class_|pkg_]mixes => [
781             'Module1',
782             { Module2 => ['method1'] },
783             { Module3 => 'ALL'|'PUB'|'SAFE' },
784             { Module4 => qr/.../ },
785             { Module5 => ... , scope=>'CLASS' },
786             ],
787              
788             mixes_def => 'ALL'|'PUB'|'SAFE',
789             EOM
790             # mixes => 'Module1',
791             # mixes => { Module1 => ... },
792 48 100 100     246 if ( !$is_ref || $is_ref eq 'HASH' ) {
793 47         114 $mixes = [$mixes];
794 47         86 $is_ref = 'ARRAY';
795             }
796              
797 48 50       138 X::Usage->throw($usage) if $is_ref ne 'ARRAY';
798              
799             MIXIN:
800 48         105 for my $module ( reverse @$mixes ) { # first wins
801              
802 49         127 my $is_ref = ref $module;
803 49   100     219 my $filter = $mixes_def || 'SAFE';
804              
805             # mixes => [ 'Module1' ],
806 49 100       126 if ( !$is_ref ) {
807 47         208 $module = { $module => $filter };
808 47         82 $is_ref = 'HASH';
809             }
810              
811 49 50       132 X::Usage->throw($usage) if $is_ref ne 'HASH';
812              
813             # mixes => [ { Module1 => ... } ],
814 49         142 ($module, $filter) = each %$module; # only one left
815              
816 49 50 33     617 X::InvalidName->throw( "mixes=>'$module' ??" )
817             if !$module || $module !~ $classes::ok_class_name;
818              
819             # only SAFE, PUB, and ALL acceptable non-refs
820 49         92 my $filter_is_ref = ref $filter;
821 49 100       521 if ( !$filter_is_ref ) {
822             {
823 45 100       69 $filter = qr/^(?!(?:_|\d|[A-Z0-9_]+$))/o,
  45         305  
824             last if $filter eq 'SAFE';
825 4 100       12 $filter = qr/^
826             (?!_|BEGIN|CHECK|END|INIT|CLONE
827             |CLASS|SUPER|DECL|MIXIN)
828             [a-z_]\w*$/xoi,
829             last if $filter eq 'PUB';
830 3 50       13 $filter = qr/^
831             (?!BEGIN|CHECK|END|INIT|CLONE
832             |CLASS|SUPER|DECL|MIXIN)
833             [a-z_]\w*$/xoi,
834             last if $filter eq 'ALL';
835 0         0 X::Usage->throw($filter." ??\n".$usage);
836             }
837 45         96 $filter_is_ref = 'Regexp';
838             }
839            
840 49         139 classes::load($module);
841 49         110 my $mod_sym = $module.'::';
842              
843             # unfortunately the following causes an empty DECL
844             # to autovivify if not one there already, perl's problem
845 49         68 my $mod_DECL = ${"$module\::DECL"};
  49         150  
846              
847 49         67 my $mod_type = ${"$module\::DECL"}->{'type'};
  49         166  
848              
849             # if declared AND type is set by author to 'mixin'
850             # go ahead and only use the declaration.
851              
852 49 100 100     272 if ( $mod_type and $mod_type eq 'mixable' ) {
853 35         60 my $methods = $mod_DECL->{'methods'};
854 35         66 my $class_methods = $mod_DECL->{'class_methods'};
855 35         63 my $pkg_methods = $mod_DECL->{'pkg_methods'};
856 35         57 my $attrs = $mod_DECL->{'attrs'};
857 35         59 my $attrs_ro = $mod_DECL->{'attrs_ro'};
858 35         59 my $attrs_pr = $mod_DECL->{'attrs_pr'};
859 35         51 my $class_attrs = $mod_DECL->{'class_attrs'};
860 35         55 my $class_attrs_ro = $mod_DECL->{'class_attrs_ro'};
861 35         58 my $class_attrs_pr = $mod_DECL->{'class_attrs_pr'};
862              
863 35         175 while ( my ($name, $ivalue) = each %$class_methods ) {
864 86         247 _clear_method_name($class, $name);
865 86         190 _mixin( $module => $class, $name );
866 86         440 $$class_DECL->{'class_methods'}->{$name} = $ivalue;
867             }
868              
869 35         162 while ( my ($name, $ivalue) = each %$methods ) {
870 87         204 _clear_method_name($class, $name);
871 87         173 _mixin( $module => $class, $name );
872 87         418 $$class_DECL->{'methods'}->{$name} = $ivalue;
873             }
874              
875 35         166 while ( my ($name, $ivalue) = each %$pkg_methods ) {
876 0         0 _clear_method_name($class, $name);
877 0         0 _mixin( $module => $class, $name );
878 0         0 $$class_DECL->{'pkg_methods'}->{$name} = $ivalue;
879             }
880              
881 35         98 for my $name ( @$attrs ) {
882 8         22 _clear_method_name($class, "set_$name");
883 8         20 _clear_method_name($class, "get_$name");
884 8         20 _mixin( $module => $class, "set_$name" );
885 8         17 _mixin( $module => $class, "get_$name" );
886 8         16 _mixin_key( $module => $class, $name );
887 8         10 push @{$$class_DECL->{'attrs'}}, $name;
  8         28  
888             }
889              
890 35         135 for my $name ( @$attrs_ro ) {
891 4         13 _clear_method_name($class, "set_$name");
892 4         8 _clear_method_name($class, "get_$name");
893 4         9 _mixin( $module => $class, "get_$name" );
894 4         8 _mixin_key( $module => $class, $name );
895 4         4 push @{$$class_DECL->{'attrs_ro'}}, $name;
  4         15  
896             }
897              
898 35         76 for my $name ( @$attrs_pr ) {
899 4         9 _clear_method_name($class, "set_$name");
900 4         9 _clear_method_name($class, "get_$name");
901 4         7 _mixin_key( $module => $class, $name );
902 4         4 push @{$$class_DECL->{'attrs_pr'}}, $name;
  4         13  
903             }
904              
905 35         187 while ( my ($name, $ivalue) = each %$class_attrs ) {
906 8         21 _clear_method_name($class, "set_$name");
907 8         21 _clear_method_name($class, "get_$name");
908 8         19 _mixin( $module => $class, "set_$name" );
909 8         44 _mixin( $module => $class, "get_$name" );
910 8         20 _mixin_key( $module => $class, $name, 'class' );
911 8         45 $$class_DECL->{'class_attrs'}->{$name} = $ivalue;
912             }
913              
914 35         132 while ( my ($name, $ivalue) = each %$class_attrs_ro ) {
915 4         9 _clear_method_name($class, "set_$name");
916 4         9 _clear_method_name($class, "get_$name");
917 4         9 _mixin( $module => $class, "get_$name" );
918 4         8 _mixin_key( $module => $class, $name, 'class' );
919 4         22 $$class_DECL->{'class_attrs_ro'}->{$name} = $ivalue;
920             }
921              
922 35         132 while ( my ($name, $ivalue) = each %$class_attrs_pr ) {
923 5         9 _clear_method_name($class, "set_$name");
924 5         13 _clear_method_name($class, "get_$name");
925 5         10 _mixin_key( $module => $class, $name, 'class' );
926 5         28 $$class_DECL->{'class_attrs_pr'}->{$name} = $ivalue;
927             }
928              
929 35         293 next MIXIN;
930             }
931              
932             # not declared as a mixable ...
933              
934             # we only look at the methods in the symbol table here
935             # and don't care how they got there. these may very well have
936             # classes declarations but probably not. more likely these
937             # are old fashioned function libraries
938              
939             # mixes => [ { Module1 => ['method'] } ],
940 14 100 33     64 if ( $filter_is_ref eq 'ARRAY' ) {
    50          
941 2         4 for my $name ( @$filter ){
942              
943 2 50 33     28 X::InvalidName->throw(
944             "mixes=>{$module=>['$name']} ??"
945             ) if !$name || $name !~ /^[a-z_]\w*$/o;
946              
947 2         6 my $glob = $$mod_sym{$name};
948 2         3 my $code;
949              
950             # careful: only CODE slot, not others
951 2 50 33     9 next if !$glob || !( $code = *$glob{CODE} );
952              
953             # import
954 2         5 *{ $class . '::' . $name } = $code;
  2         9  
955 2         5 _clear_method_name($class, $name);
956              
957 2         9 $$class_MIXIN->{$name} = $module.'::'.$name;
958 2         23 $$class_DECL->{$type.'methods'}->{$name}
959             = $module.'::'.$name;
960             }
961             }
962              
963             # mixes => [ { Module1 => qr/.../o } ],
964             elsif ( $filter_is_ref eq 'Regexp'
965             or $filter_is_ref eq 'Regex' ) { # older
966 12         44 while ( my ($name) = each %$mod_sym ) {
967              
968 91         206 my $glob = $$mod_sym{$name};
969 91         127 my $code;
970              
971             # careful: only CODE slot, not others
972 91 100 66     887 next if $name !~ $filter || !$glob
      33        
973             || !( $code = *$glob{CODE} );
974              
975             # import
976 34         56 *{ $class . '::' . $name } = $code;
  34         141  
977 34         66 _clear_method_name($class,$name);
978 34         77 $$class_MIXIN->{$name} = $module;
979              
980 34         244 $$class_DECL->{$type.'methods' }->{$name}
981             = $module.'::'.$name;
982             }
983             }
984              
985             # ref to something else
986             else {
987 0         0 X::Usage->throw($filter_is_ref . $usage);
988             } # filter_is_ref
989             } # module
990             }
991              
992             sub _mixin {
993 430     430   679 my ($from_pkg, $to_pkg, $name, $dest_name) = @_;
994 430   66     1112 $dest_name ||= $name;
995 430         635 *{ $to_pkg . '::' . $dest_name } = \&{ $from_pkg . '::' . $name };
  430         1902  
  430         1136  
996 430         507 my $really_from = ${ $from_pkg.'::MIXIN' }->{$name};
  430         1761  
997 430   66     1531 ${ $to_pkg.'::MIXIN' }->{$dest_name} = $really_from || $from_pkg;
  430         1450  
998             }
999              
1000             sub _mixin_key {
1001 33     33   45 my ($from_pkg, $to_pkg, $name, $is_class) = @_;
1002 33   100     97 my $var_name = ($is_class && 'CLASS_') . 'ATTR_' . $name;
1003 33         33 *{$to_pkg.'::'.$var_name} = \${$from_pkg.'::'.$var_name};
  33         109  
  33         64  
1004 33         34 my $really_from = ${$from_pkg.'::MIXIN'}->{"\$$var_name"};
  33         1357  
1005 33   66     97 ${ $to_pkg.'::MIXIN' }->{"\$$var_name"}
  33         107  
1006             = $really_from || $from_pkg;
1007             }
1008              
1009             sub _clear_method_name {
1010 729     729   726 my $decl = ${ $_[0].'::DECL' };
  729         2465  
1011 729         834 my ($cm, $m, $pkm);
1012 729 50       1640 $pkm = $decl->{'pkg_methods'} and delete $pkm->{$_[1]};
1013 729 100       1632 $cm = $decl->{'class_methods'} and delete $cm->{$_[1]};
1014 729 100       1525 $m = $decl->{'methods'} and delete $m->{$_[1]};
1015 729         829 delete ${ $_[0].'::MIXIN' }->{$_[1]};
  729         1814  
1016 729         1162 return;
1017             }
1018              
1019             ####################################################################
1020              
1021             sub _define_class_attr {
1022 148     148   259 my ( $class, $name => $i_value, $scope ) = @_;
1023 148         517 my $qual_name = $class.'::'.$name;
1024 148     0   470 my $scope_x = sub {X::AttrScope->throw([$name])};
  0         0  
1025 148         1802 my $has_getter = $class->can("get_$name");
1026 148         1388 my $has_setter = $class->can("set_$name");
1027              
1028             # set the initial value
1029 148         664 $$qual_name = $i_value;
1030              
1031             # create a string containing the name of the class attribute
1032             # for use privately within the class
1033 148         198 *{"$class\::CLASS_ATTR_$name"} = \$qual_name;
  148         753  
1034              
1035             # define 'getter' for all but private
1036 143     0   496 *{"$class\::get_$name"} = sub { $$qual_name }
  0         0  
1037 148 100       800 if $scope ne 'pr';
1038              
1039             # define 'setter' for read-write
1040 148 100 100     783 if ($scope ne 'pr' and $scope ne 'ro') {
1041 138         442 *{"$class\::set_$name"}
1042 138     0   477 = sub { $$qual_name = $_[1]; return };
  0         0  
  0         0  
1043             }
1044              
1045             # private shouldn't have getter or setter, if so, cause exception
1046 148 100 100     584 *{"$class\::get_$name"} = $scope_x
  1         26  
1047             if $scope eq 'pr' && $has_getter;
1048 148 100 100     389 *{"$class\::set_$name"} = $scope_x
  1         5  
1049             if $scope eq 'pr' && $has_setter;
1050              
1051             # read-only shouldn't have setter if so, cause exception
1052 148 100 100     384 *{"$class\::set_$name"} = $scope_x
  1         4  
1053             if $scope eq 'ro' && $has_setter;
1054              
1055             # update declaration
1056 148         157 my $decl = ${ $class.'::DECL' };
  148         418  
1057 148 100       529 $decl->{'class_attrs'
1058             . ($scope ? "_$scope" : '')}->{$name} = $i_value;
1059              
1060 148         957 return $class;
1061             }
1062              
1063             ####################################################################
1064              
1065             sub _define_attr {
1066 393     393   649 my ( $class, $name, $scope, $unqual, $noaccess ) = @_;
1067 393         791 my $qual_name = $class.'::'.$name;
1068 393 100       854 my $attr_name = $unqual ? $name : $qual_name;
1069              
1070             # create a string containing the name of the attribute
1071             # for use privately within the class as a object hash key
1072 393         462 *{"$class\::ATTR_$name"} = \$attr_name;
  393         2151  
1073              
1074             # noaccess means don't create accessors, plain old perl objects
1075 393 100       852 if (!$noaccess) {
1076 391     0   1263 my $scope_x = sub {X::AttrScope->throw([$name])};
  0         0  
1077 391         3848 my $has_getter = $class->can("get_$name");
1078 391         2917 my $has_setter = $class->can("set_$name");
1079              
1080             # define 'getter' for all but private
1081 387     3   1230 *{"$class\::get_$name"} = sub { $_[0]->{$attr_name} }
  3         376  
1082 391 100       1878 if $scope ne 'pr';
1083              
1084             # define 'setter' for read-write
1085 391 100 100     1967 if ($scope ne 'pr' and $scope ne 'ro') {
1086 118         369 *{"$class\::set_$name"}
1087 118     4   435 = sub { $_[0]->{$attr_name} = $_[1]; return };
  4         28  
  4         12  
1088             }
1089              
1090             # private shouldn't have getter or setter
1091             # if so override with one that throws an exception at run time
1092 391 100 100     970 *{"$class\::get_$name"} = $scope_x
  1         4  
1093             if $scope eq 'pr' && $has_getter;
1094 391 100 100     861 *{"$class\::set_$name"} = $scope_x
  1         4  
1095             if $scope eq 'pr' && $has_setter;
1096              
1097             # read-only shouldn't have setter if so, cause exception
1098 391 100 100     2120 *{"$class\::set_$name"} = $scope_x
  1         6  
1099             if $scope eq 'ro' && $has_setter;
1100             }
1101              
1102             # update declaration
1103 393         411 my $decl = ${ $class.'::DECL' };
  393         1116  
1104 393 100       956 my $tag = 'attrs' . ($scope ? "_$scope" : '');
1105 393         842 push @{$decl->{$tag}}, $name
  1465         2631  
1106 393 50       477 if !grep {$_ eq $name} @{$decl->{$tag}};
  393         948  
1107              
1108 393         1022 return $class;
1109             }
1110              
1111             ####################################################################
1112              
1113             sub _define_method {
1114 454     454   988 my ( $class, $name, $method, $type ) = @_; # trust
1115 454         577 my $is_ref = ref $method;
1116 454         478 my ($code, $m_pkg, $m_name);
1117              
1118 454 100       874 $method = 'EMPTY' if !$method;
1119              
1120             # classes::abstract, classes::empty, Module::method, method
1121 454 100       741 if (!$is_ref ) {
1122              
1123             # inline anon to get better X::Unimplemented message
1124 442 100       1149 if ( $method eq 'ABSTRACT' ) {
    100          
1125             $code = sub {
1126 0     0   0 X::Unimplemented->throw( "$class->$name()" );
1127 1         1277 };
1128             }
1129              
1130             elsif ( $method eq 'EMPTY' ) {
1131 1     0   4 $code = sub { };
  0         0  
1132             }
1133              
1134             # Module::Mine::method_name or method_name
1135             else {
1136 440         3062 ($m_pkg, $m_name) = $method
1137             =~ /^
1138             (?:
1139             (
1140             (?:
1141             [a-z_]\w*\:\: # opt: Module::
1142             )*
1143             [a-z_]\w* # opt: Mine
1144             )
1145             :: # opt: ::
1146             )?
1147             (
1148             [a-z_]\w* # req: method_name
1149             )
1150             $/iogx;
1151              
1152 440 50       1032 X::InvalidName->throw( [$method] ) if !$m_name;
1153              
1154             # no package name, method_name only - qualify
1155 440 100       820 if (!$m_pkg) {
1156 223         435 $method = $class . '::' . $m_name;
1157 223   33     815 $m_pkg ||= $class;
1158             }
1159              
1160             # treat as any other mixin
1161             else {
1162 217         583 _clear_method_name($class, $name);
1163 217         463 _mixin( $m_pkg => $class, $m_name => $name );
1164 217         240 my $decl = ${ $class.'::DECL' };
  217         533  
1165 217         543 $decl->{$type.'methods'}->{$name} = $method;
1166 217         455 return $class;
1167             }
1168              
1169 223 50       246 load $m_pkg if !%{$m_pkg.'::'};
  223         1296  
1170              
1171             # unfortunately since we allow compile-time
1172             # class definition it is too early to test for defined
1173             # method, we have to trust that one will be defined
1174             # or caught by perl run-time, the following springs
1175             # a symbolic code ref into life no matter what,
1176             # rather annoying, but best we can do for now
1177              
1178 223         1414 $code = \&$method;
1179             }
1180              
1181             }
1182              
1183             # sub { ... } or \&method
1184             else {
1185 12 50       26 X::Usage->throw( "$name $is_ref ??" )
1186             if $is_ref ne 'CODE';
1187 12         17 $code = $method;
1188 12         15 $method = 'CODE';
1189             }
1190              
1191 237         295 *{ $class . '::' . $name } = $code;
  237         755  
1192              
1193             # update declaration - same named removed since all are methods
1194 237         466 _clear_method_name($class, $name);
1195 237         240 my $decl = ${ $class.'::DECL' };
  237         512  
1196 237         650 $decl->{$type.'methods'}->{$name} = $method;
1197              
1198 237         959 return $class;
1199             }
1200              
1201             ######################################################################
1202              
1203             sub new_args {
1204 0     0 1 0 my $class = shift;
1205 0         0 my $self = {};
1206 0         0 bless $self, $class;
1207 0         0 return $self->classes::init_args(@_);
1208             }
1209              
1210             sub new_only {
1211 0     0 1 0 return bless {}, $_[0];
1212             }
1213              
1214             sub new_init {
1215 1     1 1 12 my $class = shift;
1216 1         3 my $self = {};
1217 1         3 bless $self, $class;
1218 1         6 return $self->initialize(@_);
1219             }
1220              
1221             sub new_fast {
1222 0     0 1 0 my $class = shift;
1223 0 0       0 return bless($_[0], $class) if ref $_[0];
1224 0 0       0 return bless({@_}, $class) if @_;
1225 0         0 return bless({}, $class);
1226             }
1227              
1228             ######################################################################
1229              
1230             sub init_args {
1231 3     3 1 705 my $self = shift;
1232 3         9 my $attrs = $_[0];
1233 3 100       18 $attrs = {@_} if ref $attrs ne 'HASH';
1234              
1235 3         18 while ( my ( $attr, $value ) = each %$attrs ) {
1236 3 50       26 my $setter = $self->can("set_$attr")
1237             or X::UnknownAttr->throw([$attr]);
1238 3         10 $self->$setter($value);
1239             }
1240              
1241 3         10 return $self;
1242             }
1243              
1244             ######################################################################
1245             # Modeled after Clone::PP and Clone, Matthew Simon Cavalletto,
1246             # David Muir Sharnoff, Ray Finch, chocolateboy. See pod for
1247             # differences.
1248             #
1249             # Could use some work to get object 'clone' methods observed--
1250             # especially with the prevalence of unclonable perl classes
1251             # out there, but alas, this makes cloning self refs difficult
1252             # because their cached copies no longer match.
1253              
1254             sub clone {
1255 0 0   0 1   X::Usage->throw('$obj->clone') if @_ != 1;
1256 0           my ($this) = @_;
1257              
1258             # 'undef' is a valid leaf clone value
1259 0 0         return $this if !defined $this;
1260              
1261 0           my $is_ref = reftype $this;
1262 0           my $blessed_as = blessed $this;
1263 0           my $clone = undef;
1264              
1265             # cache of back references to prevent recursion, mark top,
1266             # localized global makes the clone_cache visible down through
1267             # the recursive subroutine calls
1268              
1269 0 0         local %classes::_clone_cache = ( __top => 1, __self => $this )
1270             unless ( exists $classes::_clone_cache{'__self'} );
1271              
1272 0 0         if ( $classes::_clone_cache{__top} ) {
1273              
1274             # put stuff for first run, if needed, here
1275              
1276 0           delete $classes::_clone_cache{__top};
1277             }
1278              
1279             # block recursion, seen already
1280 0 0         return $classes::_clone_cache{$this}
1281             if exists $classes::_clone_cache{$this};
1282              
1283             # scalar = leaf
1284 0 0         return $this if !$is_ref;
1285              
1286             # clone each key or value that is a ref recursively
1287 0 0 0       if ( $is_ref eq 'HASH' ) {
    0          
    0          
1288 0           $classes::_clone_cache{$this} = $clone = {};
1289 0 0         %$clone = map { !ref $_ ? $_ : classes::clone($_) } %$this;
  0            
1290             }
1291              
1292             # clone each element in array that is a ref recursively
1293             elsif ( $is_ref eq 'ARRAY' ) {
1294 0           $classes::_clone_cache{$this} = $clone = [];
1295 0 0         @$clone = map { !ref $_ ? $_ : classes::clone($_) } @$this;
  0            
1296             }
1297              
1298             # clone refs to refs and simple refs to scalars
1299             elsif ( $is_ref =~ /^(REF|SCALAR)$/o && !$blessed_as ) {
1300 0           $classes::_clone_cache{$this} = $clone = \( my $var = q[] );
1301 0           $$clone = classes::clone($$this);
1302             }
1303              
1304             # plain copies of anything else (globs, regx, etc.)
1305             else {
1306 0           $classes::_clone_cache{$this} = $clone = $this;
1307             }
1308              
1309 0 0         bless $clone, $blessed_as if $blessed_as;
1310 0           return $clone;
1311             }
1312              
1313             ######################################################################
1314              
1315             sub sprintf {
1316 0     0 1   my ($self, $format, @attrs) = @_;
1317 0           my @values = map {
1318 0           my $getter = $self->can("get_$_");
1319 0 0         defined $getter ? $self->$getter : undef;
1320             } @attrs;
1321 0           CORE::sprintf $format, @values;
1322             }
1323              
1324             sub printf {
1325 0     0 1   my ($self, $format, @attrs) = @_;
1326 0           my @values = map {
1327 0           my $getter = $self->can("get_$_");
1328 0 0         defined $getter ? $self->$getter : undef;
1329             } @attrs;
1330 0           CORE::printf $format, @values;
1331             }
1332              
1333             ######################################################################
1334              
1335             sub set {
1336 0     0 1   my ( $self, $name, $value ) = @_;
1337 0   0       my $accessor = $self->can("set_$name")
1338             || X::MethodNotFound->throw("set_$name");
1339 0           return $self->$accessor($value);
1340             }
1341              
1342             sub get {
1343 0     0 1   my ( $self, $name ) = @_;
1344 0   0       my $accessor = $self->can("get_$name")
1345             || X::MethodNotFound->throw("get_$name");
1346 0           return $self->$accessor;
1347             }
1348              
1349             ####################################################################
1350              
1351             sub dump {
1352 0     0 1   my ( $this, $out ) = @_;
1353 0           my $is_ref = ref $out;
1354 0           my $blessed_as = blessed $this;
1355 0   0       my $class = $blessed_as || $this || caller;
1356 0           my $decl = ${ $class . '::DECL' };
  0            
1357 0           my $mixins = ${ $class . '::MIXIN' };
  0            
1358              
1359 0           my $usage = <<'EOM';
1360              
1361             MyClass|$object->classes::dump;
1362             ... ->classes::dump( $handle );
1363             ... ->classes::dump( \$buffer );
1364             classes::dump(['just','anything']);
1365             EOM
1366              
1367 0 0 0       X::Usage->throw($usage)
1368             if $out && !$is_ref; # handle or scalar ref
1369              
1370 0   0       $out ||= *STDERR;
1371              
1372 0           require Data::Dumper;
1373              
1374 0           local $Data::Dumper::Varname;
1375 0           local $Data::Dumper::Indent = 1;
1376 0           my $buf;
1377              
1378             # header line
1379 0           my $len = length $class;
1380 0           my $pad = int( ( 70 - $len ) / 2 );
1381 0           my $rest = 70 - ( $pad*2 + $len );
1382 0           $pad -= 2;
1383 0           $buf .= '#' x $pad . " $class "
1384             . '#' x ( $pad + $rest ) . "\n\n";
1385              
1386 0 0         if ( $decl ) {
1387 0           $Data::Dumper::Varname = 'DECL';
1388 0           $buf .= Data::Dumper::Dumper($decl) . "\n";
1389              
1390 0 0         if ( $mixins ) {
1391 0           $Data::Dumper::Varname = 'MIXIN';
1392 0           $buf .= Data::Dumper::Dumper($mixins) . "\n";
1393             }
1394              
1395 0           my $d_class_attrs = $decl->{'class_attrs'};
1396 0           my $d_class_attrs_ro = $decl->{'class_attrs_ro'};
1397              
1398             # pull out internal class attrs (if any) by accessor
1399 0 0 0       if ( $d_class_attrs || $d_class_attrs_ro ) {
1400 0           my %class_attr;
1401 0           $Data::Dumper::Varname = 'CLASS_STATE';
1402 0 0         if ( $d_class_attrs ) {
1403 0           while ( my ( $tag, $val ) = each %$d_class_attrs ) {
1404 0           my $getter = "get_$tag";
1405 0           my $accessor = $class->can($getter);
1406 0 0         $class_attr{$tag} =
1407             $accessor
1408             ? $class->$accessor
1409             : 'ERROR_NO_ACCESSOR';
1410             }
1411             }
1412 0 0         if ( $d_class_attrs_ro ) {
1413 0           while ( my ($tag, $val) = each %$d_class_attrs_ro ) {
1414 0           my $getter = "get_$tag";
1415 0           my $accessor = $class->can($getter);
1416 0 0         $class_attr{$tag} =
1417             $accessor
1418             ? $class->$accessor
1419             : 'ERROR_NO_ACCESSOR';
1420             }
1421             }
1422 0           $buf .= Data::Dumper::Dumper( \%class_attr ) . "\n";
1423             }
1424              
1425             # object only
1426 0 0         if ($blessed_as) {
1427 0           $Data::Dumper::Varname = 'OBJECT_STATE';
1428 0           $buf .= Data::Dumper::Dumper($this) . "\n";
1429             }
1430              
1431             }
1432              
1433             else {
1434 0           $Data::Dumper::Varname = 'THIS';
1435 0           $buf .= Data::Dumper::Dumper( $this );
1436              
1437 0 0         if ($blessed_as) {
1438 0           $Data::Dumper::Varname = 'PACKAGE_SYMBOLS';
1439 0           $buf .= Data::Dumper::Dumper( \%{$blessed_as.'::'} );
  0            
1440             }
1441            
1442             }
1443              
1444             # string buffer
1445 0 0 0       if ( $is_ref and $is_ref eq 'SCALAR' ) {
1446 0           $$out = $buf;
1447             }
1448              
1449             # io handle
1450             else {
1451 0           print $out $buf;
1452             }
1453              
1454 0           return $this;
1455             }
1456              
1457             ######################################################################
1458              
1459 0     0 1   sub id { Scalar::Util::refaddr($_[0]) }
1460              
1461             ######################################################################
1462             # Throwable mixin - the guts of perl exceptions
1463              
1464             package classes::Throwable;
1465 24     25   314 use strict 'subs'; no warnings;
  24     24   54  
  25         1844  
  24         142  
  24         51  
  24         1180  
1466 24     24   198 use Scalar::Util;
  24         50  
  24         2101  
1467              
1468             use classes
1469 24         312 type => 'mixable',
1470             class_methods => {
1471             throw => 'throw',
1472             caught => 'caught',
1473             catch => 'caught', # synonym
1474             },
1475             methods => {
1476             capture => 'capture',
1477             rethrow => 'rethrow',
1478             send => 'rethrow',
1479             },
1480 24     24   323 ; # END DECLARATION
  24         286  
1481              
1482 0     0     sub capture { $_[0] };
1483 0     0     sub throw { my $x = shift->new(@_); $x->capture; die $x }
  0            
  0            
1484 0 0   0     sub rethrow { die $_[0] if $_[0] }
1485              
1486             sub caught {
1487 0     0     my $class = shift;
1488 0           my $last_err = $@;
1489              
1490 0 0 0       return $last_err
1491             if Scalar::Util::blessed $last_err && $last_err->isa($class);
1492              
1493 0           return undef;
1494             }
1495              
1496             ######################################################################
1497             # Bare minimum exception class
1498              
1499             package X::classes;
1500 24     24   157 use strict 'subs'; no warnings;
  24     24   40  
  24         774  
  24         110  
  24         34  
  24         783  
1501 24     24   128 use Scalar::Util;
  24         41  
  24         1504  
1502              
1503             use classes
1504 24         115 mixes => 'classes::Throwable',
1505             new => 'classes::new_init',
1506             init => 'classes::init_args',
1507             clone => 'classes::clone',
1508             dump => 'classes::dump',
1509             methods => ['as_string'],
1510 24     24   126 ;
  24         37  
1511              
1512             sub as_string {
1513 0     0     my $c = Scalar::Util::blessed shift;
1514 0           return "exception $c\n"
1515             }
1516              
1517 24     24   1092025 use overload bool => sub {1}, '""' => 'as_string', fallback => 1;
  24     0   26731  
  24         263  
  0         0  
1518            
1519             ######################################################################
1520             # base exception class
1521              
1522             package X::classes::traceable;
1523 24     24   2666 use strict 'subs'; no warnings;
  24     24   49  
  24         718  
  24         118  
  24         38  
  24         1246  
1524 24     24   140 use Scalar::Util;
  24         44  
  24         2736  
1525              
1526             use classes
1527 24         342 extends => 'X::classes',
1528             new => 'classes::new_init',
1529             init => 'initialize',
1530             clone => 'classes::clone',
1531             dump => 'classes::dump',
1532             class_attrs => {
1533             'Verbose' => 3,
1534             'Order' => ['item','message'],
1535             'Format' => '',
1536             'Caller_Format' => q[ at %4$s %1$s '%2$s' %3$s],
1537             'Whole_Stack' => undef,
1538             },
1539             attrs => [qw(
1540             item message full_message attr_list
1541             )],
1542             attrs_ro => [qw(
1543             pid uid euid gid egid time
1544             call_stack call_package call_method call_file call_line
1545             )],
1546             methods => [ 'as_string' ],
1547 24     24   147 ;
  24         48  
1548              
1549 0     0     sub get_full_message { get_message(@_) }
1550 0     0     sub set_full_message { set_message(@_) }
1551              
1552             sub capture {
1553 0     0     my ($self, $offset) = @_;
1554              
1555 0           my @call_stack;
1556 0 0         $offset = @_ >= 2 ? $offset : 0;
1557              
1558             # remove this method (at least)
1559 0           my $whole_stack = $self->get_Whole_Stack;
1560 0 0         my $frame = $whole_stack ? 1 : 1 + $offset;
1561              
1562             # fixes problems with some versions of perl time
1563 0           $self->{$ATTR_time} = CORE::time();
1564              
1565             # process ownership
1566 0           $self->{$ATTR_pid} = $$;
1567 0           $self->{$ATTR_uid} = $<;
1568 0           $self->{$ATTR_euid} = $>;
1569 0           $self->{$ATTR_gid} = $(;
1570 0           $self->{$ATTR_egid} = $);
1571              
1572             CALL_TRACE:
1573 0           while (1) {
1574 0           my @call_frame = caller($frame);
1575 0 0         last CALL_TRACE if !@call_frame;
1576 0           push @call_stack, \@call_frame;
1577 0           ++$frame;
1578             }
1579              
1580             # if at least one frame, give context of the caller's caller
1581 0 0 0       shift @call_stack if @call_stack > 1 && !$whole_stack;
1582              
1583 0           my $call_stack_ref = \@call_stack;
1584 0           $self->{$ATTR_call_stack} = $call_stack_ref;
1585 0           $self->{$ATTR_call_package} = $call_stack[0][0];
1586 0           $self->{$ATTR_call_file} = $call_stack[0][1];
1587 0           $self->{$ATTR_call_line} = $call_stack[0][2];
1588 0           $self->{$ATTR_call_method} = $call_stack[0][3];
1589              
1590 0           return $self;
1591             }
1592              
1593             sub initialize {
1594 0     0     my $self = shift;
1595 0           my $is_ref = ref $_[0];
1596              
1597 0           $self->{$ATTR_message} = '';
1598 0           $self->{$ATTR_full_message} = '';
1599              
1600             # X::classes::traceable->new('some message')
1601 0 0 0       if ( !$is_ref && @_ == 1 ) {
    0          
1602 0           $self->{$ATTR_message} = $_[0];
1603 0           return $self;
1604             }
1605              
1606             # X::classes::traceable->new( ['item'] )
1607             elsif ( $is_ref eq 'ARRAY' ) {
1608 0           $self->set_attr_list($_[0]);
1609 0           return $self;
1610             }
1611             # normal
1612             # X::classes::traceable->new( message=>'blah', item=>2 )
1613             # X::classes::traceable->new( -message=>'blah', -item=>2 )
1614             # X::classes::traceable->new( { message=>'blah', item=>2 } )
1615             # X::classes::traceable->new( { -message=>'blah', -item=>2 } )
1616 0           return $self->classes::init_args(@_);
1617             }
1618              
1619             sub set_attr_list {
1620 0     0     my $self = shift;
1621 0 0         X::Usage->throw("\$exc->set_attr_list(ARRAYREF)\n")
1622             if ref $_[0] ne 'ARRAY';
1623 0           my $attr_list = $self->{$ATTR_attr_list} = $_[0];
1624              
1625             # Rest is dispatch side-effect ...
1626              
1627 0           my $order = $self->get_Order; # commonly overriden
1628 0           my @order = @$order;
1629              
1630             # cannot dispatch if no 'Order' of attr_list
1631 0 0         return $attr_list unless @order >= 1;
1632              
1633 0           for my $attr_value (@$attr_list) {
1634 0           my $attr_name = shift @order;
1635              
1636             # order = 'name', undef, 'rank'
1637             # list = 'Bob', 'ignored', 'private', 'ignored2'
1638 0 0         next if !$attr_name;
1639              
1640             # dispatch to accessor
1641 0           $self->classes::set( $attr_name => $attr_value );
1642             }
1643              
1644 0           return $attr_list;
1645             }
1646              
1647             sub as_string {
1648 0     0     my $self = shift;
1649 0           my $verbose = $self->get_Verbose;
1650              
1651 0 0         return '' if not $verbose;
1652              
1653 0           my $exception_name = Scalar::Util::blessed $self;
1654 0           my $full_message = $self->get_full_message;
1655              
1656 0           my $attrs = $self->get_attr_list;
1657 0 0         my @attrs = @$attrs if $attrs;
1658 0           my $format = $self->get_Format;
1659              
1660 0 0         return $exception_name . "\n" if $verbose == 1;
1661              
1662             # exception
1663 0           my $string = $exception_name;
1664              
1665             # exception + message or attr_list
1666 0 0         if ( $verbose >= 2 ) {
1667 0           $string .= ' ';
1668 0   0       $string .= $full_message
1669             || (
1670             ($format)
1671             ? sprintf( $format, @attrs )
1672             : "@attrs"
1673             );
1674 0           $string .= "\n";
1675             }
1676              
1677             # exception + message or attr_list + trace (3=file, 4=path)
1678 0 0         if ( $verbose >= 3 ) {
1679 0           my $caller_format = $self->get_Caller_Format;
1680 0           my $call_stack = $self->get_call_stack;
1681 0           for my $caller (@$call_stack) {
1682 0 0         if ( $verbose == 3) {
1683 24     24   196 use File::Basename 'basename';
  24         46  
  24         5199  
1684 0           $$caller[1] = basename $$caller[1];
1685             }
1686 0           $string .= sprintf $caller_format . "\n", @$caller;
1687             }
1688             }
1689              
1690 0           return $string;
1691             }
1692              
1693 24     24   137 use overload bool => sub {1}, '""' => 'as_string', fallback => 1;
  24     0   39  
  24         285  
  0         0  
1694              
1695             ################################################################
1696             ## exceptions used by 'classes' itself
1697             ################################################################
1698              
1699             package main;
1700             classes::classes
1701             { name=>'X::NameUnavailable', extends=>'X::classes::traceable' },
1702             { name=>'X::InvalidName', extends=>'X::classes::traceable' },
1703             { name=>'X::NotPkgMethod', extends=>'X::classes::traceable' },
1704             { name=>'X::Unimplemented', extends=>'X::classes::traceable' },
1705             { name=>'X::Usage', extends=>'X::classes::traceable' },
1706             { name=>'X::Undefined', extends=>'X::classes::traceable' },
1707             { name=>'X::Empty', extends=>'X::classes::traceable' },
1708             { name=>'X::MethodNotFound', extends=>'X::classes::traceable' },
1709             { name=>'X::AttrScope', extends=>'X::classes::traceable' },
1710             { name=>'X::UnknownAttr', extends=>'X::classes::traceable' },
1711             ;
1712              
1713             1;
1714             __END__