Use throw_error() instead of confess()
[gitmo/Mouse.git] / lib / Mouse / Meta / Class.pm
1 package Mouse::Meta::Class;
2 use strict;
3 use warnings;
4
5 use Mouse::Meta::Method::Constructor;
6 use Mouse::Meta::Method::Destructor;
7 use Scalar::Util qw/blessed weaken/;
8 use Mouse::Util qw/get_linear_isa not_supported/;
9
10 use base qw(Mouse::Meta::Module);
11
12
13 sub _new {
14     my($class, %args) = @_;
15
16     $args{attributes} ||= {};
17     $args{methods}    ||= {};
18     $args{roles}      ||= [];
19
20     $args{superclasses} = do {
21         no strict 'refs';
22         \@{ $args{package} . '::ISA' };
23     };
24
25     bless \%args, $class;
26 }
27
28 sub roles { $_[0]->{roles} }
29
30 sub superclasses {
31     my $self = shift;
32
33     if (@_) {
34         Mouse::load_class($_) for @_;
35         @{ $self->{superclasses} } = @_;
36     }
37
38     @{ $self->{superclasses} };
39 }
40
41 sub get_all_method_names {
42     my $self = shift;
43     my %uniq;
44     return grep { $uniq{$_}++ == 0 }
45             map { Mouse::Meta::Class->initialize($_)->get_method_list() }
46             $self->linearized_isa;
47 }
48
49 sub add_attribute {
50     my $self = shift;
51
52     if (@_ == 1 && blessed($_[0])) {
53         my $attr = shift @_;
54         $self->{'attributes'}{$attr->name} = $attr;
55     } else {
56         my $names = shift @_;
57         $names = [$names] if !ref($names);
58         my $metaclass = 'Mouse::Meta::Attribute';
59         my %options = @_;
60
61         if ( my $metaclass_name = delete $options{metaclass} ) {
62             my $new_class = Mouse::Util::resolve_metaclass_alias(
63                 'Attribute',
64                 $metaclass_name
65             );
66             if ( $metaclass ne $new_class ) {
67                 $metaclass = $new_class;
68             }
69         }
70
71         for my $name (@$names) {
72             if ($name =~ s/^\+//) {
73                 $metaclass->clone_parent($self, $name, @_);
74             }
75             else {
76                 $metaclass->create($self, $name, @_);
77             }
78         }
79     }
80 }
81
82 sub compute_all_applicable_attributes { shift->get_all_attributes(@_) }
83 sub get_all_attributes {
84     my $self = shift;
85     my (@attr, %seen);
86
87     for my $class ($self->linearized_isa) {
88         my $meta = $self->_metaclass_cache($class)
89             or next;
90
91         for my $name (keys %{ $meta->get_attribute_map }) {
92             next if $seen{$name}++;
93             push @attr, $meta->get_attribute($name);
94         }
95     }
96
97     return @attr;
98 }
99
100 sub linearized_isa { @{ get_linear_isa($_[0]->name) } }
101
102 sub new_object {
103     my $self = shift;
104     my $args = (@_ == 1) ? $_[0] : { @_ };
105
106     my $instance = bless {}, $self->name;
107
108     foreach my $attribute ($self->get_all_attributes) {
109         my $from = $attribute->init_arg;
110         my $key  = $attribute->name;
111
112         if (defined($from) && exists($args->{$from})) {
113             $args->{$from} = $attribute->coerce_constraint($args->{$from})
114                 if $attribute->should_coerce;
115             $attribute->verify_against_type_constraint($args->{$from});
116
117             $instance->{$key} = $args->{$from};
118
119             weaken($instance->{$key})
120                 if ref($instance->{$key}) && $attribute->is_weak_ref;
121
122             if ($attribute->has_trigger) {
123                 $attribute->trigger->($instance, $args->{$from});
124             }
125         }
126         else {
127             if ($attribute->has_default || $attribute->has_builder) {
128                 unless ($attribute->is_lazy) {
129                     my $default = $attribute->default;
130                     my $builder = $attribute->builder;
131                     my $value = $attribute->has_builder
132                               ? $instance->$builder
133                               : ref($default) eq 'CODE'
134                                   ? $default->($instance)
135                                   : $default;
136
137                     $value = $attribute->coerce_constraint($value)
138                         if $attribute->should_coerce;
139                     $attribute->verify_against_type_constraint($value);
140
141                     $instance->{$key} = $value;
142
143                     weaken($instance->{$key})
144                         if ref($instance->{$key}) && $attribute->is_weak_ref;
145                 }
146             }
147             else {
148                 if ($attribute->is_required) {
149                     $self->throw_error("Attribute (".$attribute->name.") is required");
150                 }
151             }
152         }
153     }
154     return $instance;
155 }
156
157 sub clone_object {
158     my $class    = shift;
159     my $instance = shift;
160
161     (blessed($instance) && $instance->isa($class->name))
162         || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($instance)");
163
164     $class->clone_instance($instance, @_);
165 }
166
167 sub clone_instance {
168     my ($class, $instance, %params) = @_;
169
170     (blessed($instance))
171         || $class->throw_error("You can only clone instances, ($instance) is not a blessed instance");
172
173     my $clone = bless { %$instance }, ref $instance;
174
175     foreach my $attr ($class->get_all_attributes()) {
176         if ( defined( my $init_arg = $attr->init_arg ) ) {
177             if (exists $params{$init_arg}) {
178                 $clone->{ $attr->name } = $params{$init_arg};
179             }
180         }
181     }
182
183     return $clone;
184
185 }
186
187 sub make_immutable {
188     my $self = shift;
189     my %args = (
190         inline_constructor => 1,
191         inline_destructor  => 1,
192         @_,
193     );
194
195     $self->{is_immutable}++;
196
197     if ($args{inline_constructor}) {
198         $self->add_method('new' => Mouse::Meta::Method::Constructor->generate_constructor_method_inline( $self ));
199     }
200
201     if ($args{inline_destructor}) {
202         $self->add_method('DESTROY' => Mouse::Meta::Method::Destructor->generate_destructor_method_inline( $self ));
203     }
204
205     # Moose's make_immutable returns true allowing calling code to skip setting an explicit true value
206     # at the end of a source file. 
207     return 1;
208 }
209
210 sub make_mutable { not_supported }
211
212 sub is_immutable { $_[0]->{is_immutable} }
213
214 sub attribute_metaclass { "Mouse::Meta::Class" }
215
216 sub _install_modifier {
217     my ( $self, $into, $type, $name, $code ) = @_;
218
219     # which is modifer class available?
220     my $modifier_class = do {
221         if (eval "require Class::Method::Modifiers::Fast; 1") {
222             'Class::Method::Modifiers::Fast';
223         } elsif (eval "require Class::Method::Modifiers; 1") {
224             'Class::Method::Modifiers';
225         } else {
226             Carp::croak("Method modifiers require the use of Class::Method::Modifiers or Class::Method::Modifiers::Fast. Please install it from CPAN and file a bug report with this application.");
227         }
228     };
229     my $modifier = $modifier_class->can('_install_modifier');
230
231     # replace this method itself :)
232     {
233         no strict 'refs';
234         no warnings 'redefine';
235         *{__PACKAGE__ . '::_install_modifier'} = sub {
236             my ( $self, $into, $type, $name, $code ) = @_;
237             $modifier->(
238                 $into,
239                 $type,
240                 $name,
241                 $code
242             );
243         };
244     }
245
246     # call me. for first time.
247     $self->_install_modifier( $into, $type, $name, $code );
248 }
249
250 sub add_before_method_modifier {
251     my ( $self, $name, $code ) = @_;
252     $self->_install_modifier( $self->name, 'before', $name, $code );
253 }
254
255 sub add_around_method_modifier {
256     my ( $self, $name, $code ) = @_;
257     $self->_install_modifier( $self->name, 'around', $name, $code );
258 }
259
260 sub add_after_method_modifier {
261     my ( $self, $name, $code ) = @_;
262     $self->_install_modifier( $self->name, 'after', $name, $code );
263 }
264
265 sub add_override_method_modifier {
266     my ($self, $name, $code) = @_;
267
268     my $pkg = $self->name;
269     my $method = "${pkg}::${name}";
270
271     # Class::Method::Modifiers won't do this for us, so do it ourselves
272
273     my $body = $pkg->can($name)
274         or $self->throw_error("You cannot override '$method' because it has no super method");
275
276     no strict 'refs';
277     *$method = sub { $code->($pkg, $body, @_) };
278 }
279
280 sub does_role {
281     my ($self, $role_name) = @_;
282
283     (defined $role_name)
284         || $self->throw_error("You must supply a role name to look for");
285
286     for my $class ($self->linearized_isa) {
287         my $meta = Mouse::class_of($class);
288         next unless $meta && $meta->can('roles');
289
290         for my $role (@{ $meta->roles }) {
291             return 1 if $role->does_role($role_name);
292         }
293     }
294
295     return 0;
296 }
297
298 sub create {
299     my ($class, $package_name, %options) = @_;
300
301     (ref $options{superclasses} eq 'ARRAY')
302         || $class->throw_error("You must pass an ARRAY ref of superclasses")
303             if exists $options{superclasses};
304
305     (ref $options{attributes} eq 'ARRAY')
306         || $class->throw_error("You must pass an ARRAY ref of attributes")
307             if exists $options{attributes};
308
309     (ref $options{methods} eq 'HASH')
310         || $class->throw_error("You must pass a HASH ref of methods")
311             if exists $options{methods};
312
313     do {
314         ( defined $package_name && $package_name )
315           || $class->throw_error("You must pass a package name");
316
317         my $code = "package $package_name;";
318         $code .= "\$$package_name\:\:VERSION = '" . $options{version} . "';"
319           if exists $options{version};
320         $code .= "\$$package_name\:\:AUTHORITY = '" . $options{authority} . "';"
321           if exists $options{authority};
322
323         eval $code;
324         $class->throw_error("creation of $package_name failed : $@") if $@;
325     };
326
327     my %initialize_options = %options;
328     delete @initialize_options{qw(
329         package
330         superclasses
331         attributes
332         methods
333         version
334         authority
335     )};
336     my $meta = $class->initialize( $package_name => %initialize_options );
337
338     # FIXME totally lame
339     $meta->add_method('meta' => sub {
340         Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
341     });
342
343     $meta->superclasses(@{$options{superclasses}})
344         if exists $options{superclasses};
345     # NOTE:
346     # process attributes first, so that they can
347     # install accessors, but locally defined methods
348     # can then overwrite them. It is maybe a little odd, but
349     # I think this should be the order of things.
350     if (exists $options{attributes}) {
351         foreach my $attr (@{$options{attributes}}) {
352             Mouse::Meta::Attribute->create($meta, $attr->{name}, %$attr);
353         }
354     }
355     if (exists $options{methods}) {
356         foreach my $method_name (keys %{$options{methods}}) {
357             $meta->add_method($method_name, $options{methods}->{$method_name});
358         }
359     }
360     return $meta;
361 }
362
363 {
364     my $ANON_CLASS_SERIAL = 0;
365     my $ANON_CLASS_PREFIX = 'Mouse::Meta::Class::__ANON__::SERIAL::';
366     sub create_anon_class {
367         my ( $class, %options ) = @_;
368         my $package_name = $ANON_CLASS_PREFIX . ++$ANON_CLASS_SERIAL;
369         return $class->create( $package_name, %options );
370     }
371 }
372
373 1;
374
375 __END__
376
377 =head1 NAME
378
379 Mouse::Meta::Class - hook into the Mouse MOP
380
381 =head1 METHODS
382
383 =head2 initialize ClassName -> Mouse::Meta::Class
384
385 Finds or creates a Mouse::Meta::Class instance for the given ClassName. Only
386 one instance should exist for a given class.
387
388 =head2 new %args -> Mouse::Meta::Class
389
390 Creates a new Mouse::Meta::Class. Don't call this directly.
391
392 =head2 name -> ClassName
393
394 Returns the name of the owner class.
395
396 =head2 superclasses -> [ClassName]
397
398 Gets (or sets) the list of superclasses of the owner class.
399
400 =head2 add_attribute (Mouse::Meta::Attribute| name => spec)
401
402 Begins keeping track of the existing L<Mouse::Meta::Attribute> for the owner
403 class.
404
405 =head2 get_all_attributes -> (Mouse::Meta::Attribute)
406
407 Returns the list of all L<Mouse::Meta::Attribute> instances associated with
408 this class and its superclasses.
409
410 =head2 get_attribute_map -> { name => Mouse::Meta::Attribute }
411
412 Returns a mapping of attribute names to their corresponding
413 L<Mouse::Meta::Attribute> objects.
414
415 =head2 get_attribute_list -> { name => Mouse::Meta::Attribute }
416
417 This returns a list of attribute names which are defined in the local
418 class. If you want a list of all applicable attributes for a class,
419 use the C<get_all_attributes> method.
420
421 =head2 has_attribute Name -> Bool
422
423 Returns whether we have a L<Mouse::Meta::Attribute> with the given name.
424
425 =head2 get_attribute Name -> Mouse::Meta::Attribute | undef
426
427 Returns the L<Mouse::Meta::Attribute> with the given name.
428
429 =head2 linearized_isa -> [ClassNames]
430
431 Returns the list of classes in method dispatch order, with duplicates removed.
432
433 =head2 clone_object Instance -> Instance
434
435 Clones the given C<Instance> which must be an instance governed by this
436 metaclass.
437
438 =head2 clone_instance Instance, Parameters -> Instance
439
440 The clone_instance method has been made private.
441 The public version is deprecated.
442
443 =cut
444