Add various things
[gitmo/Mouse.git] / lib / Mouse / Meta / Class.pm
CommitLineData
306290e8 1package Mouse::Meta::Class;
c3398f5b 2use strict;
3use warnings;
4
fc1d8369 5use Mouse::Meta::Method::Constructor;
bbf64e76 6use Mouse::Meta::Method::Destructor;
cecfb973 7use Scalar::Util qw/blessed weaken/;
fce211ae 8use Mouse::Util qw/get_linear_isa not_supported/;
72b88a88 9
3a63a2e7 10use base qw(Mouse::Meta::Module);
11
6cfa1e5e 12sub method_metaclass(){ 'Mouse::Meta::Method' } # required for get_method()
3a63a2e7 13
8536d351 14sub _new {
88ed7189 15 my($class, %args) = @_;
c3398f5b 16
8536d351 17 $args{attributes} ||= {};
18 $args{methods} ||= {};
19 $args{roles} ||= [];
20
c3398f5b 21 $args{superclasses} = do {
22 no strict 'refs';
88ed7189 23 \@{ $args{package} . '::ISA' };
c3398f5b 24 };
25
7a50b450 26 #return Mouse::Meta::Class->initialize($class)->new_object(%args)
27 # if $class ne __PACKAGE__;
28
29 return bless \%args, $class;
30}
31
32sub create_anon_class{
33 my $self = shift;
34 return $self->create(undef, @_);
35}
36
37sub is_anon_class{
38 return exists $_[0]->{anon_serial_id};
c3398f5b 39}
40
afc73948 41sub roles { $_[0]->{roles} }
c3398f5b 42
43sub superclasses {
44 my $self = shift;
45
46 if (@_) {
47 Mouse::load_class($_) for @_;
48 @{ $self->{superclasses} } = @_;
49 }
50
51 @{ $self->{superclasses} };
52}
53
60cfc6ad 54sub get_all_method_names {
55 my $self = shift;
56 my %uniq;
57 return grep { $uniq{$_}++ == 0 }
3a63a2e7 58 map { Mouse::Meta::Class->initialize($_)->get_method_list() }
60cfc6ad 59 $self->linearized_isa;
60}
61
c3398f5b 62sub add_attribute {
63 my $self = shift;
c3398f5b 64
60f6eba9 65 if (@_ == 1 && blessed($_[0])) {
66 my $attr = shift @_;
67 $self->{'attributes'}{$attr->name} = $attr;
7a50b450 68 }
69 else {
60f6eba9 70 my $names = shift @_;
71 $names = [$names] if !ref($names);
72 my $metaclass = 'Mouse::Meta::Attribute';
7a50b450 73 my %options = (@_ == 1 ? %{$_[0]} : @_);
60f6eba9 74
75 if ( my $metaclass_name = delete $options{metaclass} ) {
76 my $new_class = Mouse::Util::resolve_metaclass_alias(
77 'Attribute',
78 $metaclass_name
79 );
80 if ( $metaclass ne $new_class ) {
81 $metaclass = $new_class;
82 }
83 }
84
85 for my $name (@$names) {
86 if ($name =~ s/^\+//) {
7a50b450 87 $metaclass->clone_parent($self, $name, %options);
60f6eba9 88 }
89 else {
7a50b450 90 $metaclass->create($self, $name, %options);
60f6eba9 91 }
92 }
93 }
c3398f5b 94}
95
d60824af 96sub compute_all_applicable_attributes { shift->get_all_attributes(@_) }
97sub get_all_attributes {
72b88a88 98 my $self = shift;
99 my (@attr, %seen);
100
101 for my $class ($self->linearized_isa) {
102 my $meta = $self->_metaclass_cache($class)
103 or next;
104
105 for my $name (keys %{ $meta->get_attribute_map }) {
106 next if $seen{$name}++;
107 push @attr, $meta->get_attribute($name);
108 }
109 }
110
111 return @attr;
112}
113
8536d351 114sub linearized_isa { @{ get_linear_isa($_[0]->name) } }
115
116sub new_object {
c68b4110 117 my $self = shift;
7a50b450 118 my %args = (@_ == 1 ? %{$_[0]} : @_);
c3398f5b 119
fce211ae 120 my $instance = bless {}, $self->name;
121
122 foreach my $attribute ($self->get_all_attributes) {
8536d351 123 my $from = $attribute->init_arg;
124 my $key = $attribute->name;
125
7a50b450 126 if (defined($from) && exists($args{$from})) {
127 $args{$from} = $attribute->coerce_constraint($args{$from})
8536d351 128 if $attribute->should_coerce;
7a50b450 129 $attribute->verify_against_type_constraint($args{$from});
8536d351 130
7a50b450 131 $instance->{$key} = $args{$from};
8536d351 132
133 weaken($instance->{$key})
fce211ae 134 if ref($instance->{$key}) && $attribute->is_weak_ref;
8536d351 135
136 if ($attribute->has_trigger) {
7a50b450 137 $attribute->trigger->($instance, $args{$from});
8536d351 138 }
139 }
140 else {
141 if ($attribute->has_default || $attribute->has_builder) {
142 unless ($attribute->is_lazy) {
143 my $default = $attribute->default;
144 my $builder = $attribute->builder;
145 my $value = $attribute->has_builder
146 ? $instance->$builder
147 : ref($default) eq 'CODE'
148 ? $default->($instance)
149 : $default;
150
151 $value = $attribute->coerce_constraint($value)
152 if $attribute->should_coerce;
153 $attribute->verify_against_type_constraint($value);
154
155 $instance->{$key} = $value;
156
157 weaken($instance->{$key})
fce211ae 158 if ref($instance->{$key}) && $attribute->is_weak_ref;
8536d351 159 }
160 }
161 else {
162 if ($attribute->is_required) {
fce211ae 163 $self->throw_error("Attribute (".$attribute->name.") is required");
8536d351 164 }
165 }
166 }
167 }
168 return $instance;
169}
c3398f5b 170
7a59f4e8 171sub clone_object {
172 my $class = shift;
173 my $instance = shift;
174
175 (blessed($instance) && $instance->isa($class->name))
fce211ae 176 || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($instance)");
7a59f4e8 177
178 $class->clone_instance($instance, @_);
179}
180
181sub clone_instance {
182 my ($class, $instance, %params) = @_;
183
184 (blessed($instance))
fce211ae 185 || $class->throw_error("You can only clone instances, ($instance) is not a blessed instance");
7a59f4e8 186
187 my $clone = bless { %$instance }, ref $instance;
188
d60824af 189 foreach my $attr ($class->get_all_attributes()) {
7a59f4e8 190 if ( defined( my $init_arg = $attr->init_arg ) ) {
191 if (exists $params{$init_arg}) {
192 $clone->{ $attr->name } = $params{$init_arg};
193 }
194 }
195 }
196
197 return $clone;
198
199}
200
fc1d8369 201sub make_immutable {
202 my $self = shift;
6a1d1835 203 my %args = (
204 inline_constructor => 1,
e578d610 205 inline_destructor => 1,
6a1d1835 206 @_,
207 );
208
fc1d8369 209 $self->{is_immutable}++;
c7a6403f 210
6a1d1835 211 if ($args{inline_constructor}) {
c7a6403f 212 $self->add_method('new' => Mouse::Meta::Method::Constructor->generate_constructor_method_inline( $self ));
213 }
214
8632b6fe 215 if ($args{inline_destructor}) {
216 $self->add_method('DESTROY' => Mouse::Meta::Method::Destructor->generate_destructor_method_inline( $self ));
217 }
2276cb14 218
219 # Moose's make_immutable returns true allowing calling code to skip setting an explicit true value
220 # at the end of a source file.
221 return 1;
fc1d8369 222}
ad958001 223
fce211ae 224sub make_mutable { not_supported }
ad958001 225
6cfa1e5e 226sub is_immutable { $_[0]->{is_immutable} }
227sub is_mutable { !$_[0]->{is_immutable} }
84ef660f 228
4859d490 229sub _install_modifier {
230 my ( $self, $into, $type, $name, $code ) = @_;
4f5b44a0 231
232 # which is modifer class available?
233 my $modifier_class = do {
234 if (eval "require Class::Method::Modifiers::Fast; 1") {
235 'Class::Method::Modifiers::Fast';
236 } elsif (eval "require Class::Method::Modifiers; 1") {
237 'Class::Method::Modifiers';
238 } else {
239 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.");
240 }
241 };
242 my $modifier = $modifier_class->can('_install_modifier');
243
244 # replace this method itself :)
245 {
4f5b44a0 246 no warnings 'redefine';
4f9945f5 247 *_install_modifier = sub {
4f5b44a0 248 my ( $self, $into, $type, $name, $code ) = @_;
249 $modifier->(
250 $into,
251 $type,
252 $name,
253 $code
254 );
6cfa1e5e 255 $self->{methods}{$name}++; # register it to the method map
256 return;
4f5b44a0 257 };
1b79a118 258 }
4f5b44a0 259
260 # call me. for first time.
261 $self->_install_modifier( $into, $type, $name, $code );
4859d490 262}
263
50dc6ee5 264sub add_before_method_modifier {
4859d490 265 my ( $self, $name, $code ) = @_;
266 $self->_install_modifier( $self->name, 'before', $name, $code );
50dc6ee5 267}
268
269sub add_around_method_modifier {
4859d490 270 my ( $self, $name, $code ) = @_;
271 $self->_install_modifier( $self->name, 'around', $name, $code );
50dc6ee5 272}
273
274sub add_after_method_modifier {
4859d490 275 my ( $self, $name, $code ) = @_;
276 $self->_install_modifier( $self->name, 'after', $name, $code );
50dc6ee5 277}
278
67199842 279sub add_override_method_modifier {
280 my ($self, $name, $code) = @_;
281
6cfa1e5e 282 my $package = $self->name;
67199842 283
6cfa1e5e 284 my $body = $package->can($name)
285 or $self->throw_error("You cannot override '$name' because it has no super method");
67199842 286
6cfa1e5e 287 $self->add_method($name => sub { $code->($package, $body, @_) });
67199842 288}
289
47f36c05 290sub does_role {
291 my ($self, $role_name) = @_;
ad958001 292
47f36c05 293 (defined $role_name)
fce211ae 294 || $self->throw_error("You must supply a role name to look for");
ad958001 295
f7fec86c 296 for my $class ($self->linearized_isa) {
08f7a8db 297 my $meta = Mouse::Meta::Module::class_of($class);
3a63a2e7 298 next unless $meta && $meta->can('roles');
299
300 for my $role (@{ $meta->roles }) {
ff687069 301
3a63a2e7 302 return 1 if $role->does_role($role_name);
f7fec86c 303 }
47f36c05 304 }
ad958001 305
47f36c05 306 return 0;
307}
308
c3398f5b 3091;
310
311__END__
312
313=head1 NAME
314
306290e8 315Mouse::Meta::Class - hook into the Mouse MOP
c3398f5b 316
317=head1 METHODS
318
306290e8 319=head2 initialize ClassName -> Mouse::Meta::Class
c3398f5b 320
306290e8 321Finds or creates a Mouse::Meta::Class instance for the given ClassName. Only
322one instance should exist for a given class.
c3398f5b 323
306290e8 324=head2 new %args -> Mouse::Meta::Class
c3398f5b 325
306290e8 326Creates a new Mouse::Meta::Class. Don't call this directly.
c3398f5b 327
328=head2 name -> ClassName
329
330Returns the name of the owner class.
331
332=head2 superclasses -> [ClassName]
333
334Gets (or sets) the list of superclasses of the owner class.
335
60f6eba9 336=head2 add_attribute (Mouse::Meta::Attribute| name => spec)
c3398f5b 337
306290e8 338Begins keeping track of the existing L<Mouse::Meta::Attribute> for the owner
339class.
c3398f5b 340
d60824af 341=head2 get_all_attributes -> (Mouse::Meta::Attribute)
72b88a88 342
343Returns the list of all L<Mouse::Meta::Attribute> instances associated with
344this class and its superclasses.
345
306290e8 346=head2 get_attribute_map -> { name => Mouse::Meta::Attribute }
c3398f5b 347
348Returns a mapping of attribute names to their corresponding
306290e8 349L<Mouse::Meta::Attribute> objects.
c3398f5b 350
c68b4110 351=head2 get_attribute_list -> { name => Mouse::Meta::Attribute }
352
353This returns a list of attribute names which are defined in the local
354class. If you want a list of all applicable attributes for a class,
d60824af 355use the C<get_all_attributes> method.
c68b4110 356
cbc437f2 357=head2 has_attribute Name -> Bool
66eea168 358
359Returns whether we have a L<Mouse::Meta::Attribute> with the given name.
360
306290e8 361=head2 get_attribute Name -> Mouse::Meta::Attribute | undef
c3398f5b 362
306290e8 363Returns the L<Mouse::Meta::Attribute> with the given name.
c3398f5b 364
365=head2 linearized_isa -> [ClassNames]
366
367Returns the list of classes in method dispatch order, with duplicates removed.
368
f7b11a21 369=head2 clone_object Instance -> Instance
370
371Clones the given C<Instance> which must be an instance governed by this
372metaclass.
373
374=head2 clone_instance Instance, Parameters -> Instance
375
518e303a 376The clone_instance method has been made private.
377The public version is deprecated.
f7b11a21 378
c3398f5b 379=cut
380