Remove an unused method
[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 _install_modifier {
215     my ( $self, $into, $type, $name, $code ) = @_;
216
217     # which is modifer class available?
218     my $modifier_class = do {
219         if (eval "require Class::Method::Modifiers::Fast; 1") {
220             'Class::Method::Modifiers::Fast';
221         } elsif (eval "require Class::Method::Modifiers; 1") {
222             'Class::Method::Modifiers';
223         } else {
224             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.");
225         }
226     };
227     my $modifier = $modifier_class->can('_install_modifier');
228
229     # replace this method itself :)
230     {
231         no strict 'refs';
232         no warnings 'redefine';
233         *{__PACKAGE__ . '::_install_modifier'} = sub {
234             my ( $self, $into, $type, $name, $code ) = @_;
235             $modifier->(
236                 $into,
237                 $type,
238                 $name,
239                 $code
240             );
241         };
242     }
243
244     # call me. for first time.
245     $self->_install_modifier( $into, $type, $name, $code );
246 }
247
248 sub add_before_method_modifier {
249     my ( $self, $name, $code ) = @_;
250     $self->_install_modifier( $self->name, 'before', $name, $code );
251 }
252
253 sub add_around_method_modifier {
254     my ( $self, $name, $code ) = @_;
255     $self->_install_modifier( $self->name, 'around', $name, $code );
256 }
257
258 sub add_after_method_modifier {
259     my ( $self, $name, $code ) = @_;
260     $self->_install_modifier( $self->name, 'after', $name, $code );
261 }
262
263 sub add_override_method_modifier {
264     my ($self, $name, $code) = @_;
265
266     my $pkg = $self->name;
267     my $method = "${pkg}::${name}";
268
269     # Class::Method::Modifiers won't do this for us, so do it ourselves
270
271     my $body = $pkg->can($name)
272         or $self->throw_error("You cannot override '$method' because it has no super method");
273
274     no strict 'refs';
275     *$method = sub { $code->($pkg, $body, @_) };
276 }
277
278 sub does_role {
279     my ($self, $role_name) = @_;
280
281     (defined $role_name)
282         || $self->throw_error("You must supply a role name to look for");
283
284     for my $class ($self->linearized_isa) {
285         my $meta = Mouse::class_of($class);
286         next unless $meta && $meta->can('roles');
287
288         for my $role (@{ $meta->roles }) {
289             return 1 if $role->does_role($role_name);
290         }
291     }
292
293     return 0;
294 }
295
296 sub create {
297     my ($class, $package_name, %options) = @_;
298
299     (ref $options{superclasses} eq 'ARRAY')
300         || $class->throw_error("You must pass an ARRAY ref of superclasses")
301             if exists $options{superclasses};
302
303     (ref $options{attributes} eq 'ARRAY')
304         || $class->throw_error("You must pass an ARRAY ref of attributes")
305             if exists $options{attributes};
306
307     (ref $options{methods} eq 'HASH')
308         || $class->throw_error("You must pass a HASH ref of methods")
309             if exists $options{methods};
310
311     do {
312         ( defined $package_name && $package_name )
313           || $class->throw_error("You must pass a package name");
314
315         my $code = "package $package_name;";
316         $code .= "\$$package_name\:\:VERSION = '" . $options{version} . "';"
317           if exists $options{version};
318         $code .= "\$$package_name\:\:AUTHORITY = '" . $options{authority} . "';"
319           if exists $options{authority};
320
321         eval $code;
322         $class->throw_error("creation of $package_name failed : $@") if $@;
323     };
324
325     my %initialize_options = %options;
326     delete @initialize_options{qw(
327         package
328         superclasses
329         attributes
330         methods
331         version
332         authority
333     )};
334     my $meta = $class->initialize( $package_name => %initialize_options );
335
336     # FIXME totally lame
337     $meta->add_method('meta' => sub {
338         Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
339     });
340
341     $meta->superclasses(@{$options{superclasses}})
342         if exists $options{superclasses};
343     # NOTE:
344     # process attributes first, so that they can
345     # install accessors, but locally defined methods
346     # can then overwrite them. It is maybe a little odd, but
347     # I think this should be the order of things.
348     if (exists $options{attributes}) {
349         foreach my $attr (@{$options{attributes}}) {
350             Mouse::Meta::Attribute->create($meta, $attr->{name}, %$attr);
351         }
352     }
353     if (exists $options{methods}) {
354         foreach my $method_name (keys %{$options{methods}}) {
355             $meta->add_method($method_name, $options{methods}->{$method_name});
356         }
357     }
358     return $meta;
359 }
360
361 {
362     my $ANON_CLASS_SERIAL = 0;
363     my $ANON_CLASS_PREFIX = 'Mouse::Meta::Class::__ANON__::SERIAL::';
364     sub create_anon_class {
365         my ( $class, %options ) = @_;
366         my $package_name = $ANON_CLASS_PREFIX . ++$ANON_CLASS_SERIAL;
367         return $class->create( $package_name, %options );
368     }
369 }
370
371 1;
372
373 __END__
374
375 =head1 NAME
376
377 Mouse::Meta::Class - hook into the Mouse MOP
378
379 =head1 METHODS
380
381 =head2 initialize ClassName -> Mouse::Meta::Class
382
383 Finds or creates a Mouse::Meta::Class instance for the given ClassName. Only
384 one instance should exist for a given class.
385
386 =head2 new %args -> Mouse::Meta::Class
387
388 Creates a new Mouse::Meta::Class. Don't call this directly.
389
390 =head2 name -> ClassName
391
392 Returns the name of the owner class.
393
394 =head2 superclasses -> [ClassName]
395
396 Gets (or sets) the list of superclasses of the owner class.
397
398 =head2 add_attribute (Mouse::Meta::Attribute| name => spec)
399
400 Begins keeping track of the existing L<Mouse::Meta::Attribute> for the owner
401 class.
402
403 =head2 get_all_attributes -> (Mouse::Meta::Attribute)
404
405 Returns the list of all L<Mouse::Meta::Attribute> instances associated with
406 this class and its superclasses.
407
408 =head2 get_attribute_map -> { name => Mouse::Meta::Attribute }
409
410 Returns a mapping of attribute names to their corresponding
411 L<Mouse::Meta::Attribute> objects.
412
413 =head2 get_attribute_list -> { name => Mouse::Meta::Attribute }
414
415 This returns a list of attribute names which are defined in the local
416 class. If you want a list of all applicable attributes for a class,
417 use the C<get_all_attributes> method.
418
419 =head2 has_attribute Name -> Bool
420
421 Returns whether we have a L<Mouse::Meta::Attribute> with the given name.
422
423 =head2 get_attribute Name -> Mouse::Meta::Attribute | undef
424
425 Returns the L<Mouse::Meta::Attribute> with the given name.
426
427 =head2 linearized_isa -> [ClassNames]
428
429 Returns the list of classes in method dispatch order, with duplicates removed.
430
431 =head2 clone_object Instance -> Instance
432
433 Clones the given C<Instance> which must be an instance governed by this
434 metaclass.
435
436 =head2 clone_instance Instance, Parameters -> Instance
437
438 The clone_instance method has been made private.
439 The public version is deprecated.
440
441 =cut
442