Verify type constraints in the constructor
[gitmo/Mouse.git] / lib / Mouse / Object.pm
1 #!/usr/bin/env perl
2 package Mouse::Object;
3 use strict;
4 use warnings;
5 use MRO::Compat;
6
7 use Scalar::Util 'blessed';
8 use Carp 'confess';
9
10 sub new {
11     my $class = shift;
12     my %args  = @_;
13     my $instance = bless {}, $class;
14
15     for my $attribute ($class->meta->attributes) {
16         my $key = $attribute->init_arg;
17         my $default;
18
19         if (!exists($args{$key})) {
20             if (exists($attribute->{default})) {
21                 unless ($attribute->{lazy}) {
22                     my $default = ref($attribute->{default}) eq 'CODE'
23                                 ? $attribute->{default}->()
24                                 : $attribute->{default};
25
26                     $attribute->verify_type_constraint($default)
27                         if $attribute->has_type_constraint;
28
29                     $instance->{$key} = $default;
30
31                     Scalar::Util::weaken($instance->{$key})
32                         if $attribute->{weak_ref};
33                 }
34             }
35             else {
36                 if ($attribute->{required}) {
37                     confess "Attribute '$attribute->{name}' is required";
38                 }
39             }
40         }
41
42         if (exists($args{$key})) {
43             $attribute->verify_type_constraint($args{$key})
44                 if $attribute->has_type_constraint;
45
46             $instance->{$key} = $args{$key};
47
48             Scalar::Util::weaken($instance->{$key})
49                 if $attribute->{weak_ref};
50
51             if ($attribute->{trigger}) {
52                 $attribute->{trigger}->($instance, $args{$key}, $attribute);
53             }
54         }
55     }
56
57     $instance->BUILDALL(\%args);
58
59     return $instance;
60 }
61
62 sub DESTROY { shift->DEMOLISHALL }
63
64 sub BUILDALL {
65     my $self = shift;
66
67     # short circuit
68     return unless $self->can('BUILD');
69
70     no strict 'refs';
71
72     for my $class ($self->meta->linearized_isa) {
73         my $code = *{ $class . '::BUILD' }{CODE}
74             or next;
75         $code->($self, @_);
76     }
77 }
78
79 sub DEMOLISHALL {
80     my $self = shift;
81
82     # short circuit
83     return unless $self->can('DEMOLISH');
84
85     no strict 'refs';
86
87     for my $class ($self->meta->linearized_isa) {
88         my $code = *{ $class . '::DEMOLISH' }{CODE}
89             or next;
90         $code->($self, @_);
91     }
92 }
93
94 1;
95
96 __END__
97
98 =head1 NAME
99
100 Mouse::Object - we don't need to steenkin' constructor
101
102 =head1 METHODS
103
104 =head2 new arguments -> object
105
106 Instantiates a new Mouse::Object. This is obviously intended for subclasses.
107
108 =head2 BUILDALL \%args
109
110 Calls L</BUILD> on each class in the class hierarchy. This is called at the
111 end of L</new>.
112
113 =head2 BUILD \%args
114
115 You may put any business logic initialization in BUILD methods. You don't
116 need to redispatch or return any specific value.
117
118 =head2 DEMOLISHALL
119
120 Calls L</DEMOLISH> on each class in the class hierarchy. This is called at
121 L</DESTROY> time.
122
123 =head2 DEMOLISH
124
125 You may put any business logic deinitialization in DEMOLISH methods. You don't
126 need to redispatch or return any specific value.
127
128 =cut
129