linear_isa code, basic tests
[gitmo/MRO-Compat.git] / lib / MRO / Compat.pm
CommitLineData
399a1dc7 1package MRO::Compat;
4c4e4170 2use strict;
3use warnings;
4
4c4e4170 5our $VERSION = '0.01';
6
399a1dc7 7# Is Class::C3 installed locally?
4c4e4170 8our $C3_INSTALLED;
399a1dc7 9
4c4e4170 10BEGIN {
399a1dc7 11 # Don't do anything if 5.9.5+
12 if($] < 5.009_005) {
13 # Find out if we have Class::C3 at all
14 eval { require Class::C3 };
15 $C3_INSTALLED = 1 if !$@;
16
17 # Alias our private functions over to
18 # the mro:: namespace
19 *mro::import = \&__import;
20 *mro::get_linear_isa = \&__get_linear_isa;
21 *mro::set_mro = \&__set_mro;
22 *mro::get_mro = \&__get_mro;
23 *mro::get_isarev = \&__get_isarev;
24 *mro::is_universal = \&__is_universal;
25 *mro::method_changed_in = \&__method_changed_in;
26 *mro::invalidate_all_method_caches
27 = \&__invalidate_all_method_caches;
4c4e4170 28 }
29}
30
4c4e4170 31=head1 NAME
32
399a1dc7 33MRO::Compat - Partial mro::* interface compatibility for Perls < 5.9.5
4c4e4170 34
35=head1 SYNOPSIS
36
37 package FooClass; use base qw/X Y Z/;
38 package X; use base qw/ZZZ/;
39 package Y; use base qw/ZZZ/;
40 package Z; use base qw/ZZZ/;
41
42 package main;
399a1dc7 43 use MRO::Compat;
4c4e4170 44 my $linear = mro::get_linear_isa('FooClass');
399a1dc7 45 print join(q{, }, @$linear);
4c4e4170 46
47 # Prints: "FooClass, X, ZZZ, Y, Z"
48
49=head1 DESCRIPTION
50
51The "mro" namespace provides several utilities for dealing
399a1dc7 52with method resolution order and method caching in general
53in Perl 5.9.5 and higher.
4c4e4170 54
399a1dc7 55This module provides a subset of those interfaces for
56earlier versions of Perl. It is a harmless no-op to use
57it on 5.9.5+. If you're writing a piece of software
58that would like to use the parts of 5.9.5+'s mro::
59interfaces that are supported here, and you want
60compatibility with older Perls, this is the module
61for you.
4c4e4170 62
399a1dc7 63This module never exports any functions. All calls must
64be fully qualified with the C<mro::> prefix.
4c4e4170 65
66=head1 Functions
67
68=head2 mro::get_linear_isa($classname[, $type])
69
70Returns an arrayref which is the linearized MRO of the given class.
71Uses whichever MRO is currently in effect for that class by default,
72or the given MRO (either C<c3> or C<dfs> if specified as C<$type>).
73
74The linearized MRO of a class is a single ordered list of all of the
75classes that would be visited in the process of resolving a method
76on the given class, starting with itself. It does not include any
77duplicate entries.
78
399a1dc7 79On pre-5.9.5 Perls with MRO::Compat, explicitly asking for the C<c3>
80MRO of a class will die if L<Class::C3> is not installed. If
81L<Class::C3> is installed, it will detect C3 classes and return the
82correct C3 MRO unless explicitly asked to return the C<dfs> MRO.
4c4e4170 83
84Note that C<UNIVERSAL> (and any members of C<UNIVERSAL>'s MRO) are not
85part of the MRO of a class, even though all classes implicitly inherit
86methods from C<UNIVERSAL> and its parents.
87
399a1dc7 88=cut
89
ed71cabb 90sub __get_linear_isa_dfs {
91 no strict 'refs';
92
93 my $classname = shift;
94
95 my @lin = ($classname);
96 my %stored;
97 foreach my $parent (@{"$classname\::ISA"}) {
98 my $plin = __get_linear_isa_dfs($parent);
99 foreach (@$plin) {
100 next if exists $stored{$_};
101 push(@lin, $_);
102 $stored{$_} = 1;
103 }
104 }
105 return \@lin;
106}
107
399a1dc7 108sub __get_linear_isa {
ed71cabb 109 my ($classname, $type) = @_;
110 die "mro::get_mro requires a classname" if !$classname;
111
112 $type ||= __get_mro($classname);
113 if($type eq 'dfs') {
114 return __get_linear_isa_dfs($classname);
115 }
116 elsif($type eq 'c3') {
117 return [Class::C3::calculateMRO($classname)];
118 }
119 die "type argument must be 'dfs' or 'c3'";
399a1dc7 120}
121
122=head2 mro::import
123
124Not supported, and hence 5.9.5's "use mro 'foo'" is also not supported.
125These will die if used on pre-5.9.5 Perls.
126
127=cut
128
129sub __import {
130 die q{The "use mro 'foo'" is only supported on Perl 5.9.5+};
131}
132
4c4e4170 133=head2 mro::set_mro($classname, $type)
134
399a1dc7 135Not supported, will die if used on pre-5.9.5 Perls.
136
137=cut
138
139sub __set_mro {
140 die q{mro::set_mro() is only supported on Perl 5.9.5+};
141}
4c4e4170 142
143=head2 mro::get_mro($classname)
144
145Returns the MRO of the given class (either C<c3> or C<dfs>).
146
399a1dc7 147=cut
148
149sub __get_mro {
ed71cabb 150 my $classname = shift;
399a1dc7 151 die "mro::get_mro requires a classname" if !$classname;
ed71cabb 152 if($C3_INSTALLED && exists $Class::C3::MRO{$classname}) {
399a1dc7 153 return 'c3';
154 }
155 return 'dfs';
156}
157
4c4e4170 158=head2 mro::get_isarev($classname)
159
399a1dc7 160Not supported, will die if used on pre-5.9.5 Perls.
161
162=cut
163
164sub __get_isarev {
165 die "mro::get_isarev() is only supported on Perl 5.9.5+";
166}
4c4e4170 167
168=head2 mro::is_universal($classname)
169
170Returns a boolean status indicating whether or not
171the given classname is either C<UNIVERSAL> itself,
172or one of C<UNIVERSAL>'s parents by C<@ISA> inheritance.
173
174Any class for which this function returns true is
175"universal" in the sense that all classes potentially
176inherit methods from it.
177
399a1dc7 178=cut
179
180sub __is_universal {
181 my $classname = shift;
182 die "mro::is_universal requires a classname" if !$classname;
183
184 my $lin = __get_linear_isa($classname);
185 foreach (@$lin) {
186 return 1 if $classname eq $_;
187 }
188
189 return 0;
190}
191
192=head2 mro::invalidate_all_method_caches
4c4e4170 193
194Increments C<PL_sub_generation>, which invalidates method
195caching in all packages.
196
399a1dc7 197=cut
198
199sub __invalidate_all_method_caches {
200 # Super secret mystery code :)
201 @fedcba98::ISA = @fedcba98::ISA;
202 return;
203}
204
4c4e4170 205=head2 mro::method_changed_in($classname)
206
207Invalidates the method cache of any classes dependent on the
399a1dc7 208given class. In L<MRO::Compat> on pre-5.9.5 Perls, this is
209an alias for C<mro::invalidate_all_method_caches> above, as
210pre-5.9.5 Perls have no other way to do this. It will still
211enforce the requirement that you pass it a classname, for
212compatibility.
213
214=cut
215
216sub __method_changed_in {
217 my $classname = shift;
218 die "mro::method_changed_in requires a classname" if !$classname;
219
220 __invalidate_all_method_caches();
221}
4c4e4170 222
223=head1 SEE ALSO
224
225L<Class::C3>
226
399a1dc7 227L<mro>
228
4c4e4170 229=head1 AUTHOR
230
231Brandon L. Black, E<lt>blblack@gmail.comE<gt>
232
233=head1 COPYRIGHT AND LICENSE
234
235Copyright 2007 Brandon L. Black E<lt>blblack@gmail.comE<gt>
236
237This library is free software; you can redistribute it and/or modify
238it under the same terms as Perl itself.
239
240=cut
241
2421;