Move is_valid_class_name into XS
[gitmo/Mouse.git] / lib / Mouse / PurePerl.pm
CommitLineData
ccb38d0b 1package Mouse::PurePerl;
2
a52cca04 3require Mouse::Util;
4
6fffa617 5package Mouse::Util;
df6dd016 6
7use strict;
8use warnings;
9
10use warnings FATAL => 'redefine'; # to avoid to load Mouse::PurePerl
11
12use B ();
13
0ffc4183 14
15# taken from Class/MOP.pm
16sub is_valid_class_name {
17 my $class = shift;
18
19 return 0 if ref($class);
20 return 0 unless defined($class);
21
22 return 1 if $class =~ /\A \w+ (?: :: \w+ )* \z/xms;
23
24 return 0;
25}
26
df6dd016 27sub is_class_loaded {
28 my $class = shift;
29
30 return 0 if ref($class) || !defined($class) || !length($class);
31
32 # walk the symbol table tree to avoid autovififying
3bb9e54e 33 # \*{${main::}{"Foo::"}{"Bar::"}} == \*main::Foo::Bar::
df6dd016 34
35 my $pack = \%::;
36 foreach my $part (split('::', $class)) {
3bb9e54e 37 $part .= '::';
38 return 0 if !exists $pack->{$part};
39
40 my $entry = \$pack->{$part};
df6dd016 41 return 0 if ref($entry) ne 'GLOB';
3bb9e54e 42 $pack = *{$entry}{HASH};
df6dd016 43 }
44
3bb9e54e 45 return 0 if !%{$pack};
46
df6dd016 47 # check for $VERSION or @ISA
48 return 1 if exists $pack->{VERSION}
49 && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
50 return 1 if exists $pack->{ISA}
51 && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
52
53 # check for any method
54 foreach my $name( keys %{$pack} ) {
55 my $entry = \$pack->{$name};
56 return 1 if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
57 }
58
59 # fail
60 return 0;
61}
62
63
64# taken from Sub::Identify
65sub get_code_info {
66 my ($coderef) = @_;
67 ref($coderef) or return;
68
69 my $cv = B::svref_2object($coderef);
70 $cv->isa('B::CV') or return;
71
72 my $gv = $cv->GV;
73 $gv->isa('B::GV') or return;
74
75 return ($gv->STASH->NAME, $gv->NAME);
76}
77
78sub get_code_package{
79 my($coderef) = @_;
80
81 my $cv = B::svref_2object($coderef);
82 $cv->isa('B::CV') or return '';
83
84 my $gv = $cv->GV;
85 $gv->isa('B::GV') or return '';
86
87 return $gv->STASH->NAME;
88}
89
7d96ae4d 90sub get_code_ref{
91 my($package, $name) = @_;
92 no strict 'refs';
93 no warnings 'once';
94 use warnings FATAL => 'uninitialized';
95 return *{$package . '::' . $name}{CODE};
96}
97
e3540312 98sub generate_isa_predicate_for {
1d5ecd5f 99 my($for_class, $name) = @_;
100
f48920c1 101 my $predicate = sub{ Scalar::Util::blessed($_[0]) && $_[0]->isa($for_class) };
1d5ecd5f 102
103 if(defined $name){
104 no strict 'refs';
105 *{ caller() . '::' . $name } = $predicate;
106 return;
107 }
108
109 return $predicate;
110}
111
ebe91068 112sub generate_can_predicate_for {
113 my($methods_ref, $name) = @_;
114
115 my @methods = @{$methods_ref};
116
117 my $predicate = sub{
118 my($instance) = @_;
119 if(Scalar::Util::blessed($instance)){
120 foreach my $method(@methods){
121 if(!$instance->can($method)){
122 return 0;
123 }
124 }
125 return 1;
126 }
127 return 0;
128 };
129
130 if(defined $name){
131 no strict 'refs';
132 *{ caller() . '::' . $name } = $predicate;
133 return;
134 }
135
136 return $predicate;
137}
1d5ecd5f 138
6fffa617 139package Mouse::Util::TypeConstraints;
f48920c1 140
141use Scalar::Util qw(blessed looks_like_number openhandle);
142
7d96ae4d 143sub Any { 1 }
144sub Item { 1 }
7d96ae4d 145
146sub Bool { $_[0] ? $_[0] eq '1' : 1 }
147sub Undef { !defined($_[0]) }
148sub Defined { defined($_[0]) }
149sub Value { defined($_[0]) && !ref($_[0]) }
150sub Num { !ref($_[0]) && looks_like_number($_[0]) }
151sub Int { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ }
9848d3e1 152sub Str {
153 my($value) = @_;
154 return defined($value) && ref(\$value) eq 'SCALAR';
155}
7d96ae4d 156
157sub Ref { ref($_[0]) }
9848d3e1 158sub ScalarRef {
159 my($value) = @_;
160 return ref($value) eq 'SCALAR'
161}
7d96ae4d 162sub ArrayRef { ref($_[0]) eq 'ARRAY' }
163sub HashRef { ref($_[0]) eq 'HASH' }
164sub CodeRef { ref($_[0]) eq 'CODE' }
165sub RegexpRef { ref($_[0]) eq 'Regexp' }
166sub GlobRef { ref($_[0]) eq 'GLOB' }
167
168sub FileHandle {
9848d3e1 169 return openhandle($_[0]) || (blessed($_[0]) && $_[0]->isa("IO::Handle"))
7d96ae4d 170}
171
172sub Object { blessed($_[0]) && blessed($_[0]) ne 'Regexp' }
173
174sub ClassName { Mouse::Util::is_class_loaded($_[0]) }
175sub RoleName { (Mouse::Util::class_of($_[0]) || return 0)->isa('Mouse::Meta::Role') }
176
619338ac 177sub _parameterize_ArrayRef_for {
178 my($type_parameter) = @_;
179 my $check = $type_parameter->_compiled_type_constraint;
180
181 return sub {
182 foreach my $value (@{$_}) {
183 return undef unless $check->($value);
184 }
185 return 1;
186 }
187}
188
189sub _parameterize_HashRef_for {
190 my($type_parameter) = @_;
191 my $check = $type_parameter->_compiled_type_constraint;
192
193 return sub {
194 foreach my $value(values %{$_}){
195 return undef unless $check->($value);
196 }
197 return 1;
198 };
199}
200
201# 'Maybe' type accepts 'Any', so it requires parameters
202sub _parameterize_Maybe_for {
203 my($type_parameter) = @_;
204 my $check = $type_parameter->_compiled_type_constraint;
205
206 return sub{
207 return !defined($_) || $check->($_);
208 };
209};
210
211
7d96ae4d 212
6fffa617 213package Mouse::Meta::Module;
43165725 214
926404f2 215sub name { $_[0]->{package} }
216
217sub _method_map { $_[0]->{methods} }
c5f6ad05 218sub _attribute_map{ $_[0]->{attributes} }
43165725 219
2591e962 220sub namespace{
221 my $name = $_[0]->{package};
222 no strict 'refs';
223 return \%{ $name . '::' };
224}
225
3e44140b 226sub add_method {
227 my($self, $name, $code) = @_;
228
229 if(!defined $name){
230 $self->throw_error('You must pass a defined name');
231 }
232 if(!defined $code){
233 $self->throw_error('You must pass a defined code');
234 }
235
236 if(ref($code) ne 'CODE'){
237 $code = \&{$code}; # coerce
238 }
239
240 $self->{methods}->{$name} = $code; # Moose stores meta object here.
241
242 my $pkg = $self->name;
243 no strict 'refs';
244 no warnings 'redefine', 'once';
245 *{ $pkg . '::' . $name } = $code;
246 return;
247}
248
6fffa617 249package Mouse::Meta::Class;
43165725 250
e058b279 251sub method_metaclass { $_[0]->{method_metaclass} || 'Mouse::Meta::Method' }
252sub attribute_metaclass { $_[0]->{attribute_metaclass} || 'Mouse::Meta::Attribute' }
253
254sub constructor_class { $_[0]->{constructor_class} || 'Mouse::Meta::Method::Constructor' }
255sub destructor_class { $_[0]->{destructor_class} || 'Mouse::Meta::Method::Destructor' }
4e7e3250 256
43165725 257sub is_anon_class{
258 return exists $_[0]->{anon_serial_id};
259}
260
261sub roles { $_[0]->{roles} }
262
cccb83de 263sub linearized_isa { @{ get_linear_isa($_[0]->{package}) } }
264
da4432f3 265sub get_all_attributes {
266 my($self) = @_;
047d7af0 267 my %attrs = map { %{ $self->initialize($_)->{attributes} } } reverse $self->linearized_isa;
da4432f3 268 return values %attrs;
269}
270
ba153b33 271sub new_object {
272 my $self = shift;
273 my %args = (@_ == 1 ? %{$_[0]} : @_);
274
275 my $object = bless {}, $self->name;
276
277 $self->_initialize_object($object, \%args);
278 return $object;
279}
280
4e7e3250 281sub _initialize_object{
282 my($self, $object, $args, $ignore_triggers) = @_;
283
284 my @triggers_queue;
285
286 foreach my $attribute ($self->get_all_attributes) {
287 my $init_arg = $attribute->init_arg;
288 my $slot = $attribute->name;
289
290 if (defined($init_arg) && exists($args->{$init_arg})) {
291 $object->{$slot} = $attribute->_coerce_and_verify($args->{$init_arg}, $object);
292
293 weaken($object->{$slot})
294 if ref($object->{$slot}) && $attribute->is_weak_ref;
295
296 if ($attribute->has_trigger) {
297 push @triggers_queue, [ $attribute->trigger, $object->{$slot} ];
298 }
299 }
300 else { # no init arg
301 if ($attribute->has_default || $attribute->has_builder) {
302 if (!$attribute->is_lazy) {
303 my $default = $attribute->default;
304 my $builder = $attribute->builder;
305 my $value = $builder ? $object->$builder()
306 : ref($default) eq 'CODE' ? $object->$default()
307 : $default;
308
309 $object->{$slot} = $attribute->_coerce_and_verify($value, $object);
310
311 weaken($object->{$slot})
312 if ref($object->{$slot}) && $attribute->is_weak_ref;
313 }
314 }
315 elsif($attribute->is_required) {
316 $self->throw_error("Attribute (".$attribute->name.") is required");
317 }
318 }
319 }
320
321 if(!$ignore_triggers){
322 foreach my $trigger_and_value(@triggers_queue){
323 my($trigger, $value) = @{$trigger_and_value};
324 $trigger->($object, $value);
325 }
326 }
327
328 if($self->is_anon_class){
329 $object->{__METACLASS__} = $self;
330 }
331
332 return;
333}
334
80efe911 335sub is_immutable { $_[0]->{is_immutable} }
4e7e3250 336
e128626c 337sub __strict_constructor{ $_[0]->{strict_constructor} }
338
6fffa617 339package Mouse::Meta::Role;
43165725 340
e058b279 341sub method_metaclass{ $_[0]->{method_metaclass} || 'Mouse::Meta::Role::Method' }
342
43165725 343sub is_anon_role{
344 return exists $_[0]->{anon_serial_id};
345}
346
347sub get_roles { $_[0]->{roles} }
348
6fffa617 349package Mouse::Meta::Attribute;
43165725 350
e058b279 351require Mouse::Meta::Method::Accessor;
352
353sub accessor_metaclass{ $_[0]->{accessor_metaclass} || 'Mouse::Meta::Method::Accessor' }
43165725 354
355# readers
356
357sub name { $_[0]->{name} }
358sub associated_class { $_[0]->{associated_class} }
359
360sub accessor { $_[0]->{accessor} }
361sub reader { $_[0]->{reader} }
362sub writer { $_[0]->{writer} }
363sub predicate { $_[0]->{predicate} }
364sub clearer { $_[0]->{clearer} }
365sub handles { $_[0]->{handles} }
366
367sub _is_metadata { $_[0]->{is} }
368sub is_required { $_[0]->{required} }
369sub default { $_[0]->{default} }
370sub is_lazy { $_[0]->{lazy} }
371sub is_lazy_build { $_[0]->{lazy_build} }
372sub is_weak_ref { $_[0]->{weak_ref} }
373sub init_arg { $_[0]->{init_arg} }
374sub type_constraint { $_[0]->{type_constraint} }
375
376sub trigger { $_[0]->{trigger} }
377sub builder { $_[0]->{builder} }
378sub should_auto_deref { $_[0]->{auto_deref} }
379sub should_coerce { $_[0]->{coerce} }
380
d899d3e7 381sub documentation { $_[0]->{documentation} }
382
43165725 383# predicates
384
385sub has_accessor { exists $_[0]->{accessor} }
386sub has_reader { exists $_[0]->{reader} }
387sub has_writer { exists $_[0]->{writer} }
388sub has_predicate { exists $_[0]->{predicate} }
389sub has_clearer { exists $_[0]->{clearer} }
390sub has_handles { exists $_[0]->{handles} }
391
392sub has_default { exists $_[0]->{default} }
393sub has_type_constraint { exists $_[0]->{type_constraint} }
394sub has_trigger { exists $_[0]->{trigger} }
395sub has_builder { exists $_[0]->{builder} }
396
d899d3e7 397sub has_documentation { exists $_[0]->{documentation} }
398
ba1f50a2 399sub _process_options{
400 my($class, $name, $args) = @_;
401
402 # taken from Class::MOP::Attribute::new
403
404 defined($name)
405 or $class->throw_error('You must provide a name for the attribute');
406
407 if(!exists $args->{init_arg}){
408 $args->{init_arg} = $name;
409 }
410
411 # 'required' requires eigher 'init_arg', 'builder', or 'default'
412 my $can_be_required = defined( $args->{init_arg} );
413
414 if(exists $args->{builder}){
415 # XXX:
416 # Moose refuses a CODE ref builder, but Mouse doesn't for backward compatibility
417 # This feature will be changed in a future. (gfx)
418 $class->throw_error('builder must be a defined scalar value which is a method name')
419 #if ref $args->{builder} || !defined $args->{builder};
420 if !defined $args->{builder};
421
422 $can_be_required++;
423 }
424 elsif(exists $args->{default}){
425 if(ref $args->{default} && ref($args->{default}) ne 'CODE'){
426 $class->throw_error("References are not allowed as default values, you must "
427 . "wrap the default of '$name' in a CODE reference (ex: sub { [] } and not [])");
428 }
429 $can_be_required++;
430 }
431
432 if( $args->{required} && !$can_be_required ) {
433 $class->throw_error("You cannot have a required attribute ($name) without a default, builder, or an init_arg");
434 }
435
436 # taken from Mouse::Meta::Attribute->new and ->_process_args
437
438 if(exists $args->{is}){
439 my $is = $args->{is};
440
441 if($is eq 'ro'){
442 $args->{reader} ||= $name;
443 }
444 elsif($is eq 'rw'){
445 if(exists $args->{writer}){
446 $args->{reader} ||= $name;
447 }
448 else{
449 $args->{accessor} ||= $name;
450 }
451 }
452 elsif($is eq 'bare'){
453 # do nothing, but don't complain (later) about missing methods
454 }
455 else{
456 $is = 'undef' if !defined $is;
457 $class->throw_error("I do not understand this option (is => $is) on attribute ($name)");
458 }
459 }
460
461 my $tc;
462 if(exists $args->{isa}){
d503a4f3 463 $tc = $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($args->{isa});
ba1f50a2 464 }
d503a4f3 465
466 if(exists $args->{does}){
467 if(defined $tc){ # both isa and does supplied
468 my $does_ok = do{
469 local $@;
470 eval{ "$tc"->does($args) };
471 };
472 if(!$does_ok){
473 $class->throw_error("Cannot have both an isa option and a does option because '$tc' does not do '$args->{does}' on attribute ($name)");
474 }
475 }
476 else {
477 $tc = $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_does_type_constraint($args->{does});
478 }
ba1f50a2 479 }
ba1f50a2 480
481 if($args->{coerce}){
482 defined($tc)
483 || $class->throw_error("You cannot have coercion without specifying a type constraint on attribute ($name)");
484
485 $args->{weak_ref}
486 && $class->throw_error("You cannot have a weak reference to a coerced value on attribute ($name)");
487 }
488
489 if ($args->{lazy_build}) {
490 exists($args->{default})
491 && $class->throw_error("You can not use lazy_build and default for the same attribute ($name)");
492
493 $args->{lazy} = 1;
494 $args->{builder} ||= "_build_${name}";
495 if ($name =~ /^_/) {
496 $args->{clearer} ||= "_clear${name}";
497 $args->{predicate} ||= "_has${name}";
498 }
499 else {
500 $args->{clearer} ||= "clear_${name}";
501 $args->{predicate} ||= "has_${name}";
502 }
503 }
504
505 if ($args->{auto_deref}) {
506 defined($tc)
507 || $class->throw_error("You cannot auto-dereference without specifying a type constraint on attribute ($name)");
508
509 ( $tc->is_a_type_of('ArrayRef') || $tc->is_a_type_of('HashRef') )
510 || $class->throw_error("You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)");
511 }
512
513 if (exists $args->{trigger}) {
514 ('CODE' eq ref $args->{trigger})
515 || $class->throw_error("Trigger must be a CODE ref on attribute ($name)");
516 }
517
518 if ($args->{lazy}) {
519 (exists $args->{default} || defined $args->{builder})
520 || $class->throw_error("You cannot have lazy attribute ($name) without specifying a default value for it");
521 }
522
523 return;
524}
525
526
6fffa617 527package Mouse::Meta::TypeConstraint;
43165725 528
529sub name { $_[0]->{name} }
530sub parent { $_[0]->{parent} }
531sub message { $_[0]->{message} }
532
fc83f4cf 533sub type_parameter { $_[0]->{type_parameter} }
534sub __is_parameterized { exists $_[0]->{type_parameter} }
535
43165725 536sub _compiled_type_constraint{ $_[0]->{compiled_type_constraint} }
537
93540011 538sub _compiled_type_coercion { $_[0]->{_compiled_type_coercion} }
df6dd016 539
93540011 540sub has_coercion{ exists $_[0]->{_compiled_type_coercion} }
df6dd016 541
f790c46b 542
543sub compile_type_constraint{
544 my($self) = @_;
545
546 # add parents first
547 my @checks;
548 for(my $parent = $self->{parent}; defined $parent; $parent = $parent->{parent}){
549 if($parent->{hand_optimized_type_constraint}){
550 unshift @checks, $parent->{hand_optimized_type_constraint};
551 last; # a hand optimized constraint must include all the parents
552 }
553 elsif($parent->{constraint}){
554 unshift @checks, $parent->{constraint};
555 }
556 }
557
558 # then add child
559 if($self->{constraint}){
560 push @checks, $self->{constraint};
561 }
562
563 if($self->{type_constraints}){ # Union
564 my @types = map{ $_->{compiled_type_constraint} } @{ $self->{type_constraints} };
565 push @checks, sub{
566 foreach my $c(@types){
567 return 1 if $c->($_[0]);
568 }
569 return 0;
570 };
571 }
572
573 if(@checks == 0){
574 $self->{compiled_type_constraint} = \&Mouse::Util::TypeConstraints::Any;
575 }
576 else{
577 $self->{compiled_type_constraint} = sub{
578 my(@args) = @_;
579 local $_ = $args[0];
580 foreach my $c(@checks){
581 return undef if !$c->(@args);
582 }
583 return 1;
584 };
585 }
586 return;
587}
588
6fffa617 589package Mouse::Object;
aa2d2e2c 590
591
592sub BUILDARGS {
593 my $class = shift;
f790c46b 594
aa2d2e2c 595 if (scalar @_ == 1) {
596 (ref($_[0]) eq 'HASH')
597 || $class->meta->throw_error("Single parameters to new() must be a HASH ref");
598
599 return {%{$_[0]}};
600 }
601 else {
602 return {@_};
603 }
604}
f790c46b 605
af04626d 606sub new {
607 my $class = shift;
608
609 $class->meta->throw_error('Cannot call new() on an instance') if ref $class;
610
611 my $args = $class->BUILDARGS(@_);
612
613 my $meta = Mouse::Meta::Class->initialize($class);
614 my $self = $meta->new_object($args);
615
616 # BUILDALL
617 if( $self->can('BUILD') ) {
618 for my $class (reverse $meta->linearized_isa) {
619 my $build = Mouse::Util::get_code_ref($class, 'BUILD')
620 || next;
621
622 $self->$build($args);
623 }
624 }
625
626 return $self;
627}
628
a5c683f6 629sub DESTROY {
630 my $self = shift;
631
632 return unless $self->can('DEMOLISH'); # short circuit
633
634 local $?;
635
636 my $e = do{
637 local $@;
638 eval{
639
640 # DEMOLISHALL
641
642 # We cannot count on being able to retrieve a previously made
643 # metaclass, _or_ being able to make a new one during global
644 # destruction. However, we should still be able to use mro at
645 # that time (at least tests suggest so ;)
646
647 foreach my $class (@{ Mouse::Util::get_linear_isa(ref $self) }) {
648 my $demolish = Mouse::Util::get_code_ref($class, 'DEMOLISH')
649 || next;
650
6514735e 651 $self->$demolish($Mouse::Util::in_global_destruction);
a5c683f6 652 }
653 };
654 $@;
655 };
656
657 no warnings 'misc';
658 die $e if $e; # rethrow
659}
660
adb5eb76 661sub BUILDALL {
662 my $self = shift;
663
664 # short circuit
665 return unless $self->can('BUILD');
666
667 for my $class (reverse $self->meta->linearized_isa) {
668 my $build = Mouse::Util::get_code_ref($class, 'BUILD')
669 || next;
670
671 $self->$build(@_);
672 }
673 return;
674}
675
676sub DEMOLISHALL;
677*DEMOLISHALL = \&DESTROY;
678
df6dd016 6791;
680__END__
ccb38d0b 681
682=head1 NAME
683
684Mouse::PurePerl - A Mouse guts in pure Perl
685
686=head1 VERSION
687
4bc73e47 688This document describes Mouse version 0.50_03
ccb38d0b 689
690=head1 SEE ALSO
691
692L<Mouse::XS>
693
694=cut