Class::MOP - all the method methods and tests
[gitmo/Class-MOP.git] / lib / Class / MOP / Class.pm
CommitLineData
8b978dd5 1
2package Class::MOP::Class;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
0882828e 8use Scalar::Util 'blessed', 'reftype';
8b978dd5 9use Sub::Name 'subname';
10use B 'svref_2object';
11
12our $VERSION = '0.01';
13
14# Creation
15
bfe4d0fc 16{
17 # Metaclasses are singletons, so we cache them here.
18 # there is no need to worry about destruction though
19 # because they should die only when the program dies.
20 # After all, do package definitions even get reaped?
21 my %METAS;
22 sub initialize {
23 my ($class, $package_name) = @_;
24 (defined $package_name && $package_name)
25 || confess "You must pass a package name";
26 $METAS{$package_name} ||= bless \$package_name => blessed($class) || $class;
27 }
8b978dd5 28}
29
30sub create {
31 my ($class, $package_name, $package_version, %options) = @_;
bfe4d0fc 32 (defined $package_name && $package_name)
8b978dd5 33 || confess "You must pass a package name";
34 my $code = "package $package_name;";
35 $code .= "\$$package_name\:\:VERSION = '$package_version';"
36 if defined $package_version;
37 eval $code;
38 confess "creation of $package_name failed : $@" if $@;
bfe4d0fc 39 my $meta = $class->initialize($package_name);
8b978dd5 40 $meta->superclasses(@{$options{superclasses}})
41 if exists $options{superclasses};
bfe4d0fc 42 if (exists $options{methods}) {
43 foreach my $method_name (keys %{$options{methods}}) {
44 $meta->add_method($method_name, $options{methods}->{$method_name});
45 }
46 }
8b978dd5 47 return $meta;
48}
49
50# Informational
51
52sub name { ${$_[0]} }
53
54sub version {
55 my $self = shift;
56 no strict 'refs';
57 ${$self->name . '::VERSION'};
58}
59
60# Inheritance
61
62sub superclasses {
63 my $self = shift;
64 no strict 'refs';
65 if (@_) {
66 my @supers = @_;
67 @{$self->name . '::ISA'} = @supers;
68 }
69 @{$self->name . '::ISA'};
70}
71
72sub class_precedence_list {
73 my $self = shift;
bfe4d0fc 74 # NOTE:
75 # We need to check for ciruclar inheirtance here.
76 # This will do nothing if all is well, and blow
77 # up otherwise. Yes, it's an ugly hack, better
78 # suggestions are welcome.
79 { $self->name->isa('This is a test for circular inheritance') }
80 # ... and no back to our regularly scheduled program
8b978dd5 81 (
82 $self->name,
83 map {
bfe4d0fc 84 $self->initialize($_)->class_precedence_list()
8b978dd5 85 } $self->superclasses()
86 );
87}
88
0882828e 89## Methods
90
91sub add_method {
92 my ($self, $method_name, $method) = @_;
93 (defined $method_name && $method_name)
94 || confess "You must define a method name";
a5eca695 95 # use reftype here to allow for blessed subs ...
0882828e 96 (reftype($method) && reftype($method) eq 'CODE')
97 || confess "Your code block must be a CODE reference";
98 my $full_method_name = ($self->name . '::' . $method_name);
99
100 no strict 'refs';
c9b8b7f9 101 no warnings 'redefine';
0882828e 102 *{$full_method_name} = subname $full_method_name => $method;
103}
104
bfe4d0fc 105{
106
107 ## private utility functions for has_method
108 my $_find_subroutine_package_name = sub { eval { svref_2object($_[0])->GV->STASH->NAME } };
109 my $_find_subroutine_name = sub { eval { svref_2object($_[0])->GV->NAME } };
110
111 sub has_method {
c9b8b7f9 112 my ($self, $method_name) = @_;
bfe4d0fc 113 (defined $method_name && $method_name)
114 || confess "You must define a method name";
0882828e 115
bfe4d0fc 116 my $sub_name = ($self->name . '::' . $method_name);
0882828e 117
bfe4d0fc 118 no strict 'refs';
119 return 0 if !defined(&{$sub_name});
120 return 0 if $_find_subroutine_package_name->(\&{$sub_name}) ne $self->name &&
121 $_find_subroutine_name->(\&{$sub_name}) ne '__ANON__';
122 return 1;
123 }
124
0882828e 125}
126
127sub get_method {
c9b8b7f9 128 my ($self, $method_name) = @_;
0882828e 129 (defined $method_name && $method_name)
130 || confess "You must define a method name";
131
132 no strict 'refs';
133 return \&{$self->name . '::' . $method_name}
bfe4d0fc 134 if $self->has_method($method_name);
c9b8b7f9 135 return; # <- make sure to return undef
136}
137
138sub remove_method {
139 my ($self, $method_name) = @_;
140 (defined $method_name && $method_name)
141 || confess "You must define a method name";
142
143 my $removed_method = $self->get_method($method_name);
144
145 no strict 'refs';
146 delete ${$self->name . '::'}{$method_name}
147 if defined $removed_method;
148
149 return $removed_method;
150}
151
152sub get_method_list {
153 my $self = shift;
154 no strict 'refs';
a5eca695 155 grep { $self->has_method($_) } %{$self->name . '::'};
156}
157
158sub compute_all_applicable_methods {
159 my $self = shift;
160 my @methods;
161 # keep a record of what we have seen
162 # here, this will handle all the
163 # inheritence issues because we are
164 # using the &class_precedence_list
165 my (%seen_class, %seen_method);
166 foreach my $class ($self->class_precedence_list()) {
167 next if $seen_class{$class};
168 $seen_class{$class}++;
169 # fetch the meta-class ...
170 my $meta = $self->initialize($class);
171 foreach my $method_name ($meta->get_method_list()) {
172 next if exists $seen_method{$method_name};
173 $seen_method{$method_name}++;
174 push @methods => {
175 name => $method_name,
176 class => $class,
177 code => $meta->get_method($method_name)
178 };
179 }
180 }
181 return @methods;
182}
183
184## Recursive Version of compute_all_applicable_methods
185# sub compute_all_applicable_methods {
186# my ($self, $seen) = @_;
187# $seen ||= {};
188# (
189# (map {
190# if (exists $seen->{$_}) {
191# ();
192# }
193# else {
194# $seen->{$_}++;
195# {
196# name => $_,
197# class => $self->name,
198# code => $self->get_method($_)
199# };
200# }
201# } $self->get_method_list()),
202# map {
203# $self->initialize($_)->compute_all_applicable_methods($seen)
204# } $self->superclasses()
205# );
206# }
207
208sub find_all_methods_by_name {
209 my ($self, $method_name) = @_;
210 (defined $method_name && $method_name)
211 || confess "You must define a method name to find";
212 my @methods;
213 # keep a record of what we have seen
214 # here, this will handle all the
215 # inheritence issues because we are
216 # using the &class_precedence_list
217 my %seen_class;
218 foreach my $class ($self->class_precedence_list()) {
219 next if $seen_class{$class};
220 $seen_class{$class}++;
221 # fetch the meta-class ...
222 my $meta = $self->initialize($class);
223 push @methods => {
224 name => $method_name,
225 class => $class,
226 code => $meta->get_method($method_name)
227 } if $meta->has_method($method_name);
228 }
229 return @methods;
230
8b978dd5 231}
232
2331;
234
235__END__
236
237=pod
238
239=head1 NAME
240
241Class::MOP::Class - Class Meta Object
242
243=head1 SYNOPSIS
244
245=head1 DESCRIPTION
246
247=head1 AUTHOR
248
249Stevan Little E<gt>stevan@iinteractive.comE<lt>
250
251=head1 COPYRIGHT AND LICENSE
252
253Copyright 2006 by Infinity Interactive, Inc.
254
255L<http://www.iinteractive.com>
256
257This library is free software; you can redistribute it and/or modify
258it under the same terms as Perl itself.
259
260=cut