Remove duplications and cleanup
[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/;
9 use Carp 'confess';
10
11 use base qw(Mouse::Meta::Module);
12
13
14 sub _new {
15     my($class, %args) = @_;
16
17     $args{attributes} ||= {};
18     $args{methods}    ||= {};
19     $args{roles}      ||= [];
20
21     $args{superclasses} = do {
22         no strict 'refs';
23         \@{ $args{package} . '::ISA' };
24     };
25
26     bless \%args, $class;
27 }
28
29 sub roles { $_[0]->{roles} }
30
31 sub superclasses {
32     my $self = shift;
33
34     if (@_) {
35         Mouse::load_class($_) for @_;
36         @{ $self->{superclasses} } = @_;
37     }
38
39     @{ $self->{superclasses} };
40 }
41
42 sub get_all_method_names {
43     my $self = shift;
44     my %uniq;
45     return grep { $uniq{$_}++ == 0 }
46             map { Mouse::Meta::Class->initialize($_)->get_method_list() }
47             $self->linearized_isa;
48 }
49
50 sub add_attribute {
51     my $self = shift;
52
53     if (@_ == 1 && blessed($_[0])) {
54         my $attr = shift @_;
55         $self->{'attributes'}{$attr->name} = $attr;
56     } else {
57         my $names = shift @_;
58         $names = [$names] if !ref($names);
59         my $metaclass = 'Mouse::Meta::Attribute';
60         my %options = @_;
61
62         if ( my $metaclass_name = delete $options{metaclass} ) {
63             my $new_class = Mouse::Util::resolve_metaclass_alias(
64                 'Attribute',
65                 $metaclass_name
66             );
67             if ( $metaclass ne $new_class ) {
68                 $metaclass = $new_class;
69             }
70         }
71
72         for my $name (@$names) {
73             if ($name =~ s/^\+//) {
74                 $metaclass->clone_parent($self, $name, @_);
75             }
76             else {
77                 $metaclass->create($self, $name, @_);
78             }
79         }
80     }
81 }
82
83 sub compute_all_applicable_attributes { shift->get_all_attributes(@_) }
84 sub get_all_attributes {
85     my $self = shift;
86     my (@attr, %seen);
87
88     for my $class ($self->linearized_isa) {
89         my $meta = $self->_metaclass_cache($class)
90             or next;
91
92         for my $name (keys %{ $meta->get_attribute_map }) {
93             next if $seen{$name}++;
94             push @attr, $meta->get_attribute($name);
95         }
96     }
97
98     return @attr;
99 }
100
101 sub linearized_isa { @{ get_linear_isa($_[0]->name) } }
102
103 sub new_object {
104     my $self = shift;
105     my $args = (@_ == 1) ? $_[0] : { @_ };
106
107     foreach my $attribute ($self->meta->get_all_attributes) {
108         my $from = $attribute->init_arg;
109         my $key  = $attribute->name;
110
111         if (defined($from) && exists($args->{$from})) {
112             $args->{$from} = $attribute->coerce_constraint($args->{$from})
113                 if $attribute->should_coerce;
114             $attribute->verify_against_type_constraint($args->{$from});
115
116             $instance->{$key} = $args->{$from};
117
118             weaken($instance->{$key})
119                 if $attribute->is_weak_ref;
120
121             if ($attribute->has_trigger) {
122                 $attribute->trigger->($instance, $args->{$from});
123             }
124         }
125         else {
126             if ($attribute->has_default || $attribute->has_builder) {
127                 unless ($attribute->is_lazy) {
128                     my $default = $attribute->default;
129                     my $builder = $attribute->builder;
130                     my $value = $attribute->has_builder
131                               ? $instance->$builder
132                               : ref($default) eq 'CODE'
133                                   ? $default->($instance)
134                                   : $default;
135
136                     $value = $attribute->coerce_constraint($value)
137                         if $attribute->should_coerce;
138                     $attribute->verify_against_type_constraint($value);
139
140                     $instance->{$key} = $value;
141
142                     weaken($instance->{$key})
143                         if $attribute->is_weak_ref;
144                 }
145             }
146             else {
147                 if ($attribute->is_required) {
148                     confess "Attribute (".$attribute->name.") is required";
149                 }
150             }
151         }
152     }
153     return $instance;
154 }
155
156 sub clone_object {
157     my $class    = shift;
158     my $instance = shift;
159
160     (blessed($instance) && $instance->isa($class->name))
161         || confess "You must pass an instance of the metaclass (" . $class->name . "), not ($instance)";
162
163     $class->clone_instance($instance, @_);
164 }
165
166 sub clone_instance {
167     my ($class, $instance, %params) = @_;
168
169     (blessed($instance))
170         || confess "You can only clone instances, ($instance) is not a blessed instance";
171
172     my $clone = bless { %$instance }, ref $instance;
173
174     foreach my $attr ($class->get_all_attributes()) {
175         if ( defined( my $init_arg = $attr->init_arg ) ) {
176             if (exists $params{$init_arg}) {
177                 $clone->{ $attr->name } = $params{$init_arg};
178             }
179         }
180     }
181
182     return $clone;
183
184 }
185
186 sub make_immutable {
187     my $self = shift;
188     my %args = (
189         inline_constructor => 1,
190         inline_destructor  => 1,
191         @_,
192     );
193
194     my $name = $self->name;
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 { confess "Mouse does not currently support 'make_mutable'" }
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 confess "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         || confess "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         || confess "You must pass an ARRAY ref of superclasses"
303             if exists $options{superclasses};
304
305     (ref $options{attributes} eq 'ARRAY')
306         || confess "You must pass an ARRAY ref of attributes"
307             if exists $options{attributes};
308
309     (ref $options{methods} eq 'HASH')
310         || confess "You must pass a HASH ref of methods"
311             if exists $options{methods};
312
313     do {
314         ( defined $package_name && $package_name )
315           || confess "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         confess "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