Update this module ready for relase, just need to write a few more tests.
[p5sagit/Class-C3-Componentised.git] / lib / Class / C3 / Componentised.pm
CommitLineData
d288ce53 1package Class::C3::Componentised;
2
20169807 3=head1 NAME
4
5Class::C3::Componentised
6
7=head1 DESCRIPTION
8
9Load mix-ins or components to your C3-based class.
10
11=head1 SYNOPSIS
12
13 package MyModule;
14
15 use strict;
16 use warnings;
17
18 use base 'Class::C3::Componentised';
19
20 sub component_base_class { "MyModule::Plugin" }
21
22 package main;
23
24 MyModule->load_components( $self->{plugins} );
25
26=head1 METHODS
27
28=cut
29
d288ce53 30use strict;
31use warnings;
32
d288ce53 33use Class::C3;
20169807 34use Class::Inspector;
35use Carp;
d288ce53 36
20169807 37our $VERSION = 1.0000;
d288ce53 38
20169807 39=head2 load_components( @comps )
d288ce53 40
20169807 41Loads the given components into the current module. If a module begins with a
42C<+> character, it is taken to be a fully qualified class name, otherwise
43C<< $class->component_base_class >> is prepended to it.
d288ce53 44
20169807 45Calling this will call C<Class::C3::reinitialize>.
46
47=cut
d288ce53 48
49sub load_components {
50 my $class = shift;
51 my $base = $class->component_base_class;
52 my @comp = map { /^\+(.*)$/ ? $1 : "${base}::$_" } grep { $_ !~ /^#/ } @_;
53 $class->_load_components(@comp);
d288ce53 54}
55
20169807 56=head2 load_own_components( @comps )
57
58Simialr to L<load_components>, but assumes every class is C<"$class::$comp">.
59
60=cut
61
d288ce53 62sub load_own_components {
63 my $class = shift;
64 my @comp = map { "${class}::$_" } grep { $_ !~ /^#/ } @_;
65 $class->_load_components(@comp);
66}
67
68sub _load_components {
69 my ($class, @comp) = @_;
70 foreach my $comp (@comp) {
20169807 71 $class->ensure_class_loaded($comp);
d288ce53 72 }
73 $class->inject_base($class => @comp);
20169807 74 Class::C3::reinitialize();
d288ce53 75}
76
20169807 77=head2 load_optional_components
d288ce53 78
20169807 79As L<load_components>, but will silently ignore any components that cannot be
80found.
d288ce53 81
20169807 82=cut
d288ce53 83
20169807 84sub load_optional_components {
85 my $class = shift;
86 my $base = $class->component_base_class;
87 my @comp = grep { $class->load_optional_class( $_ ) }
88 map { /^\+(.*)$/ ? $1 : "${base}::$_" }
89 grep { $_ !~ /^#/ } @_;
d288ce53 90
20169807 91 $class->_load_components( @comp ) if scalar @comp;
92}
d288ce53 93
20169807 94=head2 ensure_class_loaded
95
96Given a class name, tests to see if it is already loaded or otherwise
97defined. If it is not yet loaded, the package is require'd, and an exception
98is thrown if the class is still not loaded.
99
100 BUG: For some reason, packages with syntax errors are added to %INC on
101 require
102=cut
103
104#
105# TODO: handle ->has_many('rel', 'Class'...) instead of
106# ->has_many('rel', 'Some::Schema::Class'...)
107#
108sub ensure_class_loaded {
109 my ($class, $f_class) = @_;
110
111 croak "Invalid class name $f_class"
112 if ($f_class=~m/(?:\b:\b|\:{3,})/);
113 return if Class::Inspector->loaded($f_class);
114 eval "require $f_class"; # require needs a bareword or filename
115 if ($@) {
116 if ($class->can('throw_exception')) {
117 $class->throw_exception($@);
118 } else {
119 croak $@;
120 }
121 }
122}
d288ce53 123
20169807 124=head2 ensure_class_found
d288ce53 125
20169807 126Returns true if the specified class is installed or already loaded, false
127otherwise
d288ce53 128
20169807 129=cut
d288ce53 130
20169807 131sub ensure_class_found {
132 my ($class, $f_class) = @_;
133 return Class::Inspector->loaded($f_class) ||
134 Class::Inspector->installed($f_class);
135}
d288ce53 136
20169807 137# Returns a true value if the specified class is installed and loaded
138# successfully, throws an exception if the class is found but not loaded
139# successfully, and false if the class is not installed
140sub _load_optional_class {
141 my ($class, $f_class) = @_;
142 if ($class->ensure_class_found($f_class)) {
143 $class->ensure_class_loaded($f_class);
144 return 1;
145 } else {
146 return 0;
147 }
148}
d288ce53 149
150=head2 inject_base
151
20169807 152Does the actual magic of adjusting @ISA on the target module.
153
154=cut
d288ce53 155
20169807 156sub inject_base {
157 my ($class, $target, @to_inject) = @_;
158 {
159 no strict 'refs';
160 foreach my $to (reverse @to_inject) {
161 unshift( @{"${target}::ISA"}, $to )
162 unless ($target eq $to || $target->isa($to));
163 }
164 }
165
166 # Yes, this is hack. But it *does* work. Please don't submit tickets about
167 # it on the basis of the comments in Class::C3, the author was on #dbix-class
168 # while I was implementing this.
169
170 eval "package $target; import Class::C3;" unless exists $Class::C3::MRO{$target};
171}
d288ce53 172
173=head1 AUTHOR
174
20169807 175Matt S. Trout and the DBIx::Class team
176
177Pulled out into seperate module by Ash Berlin C<< <ash@cpan.org> >>
d288ce53 178
179=head1 LICENSE
180
181You may distribute this code under the same terms as Perl itself.
20169807 182
183=cut
184
1851;