File Coverage

blib/lib/OOP/Perlish/Class.pm
Criterion Covered Total %
statement 319 374 85.2
branch 63 112 56.2
condition 27 62 43.5
subroutine 64 68 94.1
pod 12 12 100.0
total 485 628 77.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             # $Id$
3             # $Author$
4             # $HeadURL$
5             # $Date$
6             # $Revision$
7 6     6   20641 use warnings;
  6         11  
  6         192  
8 6     6   34 use strict;
  6         10  
  6         247  
9             {
10             ############################################################################################
11             ## OOP::Perlish::Class: a Base class for creating Objects that conform to all common OOP
12             ## practices, Multiple-Inheritance, Mix-in, Generational-Inheritance, Overriding,
13             ## Overloading, Accessor validation, input mutation, singletons, Multitons, etc, etc
14             ############################################################################################
15              
16             package OOP::Perlish::Class;
17 6     6   30 use warnings;
  6         13  
  6         192  
18 6     6   34 use strict;
  6         8  
  6         287  
19             our $VERSION = '0.45.0';
20 6     6   3984 use OOP::Perlish::Class::Accessor;
  6         14  
  6         284  
21 6     6   6489 use Tie::IxHash;
  6         40406  
  6         265  
22 6     6   59 use Exporter;
  6         11  
  6         268  
23 6     6   6645 use IO::Handle;
  6         62929  
  6         377  
24              
25 6     6   53 use constant OOP_PERLISH_CLASS_EMITLEVEL_FATAL => 0;
  6         13  
  6         450  
26 6     6   35 use constant OOP_PERLISH_CLASS_EMITLEVEL_ERROR => 1;
  6         30  
  6         293  
27 6     6   32 use constant OOP_PERLISH_CLASS_EMITLEVEL_WARNING => 2;
  6         14  
  6         275  
28 6     6   31 use constant OOP_PERLISH_CLASS_EMITLEVEL_INFO => 3;
  6         1268  
  6         299  
29 6     6   32 use constant OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE => 4;
  6         10  
  6         270  
30 6     6   28 use constant OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0 => 5;
  6         1369  
  6         286  
31 6     6   33 use constant OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1 => 6;
  6         14  
  6         291  
32 6     6   29 use constant OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2 => 7;
  6         10  
  6         413  
33              
34 6     6   40 use Carp qw(carp croak confess cluck);
  6         8  
  6         2828  
35              
36             our @EXPORT_TAGS = (
37             'emitlevels' => [
38             'OOP_PERLISH_CLASS_EMITLEVEL_FATAL', 'OOP_PERLISH_CLASS_EMITLEVEL_ERROR',
39             'OOP_PERLISH_CLASS_EMITLEVEL_WARNING', 'OOP_PERLISH_CLASS_EMITLEVEL_INFO',
40             'OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE', 'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0',
41             'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1', 'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2'
42             ],
43             );
44              
45             our @EXPORT_OK = (
46             'OOP_PERLISH_CLASS_EMITLEVEL_FATAL', 'OOP_PERLISH_CLASS_EMITLEVEL_ERROR',
47             'OOP_PERLISH_CLASS_EMITLEVEL_WARNING', 'OOP_PERLISH_CLASS_EMITLEVEL_INFO',
48             'OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE', 'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0',
49             'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1', 'OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2'
50             );
51              
52             ############################################################################################
53             ## We still use Exporter's import, but we need to clean it up first
54             ## doing this via export_fail doesn't work because exporter must see 'our @EXPORT_FAIL'
55             ## in the namespace of package being imported; and subclasses would not normally have
56             ## re-defined that again and again and again; optionally, we'd @EXPORT = qw(@EXPORT <...>)
57             ## which would result in every module cascading the exports down all the way to users
58             ## of the object (not just inheritors) which would suck.
59             ############################################################################################
60             sub import
61             {
62 24     24   74 my ( $proto, @tags ) = @_;
63 24   33     160 my $class = ref($proto) || $proto;
64              
65 24 50       226 if( bless( {}, $class )->isa(__PACKAGE__) ) {
66 24         115 $class->____OOP_PERLISH_CLASS_DERIVED_CLASSES()->{$class} = 1;
67             }
68              
69 24 50       712 return unless(@tags);
70              
71 0         0 my %non_import_flags;
72             ## XXX: Hash slice assignment for LUT
73             @non_import_flags{
74 0         0 '_emitlevel:error', '_emitlevel:warning', '_emitlevel:info', '_emitlevel:verbose',
75             '_emitlevel:debug', '_emitlevel:debug1', '_emitlevel:debug2'
76             }
77             = undef;
78              
79 0         0 for my $tag (@tags) {
80 0         0 for my $setter_tag ( keys %non_import_flags ) {
81 0 0       0 $tag =~ m/ ^ \Q$setter_tag\E $ /gsmx && do {
82 0         0 my ( $static_method, $argument ) = split( ':', $tag );
83 0 0       0 if( bless( {}, $class )->can($static_method) ) {
84 0         0 $class->$static_method($argument);
85             }
86             };
87             }
88             }
89              
90 0         0 @tags = grep { !exists( $non_import_flags{$_} ) } @tags;
  0         0  
91 0         0 return Exporter::import(@tags);
92             }
93              
94             ############################################################################################
95             ## Create a new instance of this class; should not require overloading in derived classes.
96             ############################################################################################
97             sub new
98             {
99 145     145 1 704 my ( $proto, @opts ) = @_;
100              
101             ## support either ...( foo => 'bar' ); or ...( { foo => 'bar' } );
102 0         0 my %opts =
103 145 0       1293 ( @opts == 1 ) ? ( ( ref( $opts[0] ) eq 'HASH' ) ? %{ $opts[0] } : () )
    50          
    50          
104             : ( scalar @opts % 2 == 0 ) ? @opts
105             : confess('Invalid number or type of arguments to constructor');
106              
107 145   33     779 my $class = ref($proto) || $proto;
108 145         306 my $self = {};
109              
110             # obtain the @ISA for whomever inherited us
111 6     6   36 no strict 'refs';
  6         16  
  6         298  
112 145         200 @{ $self->{____CLASS_ISA} } = @{ $class . '::ISA' };
  145         584  
  145         872  
113 6     6   27 use strict 'refs';
  6         10  
  6         8996  
114              
115             #for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
116             # bless( $self, $parent_class );
117             #}
118              
119 145         458 bless( $self, $class ); ## Bless so we can call _all_isa
120 145         813 for my $parent_class ( $self->_all_isa() ) {
121 344         3872 bless( $self, $parent_class );
122             }
123 145         530 bless( $self, $class ); ## Bless back into this class last so we deref correctly
124 145         1485 $self = $self->____initialize_object(%opts);
125              
126 133         948 return $self;
127             }
128              
129             ############################################################################################
130             ## Get an immutable copy of the underlying data
131             ############################################################################################
132             sub get(@)
133             {
134 55     55 1 84 my ( $self, $field ) = @_;
135              
136 55         126 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->self($self);
137 55         127 return $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->value();
138             }
139              
140             ############################################################################################
141             ## Set (and validate) an immutable copy, return the validated data.
142             ############################################################################################
143             sub set(@) ## no critic (AmbiguousNames)
144             {
145 55     55 1 98 my ( $self, $field, @values ) = @_;
146              
147 55         108 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->self($self);
148 55         113 return $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->value(@values);
149             }
150              
151             ############################################################################################
152             ## return true if the value has been set before (even if set to undef)
153             ############################################################################################
154             sub is_set(@)
155             {
156             ## use critic
157 1     1 1 2 my ( $self, $field ) = @_;
158              
159 1         32 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->self($self);
160             ### Accessors uses -1, 0, and 1, but we make this boolean for Class
161 1         3 return ( $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->is_set() > 0 );
162             }
163              
164             ############################################################################################
165             ## emit an error
166             ############################################################################################
167             sub error(@)
168             {
169 1     1 1 5 my ( $self, @msgs ) = @_;
170              
171 1         8 return $self->_emit( 'ERROR', OOP_PERLISH_CLASS_EMITLEVEL_ERROR, @msgs );
172             }
173              
174             ############################################################################################
175             ## emit a warning
176             ############################################################################################
177             sub warning(@)
178             {
179 1     1 1 3 my ( $self, @msgs ) = @_;
180              
181 1         14 return $self->_emit( 'WARNING', OOP_PERLISH_CLASS_EMITLEVEL_WARNING, @msgs );
182             }
183              
184             ############################################################################################
185             ## emit info
186             ############################################################################################
187             sub info(@)
188             {
189 1     1 1 3 my ( $self, @msgs ) = @_;
190              
191 1         7 return $self->_emit( 'INFO', OOP_PERLISH_CLASS_EMITLEVEL_INFO, @msgs );
192             }
193              
194             ############################################################################################
195             ## emit something verbose
196             ############################################################################################
197             sub verbose(@)
198             {
199 1     1 1 3 my ( $self, @msgs ) = @_;
200              
201 1         8 return $self->_emit( 'VERBOSE', OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE, @msgs );
202             }
203              
204             ############################################################################################
205             ## emit debugging info
206             ############################################################################################
207             sub debug(@)
208             {
209 1     1 1 3 my ( $self, @msgs ) = @_;
210              
211 1         7 return $self->_emit( 'DEBUG0', OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0, @msgs );
212             }
213              
214             ############################################################################################
215             ## emit more obscure debugging info
216             ############################################################################################
217             sub debug1(@)
218             {
219 1     1 1 3 my ( $self, @msgs ) = @_;
220              
221 1         6 return $self->_emit( 'DEBUG1', OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1, @msgs );
222             }
223              
224             ############################################################################################
225             ## emit the most obscure debugging info
226             ############################################################################################
227             sub debug2(@)
228             {
229 1     1 1 4 my ( $self, @msgs ) = @_;
230              
231 1         7 return $self->_emit( 'DEBUG2', OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2, @msgs );
232             }
233              
234             ############################################################################################
235             ## croak with the specified message
236             ############################################################################################
237             sub fatal(@)
238             {
239 0     0 1 0 my ( $self, @msgs ) = @_;
240              
241 0 0       0 if( $self->_emitlevel() >= OOP_PERLISH_CLASS_EMITLEVEL_FATAL ) {
242 0 0       0 croak( map { my $l = defined($_) ? $_ : 'undef'; chomp($l); $l =~ s/^/FATAL: /gms; $l . $/ } @msgs );
  0         0  
  0         0  
  0         0  
  0         0  
243             }
244 0         0 return;
245             }
246              
247             ############################################################################################
248             ## Stub of a _preinit; note that init must return true or object initialization will fail
249             ## This method does object initialization _before_ accessors have been set
250             ############################################################################################
251             sub _preinit(@) {
252 134     134   672 my ($self, @args) = @_;
253 134         547 $self->_all_SUPER('_preinit', @args);
254 134         472 return 1;
255             }
256              
257             ############################################################################################
258             ## Stub of an _init; note that init must return true or object initialization will fail
259             ## This method does object initialization _after_ accessors have been set
260             ############################################################################################
261             sub _init(@) {
262 129     129   240 my ($self, @args) = @_;
263 129         339 $self->_all_SUPER('_init', @args);
264 129         401 return 1;
265             }
266              
267             ############################################################################################
268             ## name of the class which we use for accessors; overload if you want to use a different
269             ## class
270             ############################################################################################
271             sub _accessor_class_name(@)
272             {
273 16     16   25 my ($self) = @_;
274 16         35 return qw(OOP::Perlish::Class::Accessor);
275             }
276              
277             ############################################################################################
278             ## emit error, warning, info, verbose, debug, debug1, and debug2 messages; overload to
279             ## change the way you emit
280             ############################################################################################
281             sub _emit(@)
282             {
283 7     7   17 my ( $self, $prefix, $level, @msgs ) = @_;
284              
285 7 50       22 if(@msgs) {
286 7 50       36 if( $self->_emitlevel() >= $level ) {
287 7 50       15 STDERR->print( map { my $l = defined($_) ? $_ : 'undef'; chomp($l); $l =~ s/^/$prefix: /gms; $l . $/ } @msgs );
  7         19  
  7         15  
  7         50  
  7         76  
288             }
289 7         48 push( @{ $self->{ '___' . $prefix } }, @msgs );
  7         30  
290             }
291             else {
292 0         0 return @{ $self->{ '___' . $prefix } };
  0         0  
293             }
294 7         22 return;
295             }
296              
297             ############################################################################################
298             ## return a list of all methods that this object ->can() in order of:
299             ## (
300             ## methods defined in furthest-ancestors,
301             ## methods defined nearer-ancestors
302             ## methods defined in this-class
303             ## )
304             ## now memoized, as this becomes a substantial performance hit otherwise.
305             ############################################################################################
306             sub _all_methods(@)
307             {
308 171     171   285 my ( $self, $class ) = @_;
309 171   33     864 $class ||= ref($self) || $self;
      66        
310              
311 171         227 our %____oop_perlish_class_all_methods;
312              
313 171 100       503 unless(exists($____oop_perlish_class_all_methods{$class})) {
314 29         59 my %all_methods = ();
315              
316             ### preserve order so that methods defined in hiarchies are preserved in the order they
317             ### occur
318 29         111 tie %all_methods, q(Tie::IxHash);
319              
320 29         401 for my $parent_class ( $self->_all_isa($class) ) {
321 6     6   38 no strict 'refs';
  6         11  
  6         403  
322 77         1130 for my $symbol ( keys %{ '::' . $parent_class . '::' } ) {
  77         926  
323 3366 100       63385 $all_methods{$symbol} = 1 if( bless( {}, $class )->can($symbol) );
324             }
325 6     6   27 use strict 'refs';
  6         10  
  6         1630  
326             }
327              
328             ### Reverse the order of methods found, so that for meta-programming iteration, we run
329             ### the methods defined in top-level derived classes last (so they can override
330             ### inherited methods return values and such)
331 29         4792 $____oop_perlish_class_all_methods{$class} = [ reverse keys %all_methods ];
332             }
333              
334 171         9737 return( @{ $____oop_perlish_class_all_methods{$class} } );
  171         3224  
335             }
336              
337             ############################################################################################
338             ## return a list of all-classes that we derive from, in order of:
339             ## (
340             ## self
341             ## parents
342             ## parents of parents
343             ## <...>
344             ## furthest-ancestor
345             ## )
346             ############################################################################################
347             sub _all_isa(@)
348             {
349 393     393   602 my ( $self, $class ) = @_;
350 393   33     1908 $class ||= ref($self) || $self;
      66        
351              
352 393 100       1512 $self->{____isa_hash} = {} unless( exists( $self->{____isa_hash} ) );
353 393 100 66     7156 tie %{ $self->{____isa_hash}->{$class} }, q(Tie::IxHash)
  147         1317  
354             unless( exists( $self->{____isa_hash}->{$class} ) && defined( $self->{____isa_hash}->{$class} ) );
355              
356 393         3424 $self->____recurse_isa($class);
357              
358 393         479 return keys %{ $self->{____isa_hash}->{$class} };
  393         16004  
359             }
360              
361              
362             ############################################################################################
363             ## run a method in all immediate members of @ISA
364             ############################################################################################
365             sub _all_SUPER
366             {
367 263     263   638 my ($self, $method, @args) = @_;
368 263         1297 my $root_class = __PACKAGE__;
369              
370 263         355 for my $parent_class ( grep { !/^\Q$root_class\E$/ } @{ $self->{____CLASS_ISA} } ) {
  283         2459  
  263         587  
371 60 100       508 if($parent_class->can($method)) {
372 6     6   31 no strict 'refs';
  6         10  
  6         350  
373 58         70 my $sub = *{ $parent_class . '::' . $method };
  58         251  
374 6     6   33 use strict;
  6         11  
  6         1010  
375 58 50       64 if(*{ $sub }{CODE}) {
  58         295  
376 0         0 $sub->($self, @args);
377             }
378             }
379             }
380             }
381              
382              
383             ############################################################################################
384             ## DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING!
385             ############################################################################################
386             ## Returns a reference to underlying storage; bypassing validation, untainting, etc.
387             ############################################################################################
388             sub _get_mutable_reference(@)
389             {
390 0     0   0 my ( $self, $name ) = @_;
391              
392 0 0       0 if( $self->can($name) ) {
393 0         0 $self->$name(); ### Do some internal plumbing to make sure that a reference exists if it can exist.
394 0         0 return $self->{___fields}->{$name}->{_Value}; ### should always be a reference to something if it exists
395             }
396             }
397              
398             ############################################################################################
399             ## Set per-instance emit-level via accessor, or per-class emit-level via static
400             ############################################################################################
401             sub _emitlevel(@)
402             {
403 14     14   21 my ( $self, $level ) = @_;
404 14   33     36 my $class = ref($self) || $self;
405              
406 6     6   28 no strict 'refs';
  6         9  
  6         383  
407 14 50       57 my $instance_storage = \$self->{___fields}->{'_emitlevel'}->{_Value} if( ref($self) );
408 14         18 my $class_storage = \${ '::' . $class . '::_OOP_PERLISH_CLASS_EMITLEVEL' };
  14         66  
409 6     6   61 use strict;
  6         10  
  6         2285  
410              
411 14 50       35 my $storage = ( ref($self) ) ? $instance_storage : $class_storage;
412              
413 14 100       30 if($level) {
414 7 50       23 $level =~ m/\D/ && do {
415 0         0 my %level_map = (
416             'fatal' => OOP_PERLISH_CLASS_EMITLEVEL_FATAL,
417             'error' => OOP_PERLISH_CLASS_EMITLEVEL_ERROR,
418             'warning' => OOP_PERLISH_CLASS_EMITLEVEL_WARNING,
419             'info' => OOP_PERLISH_CLASS_EMITLEVEL_INFO,
420             'verbose' => OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE,
421             'debug' => OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0,
422             'debug1' => OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1,
423             'debug2' => OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2
424             );
425 0 0       0 if( exists( $level_map{ lc($level) } ) ) {
426 0         0 $level = $level_map{ lc($level) };
427             }
428             else {
429 0         0 $self->error('invalid level set; cannot set emitlevel');
430             }
431             };
432              
433 7 50       36 return unless( $level =~ m/^\d+$/ );
434 7         11 ${$storage} = $level;
  7         14  
435             }
436 14   0     17 $level = ${$storage} || ${$class_storage} || $main::_OOP_PERLISH_CLASS_EMITLEVEL || 0;
437              
438 14         41 return $level;
439             }
440              
441             ############################################################################################
442             ## set accessors, usually called like 'BEGIN { __PACKAGE__->_accessor(...) }' as the first
443             ## section of any derived class.
444             ############################################################################################
445             sub _accessors(@)
446             {
447 16     16   179 my ( $self, %accessors ) = @_;
448              
449 16   33     92 my $class = ref($self) || $self;
450              
451 16         122 my $accessor_class = $self->_accessor_class_name();
452              
453 16         47 for my $field ( keys %accessors ) {
454 20         23 my %opts = %{ $accessors{$field} };
  20         116  
455              
456 20         108 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field} = $accessor_class->new( %opts, name => $field );
457              
458             ### Symbol table manipulation; creates a method named for the $field in the package's namespace
459             ### The actual method is created via closure in ____oop_perlish_class_accessor_factory();
460 6     6   33 no strict 'refs';
  6         1381  
  6         288  
461 20         132 *{ '::' . $class . '::' . $field } = $self->____oop_perlish_class_accessor_factory($field);
  20         208  
462 6     6   29 use strict;
  6         49  
  6         1893  
463             }
464              
465 16         9235 return;
466             }
467              
468             ############################################################################################
469             ## Handle the magic constuctor argument
470             ## '_____oop_perlish_class__defer__required__fields__validation', set the key value pair
471             ## 'defer_required_fields' to when we see that arg passed to a constructor (and its true)
472             ############################################################################################
473             #sub _magic_constructor_arg_handler_defer_required(@)
474             #{
475             # my ( $self, $opts ) = @_;
476             #
477             # my $key = 'defer_required_fields';
478             # my $defer_required_fields;
479             #
480             # if( exists( $opts->{_____oop_perlish_class__defer__required__fields__validation} ) ) {
481             # $defer_required_fields = $opts->{_____oop_perlish_class__defer__required__fields__validation};
482             # delete $opts->{_____oop_perlish_class__defer__required__fields__validation};
483             # }
484             # return ( $key, $defer_required_fields );
485             #}
486              
487             ############################################################################################
488             ## List all classes which are derived from a given base class (or the class $self was
489             ## instanced from)
490             ############################################################################################
491             sub _derived_classes
492             {
493 0     0   0 my ($self) = @_;
494 0   0     0 my $class = ref($self) || $self;
495              
496 0 0       0 my @derived_classes =
497 0         0 grep { bless( {}, $_ )->isa($class) && $_ ne $class } keys %{ $self->____OOP_PERLISH_CLASS_DERIVED_CLASSES() };
  0         0  
498              
499 0         0 return (@derived_classes);
500             }
501              
502             ############################################################################################
503             ## return an accessor subroutine reference
504             ############################################################################################
505             sub ____oop_perlish_class_accessor_factory(@)
506             {
507 20     20   33 my ( $class, $key ) = @_;
508              
509             return sub {
510 110     110   216 my ( $self, @values ) = @_;
511              
512 110 100       392 return $self->set( $key, @values ) if(@values);
513 55         216 return $self->get($key);
514 20         112 };
515             }
516              
517             ############################################################################################
518             ## recurse @ISA of every class we inherit from
519             ############################################################################################
520             sub ____recurse_isa(@)
521             {
522 742     742   1880 my ( $self, $class, @traverse_isa ) = @_;
523 742         1580 unshift( @traverse_isa, $class );
524              
525 742         957 my @parent_isa = ();
526              
527 742         3906 for my $parent_class ( grep { !exists( $self->{____isa_hash}->{$class}->{$_} ) } @traverse_isa ) {
  956         5159  
528 349         4016 $self->{____isa_hash}->{$class}->{$parent_class} = 1;
529 349         5383 push( @parent_isa, $parent_class );
530 6     6   38 no strict 'refs';
  6         15  
  6         303  
531 349         470 push( @parent_isa, $self->____recurse_isa( $class, @{ $parent_class . '::ISA' } ) );
  349         2011  
532 6     6   36 use strict 'refs';
  6         9  
  6         10807  
533             }
534              
535 742         4964 return @parent_isa;
536             }
537              
538             ############################################################################################
539             ## return a static reference to a hash of accessors for this class; must work for all
540             ## derived classes
541             ############################################################################################
542             sub ____OOP_PERLISH_CLASS_ACCESSORS
543             {
544 870     870   1129 my ($self) = @_;
545 870   66     2583 my $class = ref($self) || $self;
546 870         842 our $____OOP_PERLISH_CLASS_ACCESSORS;
547              
548 870 100       1501 $____OOP_PERLISH_CLASS_ACCESSORS = {} unless( defined($____OOP_PERLISH_CLASS_ACCESSORS) );
549 870 100       1926 $____OOP_PERLISH_CLASS_ACCESSORS->{$class} = {} unless( exists( $____OOP_PERLISH_CLASS_ACCESSORS->{$class} ) );
550              
551 870         4074 return $____OOP_PERLISH_CLASS_ACCESSORS->{$class};
552             }
553              
554             ############################################################################################
555             ## Keep a list of all classes that derive from OOP::Perlish::Class; used in the utility
556             ## method '_derived_classes' to return all children of a given class.
557             ############################################################################################
558             sub ____OOP_PERLISH_CLASS_DERIVED_CLASSES
559             {
560 24     24   48 my ($self) = @_;
561 24         34 our $____OOP_PERLISH_CLASS_DERIVED_CLASSES;
562 24 100       64 $____OOP_PERLISH_CLASS_DERIVED_CLASSES = {} unless( defined($____OOP_PERLISH_CLASS_DERIVED_CLASSES) );
563              
564 24         75 return $____OOP_PERLISH_CLASS_DERIVED_CLASSES;
565             }
566              
567             ############################################################################################
568             ## return a static reference to an array of required fields for this class; must work for
569             ## all derived classes
570             ############################################################################################
571             sub ____OOP_PERLISH_CLASS_REQUIRED_FIELDS
572             {
573 560     560   712 my ($self) = @_;
574 560   33     2217 my $class = ref($self) || $self;
575 560         560 our $____OOP_PERLISH_CLASS_REQUIRED_FIELDS;
576              
577 560 100       1056 $____OOP_PERLISH_CLASS_REQUIRED_FIELDS = {} unless( defined($____OOP_PERLISH_CLASS_REQUIRED_FIELDS) );
578 560 100       1385 $____OOP_PERLISH_CLASS_REQUIRED_FIELDS->{$class} = [] unless( exists( $____OOP_PERLISH_CLASS_REQUIRED_FIELDS->{$class} ) );
579              
580 560         1908 return $____OOP_PERLISH_CLASS_REQUIRED_FIELDS->{$class};
581             }
582              
583             ############################################################################################
584             ## object construction, typically it shouldn't be necessary to overload this directly,
585             ## instead overload one or more of the things it calls
586             ############################################################################################
587             sub ____initialize_object(@)
588             {
589 145     145   420 my ( $self, %opts ) = @_;
590              
591 145         825 my %magic = $self->____process_magic_arguments( \%opts );
592 145 100       481 if( exists( $magic{'return'} ) ) {
593 4         6 my @ret = @{ $magic{'return'} };
  4         9  
594 4 50       11 return @ret if( scalar @ret > 1 );
595 4         15 return ( $ret[0] );
596             }
597              
598             ### Grab our version of %opts from $self->{____oop_perlish_class_opts}, or initialize it if its not been set.
599 141         314 %{ $self->{____oop_perlish_class_opts} } = %opts;
  141         616  
600              
601 141         730 $self->____inherit_accessors();
602 141         974 $self->____pre_validate_opts(); #unless( $magic{defer_required_fields} );
603             ### XXX: unnessessary, and annoying XXX $self->____inherit_constructed_refs();
604             ### XXX: Might want to make a for (@ISA) { $_::_init(@_); } or similar for multiple_inheritance considerations.
605 136         590 $self->____initialize_required_fields();# unless( $magic{defer_required_fields} );
606 134 50       792 return unless( $self->_preinit() );
607 134         683 $self->____initialize_non_required_fields();
608 129 50       537 return unless( $self->_init() );
609 129         344 $self->{__initialized} = 1; # unless($magic{defer_required_fields});
610              
611 129         390 return $self;
612             }
613              
614             ############################################################################################
615             ## Run any method named _magic_constructor_arg_handler* and collect its return tuple into a
616             ## hash called %magic which will be referenced in ____initialize_object; or may mutate
617             ## $self, or do any of a dozen other things.
618             ##
619             ## The key 'return' is considered magical and sacred; if you return in your tuple
620             ## 'return => foo' the constructor will immediately, and before any other initialization
621             ## completes, return the thing you said to return; usually a blessed reference to something;
622             ## be it a singleton, multiton, another object, acme-time-bomb ala wiley coyote, etc.
623             ##
624             ## Your method will be passed a reference to the options passed to the constructor; and may
625             ## (usually should) delete the magical key you are interested in, so that it is not
626             ## considered an accessor later.
627             ##
628             ## This could have been done via attributes, but then it suffers from all the annoyances of
629             ## having to be seen prior to CHECK blocks running, yada yada...
630             ############################################################################################
631             sub ____process_magic_arguments(@)
632             {
633 145     145   252 my ( $self, $opts ) = @_;
634              
635 145         273 my %magic = ();
636              
637 145         695 for( $self->_all_methods() ) {
638 11255 100       24156 m/^_magic_constructor_arg_handler/ && do {
639 6         12 my $method = $_;
640 6         29 my ( $key, $value ) = $self->$method($opts);
641 6 100 66     47 $magic{$key} = $value if( $key && $value );
642             };
643             }
644              
645 145         1206 return %magic;
646             }
647              
648             ############################################################################################
649             ## verify that we have our required fields, even if they don't have real values but have
650             ## defaults instead (a default AND required field would be odd, but is supported)
651             ############################################################################################
652             sub ____pre_validate_opts(@)
653             {
654 140     140   237 my ($self) = @_;
655              
656 140         636 my @required_fields = $self->____identify_required_fields();
657              
658 140         423 for(@required_fields) {
659 198 50 66     645 confess("Missing required field $_")
      66        
660             unless(
661             exists( $self->{____oop_perlish_class_opts}->{$_} )
662             || ( exists( $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$_} )
663             && $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$_}->default_is_set() )
664             );
665             }
666 136         333 return;
667             }
668              
669             ############################################################################################
670             ## obtain the names and references to every accessor in our inheritance
671             ############################################################################################
672             sub ____inherit_accessors(@)
673             {
674 50     50   89 my ($self) = @_;
675              
676             ### Protect overloaded accessors by identifying those in our top-level namespace
677             ### This cascaded up through the inheritance tree
678 50         86 my %top_accessors = ();
679 50 100       70 if( scalar( keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) ) {
  50         169  
680             # XXX: Hash slice assignment
681 31         73 @top_accessors{ keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } } =
  31         77  
682 31         44 ( (1) x ( ( scalar keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) ) );
683             }
684              
685             ### Assimilate inherited accessor references
686             #for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
687 50         140 for my $parent_class ( $self->_all_isa() ) {
688 146 100 66     2261 if( $parent_class && bless( {}, $parent_class )->can('____OOP_PERLISH_CLASS_ACCESSORS') ) {
689 144         174 while( my ( $k, $v ) = each %{ $parent_class->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
  232         566  
690 88 100       240 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$k} = $v unless( exists( $top_accessors{$k} ) ); #protect overloading
691             }
692             }
693             }
694 50         186 return;
695             }
696              
697             ############################################################################################
698             ## run constructors of every class we derive from, and assimilate their %{ $self } hash into
699             ## our own.
700             ############################################################################################
701             ## FIXME: We only support deriving from blessed-hashref classes.
702             ############################################################################################
703             sub ____inherit_constructed_refs
704             {
705 0     0   0 my ($self) = @_;
706              
707 0         0 for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
  0         0  
708 0 0       0 next if( $parent_class eq __PACKAGE__ );
709 0         0 my $tclass = bless( {}, $parent_class );
710 0         0 my $this;
711 0 0       0 if( $tclass->isa(__PACKAGE__) ) {
    0          
712 0         0 $this = $parent_class->new( _____oop_perlish_class__defer__required__fields__validation => 1 );
713             }
714             elsif( $tclass->can('new') ) {
715 0         0 $this = $parent_class->new();
716             }
717             ### FIXME: cleanly handle non-hashref ancestors...
718 0 0 0     0 if( $this && $this->isa('HASH') ) {
719 0         0 while( my ( $key, $val ) = each %{$this} ) {
  0         0  
720 0 0       0 $self->{$key} = $val unless( exists( $self->{$key} ) );
721             }
722 0 0       0 if( exists( $this->{___fields} ) ) {
723 0         0 while( my ( $key, $val ) = each %{ $this->{___fields} } ) {
  0         0  
724 0 0       0 $self->$key( $val->{_Value} ) unless( exists( $self->{___fields}->{$key} ) );
725             }
726             }
727             }
728             }
729 0         0 return;
730             }
731              
732             ############################################################################################
733             ## figure out what fields are required for all derived ancestor classes and ourself.
734             ############################################################################################
735             sub ____identify_required_fields(@)
736             {
737 544     544   749 my ($self) = @_;
738              
739 544   33     1554 my $class = ref($self) || $self;
740              
741 544 100       1379 if( !defined( $self->{____oop_perlish_class_required_fields} ) ) {
742 140         262 my %required_fields = ();
743              
744             ### Obtain REQUIRED_FIELDS static from derived class. Assign it via hashslice
745 140         163 @required_fields{ @{ $class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() } } = @{ $class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() };
  140         428  
  140         581  
746              
747 140         345 while( my ( $name, $field ) = each %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
  201         568  
748 61 100       215 $required_fields{$name} = $name if( $field->required() );
749             }
750              
751             # FIXME: Does not cascade beyond @ISA, should traverse inheritance tree and ensure that all required fields are
752             # provided for any hiararchy. ... does cascade via new, but only to ancesters who conform with us. unsure how to fix
753             #for my $parent_class ( @{ $self->{____CLASS_ISA} } ) {
754 140         462 for my $parent_class ( $self->_all_isa() ) {
755 324 100       5102 if( bless( {}, $parent_class )->can('____OOP_PERLISH_CLASS_REQUIRED_FIELDS') ) {
756 322         753 @required_fields{ @{ $parent_class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() } } =
  322         812  
757 322         367 @{ $parent_class->____OOP_PERLISH_CLASS_REQUIRED_FIELDS() };
758             }
759             }
760              
761 140         527 @{ $self->{____oop_perlish_class_required_fields} } = keys %required_fields;
  140         578  
762             }
763 544         685 return @{ $self->{____oop_perlish_class_required_fields} };
  544         1756  
764             }
765              
766             ############################################################################################
767             ## setup required fields, using their accessors
768             ############################################################################################
769             sub ____initialize_required_fields(@)
770             {
771 136     136   242 my ($self) = @_;
772              
773 136         326 my @required_fields = $self->____identify_required_fields();
774              
775 136         309 for my $method (@required_fields) {
776 193 50 33     1752 $self->$method( $self->{____oop_perlish_class_opts}->{$method} )
777             if( exists( $self->{____oop_perlish_class_opts}->{$method} ) && defined( $self->{____oop_perlish_class_opts}->{$method} ) );
778 192 100 66     635 croak("Invalid required attribute for $method") unless( $self->$method() || $self->is_set($method) );
779             }
780 134         274 return;
781             }
782              
783             ############################################################################################
784             ## setup non-required-fields, using their accessors
785             ############################################################################################
786             sub ____initialize_non_required_fields(@)
787             {
788 134     134   314 my ($self) = @_;
789              
790             ### XXX: Hash slice assignment
791 134         196 my %required_fields_lut;
792 134         325 @required_fields_lut{ $self->____identify_required_fields() } = $self->____identify_required_fields();
793              
794 107         430 my %opts =
795 297         674 map { ( $_ => $self->{____oop_perlish_class_opts}->{$_} ) }
796 134         285 grep { !exists( $required_fields_lut{$_} ) } keys %{ $self->{____oop_perlish_class_opts} };
  134         447  
797              
798             # prepopulate accessors so that calls that cascade will have values assigned
799             # Set everything by accessor that we ->can()
800 134         542 while( my ( $method, $value ) = each %opts ) {
801 107 50       709 $self->$method($value) if( $self->can($method) );
802             }
803              
804 134         573 $self->____validate_defaults();
805              
806             # reset all accessors for actually set values, re-running cascades where applicable...
807             # there must be a better way, but this works
808 129         443 while( my ( $method, $value ) = each %opts ) {
809 107 50       595 $self->$method($value) if( $self->can($method) );
810             }
811 129         314 return;
812             }
813              
814             ############################################################################################
815             ## verify all default values are valid for the class
816             ############################################################################################
817             ## FIXME: make this static
818             ############################################################################################
819             sub ____validate_defaults(@)
820             {
821 134     134   211 my ($self) = @_;
822              
823 134         194 for my $field ( keys %{ $self->____OOP_PERLISH_CLASS_ACCESSORS() } ) {
  134         430  
824 58         119 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->self($self);
825 58         122 $self->____OOP_PERLISH_CLASS_ACCESSORS()->{$field}->__validate_default();
826             }
827 129         310 return;
828             }
829             }
830             1;
831             __END__