NoSugarContainer doesn't need var
[catagits/Catalyst-Runtime.git] / lib / Catalyst / IOC / Container.pm
1 package Catalyst::IOC::Container;
2 use Bread::Board;
3 use Moose;
4 use Config::Any;
5 use Data::Visitor::Callback;
6 use Catalyst::Utils ();
7 use List::Util qw(first);
8 use Devel::InnerPackage ();
9 use Hash::Util qw/lock_hash/;
10 use MooseX::Types::LoadableClass qw/ LoadableClass /;
11 use Moose::Util;
12 use Catalyst::IOC::BlockInjection;
13 use Catalyst::IOC::ConstructorInjection;
14 use Module::Pluggable::Object ();
15 use namespace::autoclean;
16
17 extends 'Bread::Board::Container';
18
19 has config_local_suffix => (
20     is      => 'ro',
21     isa     => 'Str',
22     default => 'local',
23 );
24
25 has driver => (
26     is      => 'ro',
27     isa     => 'HashRef',
28     default => sub { +{} },
29 );
30
31 has file => (
32     is      => 'ro',
33     isa     => 'Str',
34     default => '',
35 );
36
37 has substitutions => (
38     is      => 'ro',
39     isa     => 'HashRef',
40     default => sub { +{} },
41 );
42
43 has application_name => (
44     is       => 'ro',
45     isa      => 'Str',
46     required => 1,
47 );
48
49 has sub_container_class => (
50     isa     => LoadableClass,
51     is      => 'ro',
52     coerce  => 1,
53     default => 'Catalyst::IOC::SubContainer',
54     handles => {
55         new_sub_container => 'new',
56     }
57 );
58
59 sub BUILD {
60     my ( $self, $params ) = @_;
61
62     $self->add_service(
63         $self->${\"build_${_}_service"}
64     ) for qw/
65         substitutions
66         file
67         driver
68         application_name
69         prefix
70         extensions
71         path
72         config
73         raw_config
74         global_files
75         local_files
76         global_config
77         local_config
78         class_config
79         config_local_suffix
80         config_path
81         locate_components
82     /;
83
84     my $config = $self->resolve( service => 'config' );
85
86     # don't force default_component to be undef if the config wasn't set
87     my @default_view  = $config->{default_view}
88                       ? ( default_component => $config->{default_view} )
89                       : ( )
90                       ;
91     my @default_model = $config->{default_model}
92                       ? ( default_component => $config->{default_model} )
93                       : ( )
94                       ;
95
96     $self->add_sub_container(
97         $self->build_component_subcontainer
98     );
99
100     $self->add_sub_container(
101         $self->build_controller_subcontainer
102     );
103
104     $self->add_sub_container(
105         $self->build_view_subcontainer( @default_view )
106     );
107
108     $self->add_sub_container(
109         $self->build_model_subcontainer( @default_model )
110     );
111
112     {
113         no strict 'refs';
114         my $class = ref $self;
115         warn("In build $class");
116         ${ $class . '::customise_container' }->($self)
117             if ${ $class . '::customise_container' };
118     }
119 }
120
121 sub build_model_subcontainer {
122     my $self = shift;
123
124     return $self->new_sub_container( @_,
125         name => 'model',
126     );
127 }
128
129 sub build_view_subcontainer {
130     my $self = shift;
131
132     return $self->new_sub_container( @_,
133         name => 'view',
134     );
135 }
136
137 sub build_controller_subcontainer {
138     my $self = shift;
139
140     return $self->new_sub_container(
141         name => 'controller',
142     );
143 }
144
145 sub build_component_subcontainer {
146     my $self = shift;
147
148     return Bread::Board::Container->new(
149         name => 'component',
150     );
151 }
152
153 sub build_application_name_service {
154     my $self = shift;
155
156     return Bread::Board::Literal->new( name => 'application_name', value => $self->application_name );
157 }
158
159 sub build_driver_service {
160     my $self = shift;
161
162     return Bread::Board::Literal->new( name => 'driver', value => $self->driver );
163 }
164
165 sub build_file_service {
166     my $self = shift;
167
168     return Bread::Board::Literal->new( name => 'file', value => $self->file );
169 }
170
171 sub build_substitutions_service {
172     my $self = shift;
173
174     return Bread::Board::Literal->new( name => 'substitutions', value => $self->substitutions );
175 }
176
177 sub build_extensions_service {
178     my $self = shift;
179
180     return Bread::Board::BlockInjection->new(
181         lifecycle => 'Singleton',
182         name => 'extensions',
183         block => sub {
184             return \@{Config::Any->extensions};
185         },
186     );
187 }
188
189 sub build_prefix_service {
190     my $self = shift;
191
192     return Bread::Board::BlockInjection->new(
193         lifecycle => 'Singleton',
194         name => 'prefix',
195         block => sub {
196             return Catalyst::Utils::appprefix( shift->param('application_name') );
197         },
198         dependencies => [ depends_on('application_name') ],
199     );
200 }
201
202 sub build_path_service {
203     my $self = shift;
204
205     return Bread::Board::BlockInjection->new(
206         lifecycle => 'Singleton',
207         name => 'path',
208         block => sub {
209             my $s = shift;
210
211             return Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG' )
212             || $s->param('file')
213             || $s->param('application_name')->path_to( $s->param('prefix') );
214         },
215         dependencies => [ depends_on('file'), depends_on('application_name'), depends_on('prefix') ],
216     );
217 }
218
219 sub build_config_service {
220     my $self = shift;
221
222     return Bread::Board::BlockInjection->new(
223         lifecycle => 'Singleton',
224         name => 'config',
225         block => sub {
226             my $s = shift;
227
228             my $v = Data::Visitor::Callback->new(
229                 plain_value => sub {
230                     return unless defined $_;
231                     return $self->_config_substitutions( $s->param('application_name'), $s->param('substitutions'), $_ );
232                 }
233
234             );
235             $v->visit( $s->param('raw_config') );
236         },
237         dependencies => [ depends_on('application_name'), depends_on('raw_config'), depends_on('substitutions') ],
238     );
239 }
240
241 sub build_raw_config_service {
242     my $self = shift;
243
244     return Bread::Board::BlockInjection->new(
245         lifecycle => 'Singleton',
246         name => 'raw_config',
247         block => sub {
248             my $s = shift;
249
250             my @global = @{$s->param('global_config')};
251             my @locals = @{$s->param('local_config')};
252
253             my $config = $s->param('class_config');
254
255             for my $cfg (@global, @locals) {
256                 for (keys %$cfg) {
257                     $config = Catalyst::Utils::merge_hashes( $config, $cfg->{$_} );
258                 }
259             }
260
261             return $config;
262         },
263         dependencies => [ depends_on('global_config'), depends_on('local_config'), depends_on('class_config') ],
264     );
265 }
266
267 sub build_global_files_service {
268     my $self = shift;
269
270     return Bread::Board::BlockInjection->new(
271         lifecycle => 'Singleton',
272         name => 'global_files',
273         block => sub {
274             my $s = shift;
275
276             my ( $path, $extension ) = @{$s->param('config_path')};
277
278             my @extensions = @{$s->param('extensions')};
279
280             my @files;
281             if ( $extension ) {
282                 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
283                 push @files, $path;
284             } else {
285                 @files = map { "$path.$_" } @extensions;
286             }
287             return \@files;
288         },
289         dependencies => [ depends_on('extensions'), depends_on('config_path') ],
290     );
291 }
292
293 sub build_local_files_service {
294     my $self = shift;
295
296     return Bread::Board::BlockInjection->new(
297         lifecycle => 'Singleton',
298         name => 'local_files',
299         block => sub {
300             my $s = shift;
301
302             my ( $path, $extension ) = @{$s->param('config_path')};
303             my $suffix = $s->param('config_local_suffix');
304
305             my @extensions = @{$s->param('extensions')};
306
307             my @files;
308             if ( $extension ) {
309                 die "Unable to handle files with the extension '${extension}'" unless grep { $_ eq $extension } @extensions;
310                 $path =~ s{\.$extension}{_$suffix.$extension};
311                 push @files, $path;
312             } else {
313                 @files = map { "${path}_${suffix}.$_" } @extensions;
314             }
315             return \@files;
316         },
317         dependencies => [ depends_on('extensions'), depends_on('config_path'), depends_on('config_local_suffix') ],
318     );
319 }
320
321 sub build_class_config_service {
322     my $self = shift;
323
324     return Bread::Board::BlockInjection->new(
325         lifecycle => 'Singleton',
326         name => 'class_config',
327         block => sub {
328             my $s   = shift;
329             my $app = $s->param('application_name');
330
331             # Container might be called outside Catalyst context
332             return {} unless Class::MOP::is_class_loaded($app);
333
334             # config might not have been defined
335             return $app->config || {};
336         },
337         dependencies => [ depends_on('application_name') ],
338     );
339 }
340
341 sub build_global_config_service {
342     my $self = shift;
343
344     return Bread::Board::BlockInjection->new(
345         lifecycle => 'Singleton',
346         name => 'global_config',
347         block => sub {
348             my $s = shift;
349
350             return Config::Any->load_files({
351                 files       => $s->param('global_files'),
352                 filter      => \&_fix_syntax,
353                 use_ext     => 1,
354                 driver_args => $s->param('driver'),
355             });
356         },
357         dependencies => [ depends_on('global_files') ],
358     );
359 }
360
361 sub build_local_config_service {
362     my $self = shift;
363
364     return Bread::Board::BlockInjection->new(
365         lifecycle => 'Singleton',
366         name => 'local_config',
367         block => sub {
368             my $s = shift;
369
370             return Config::Any->load_files({
371                 files       => $s->param('local_files'),
372                 filter      => \&_fix_syntax,
373                 use_ext     => 1,
374                 driver_args => $s->param('driver'),
375             });
376         },
377         dependencies => [ depends_on('local_files') ],
378     );
379 }
380
381 sub build_config_path_service {
382     my $self = shift;
383
384     return Bread::Board::BlockInjection->new(
385         lifecycle => 'Singleton',
386         name => 'config_path',
387         block => sub {
388             my $s = shift;
389
390             my $path = $s->param('path');
391             my $prefix = $s->param('prefix');
392
393             my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
394
395             if ( -d $path ) {
396                 $path =~ s{[\/\\]$}{};
397                 $path .= "/$prefix";
398             }
399
400             return [ $path, $extension ];
401         },
402         dependencies => [ depends_on('prefix'), depends_on('path') ],
403     );
404 }
405
406 sub build_config_local_suffix_service {
407     my $self = shift;
408
409     return Bread::Board::BlockInjection->new(
410         lifecycle => 'Singleton',
411         name => 'config_local_suffix',
412         block => sub {
413             my $s = shift;
414             my $suffix = Catalyst::Utils::env_value( $s->param('application_name'), 'CONFIG_LOCAL_SUFFIX' ) || $self->config_local_suffix;
415
416             return $suffix;
417         },
418         dependencies => [ depends_on('application_name') ],
419     );
420 }
421
422 sub build_locate_components_service {
423     my $self = shift;
424
425     return Bread::Board::BlockInjection->new(
426         lifecycle => 'Singleton',
427         name      => 'locate_components',
428         block     => sub {
429             my $s      = shift;
430             my $class  = $s->param('application_name');
431             my $config = $s->param('config')->{ setup_components };
432
433             Catalyst::Exception->throw(
434                 qq{You are using search_extra config option. That option is\n} .
435                 qq{deprecated, please refer to the documentation for\n} .
436                 qq{other ways of achieving the same results.\n}
437             ) if delete $config->{ search_extra };
438
439             my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
440
441             my $locator = Module::Pluggable::Object->new(
442                 search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
443                 %$config
444             );
445
446             return [ $locator->plugins ];
447         },
448         dependencies => [ depends_on('application_name'), depends_on('config') ],
449     );
450 }
451
452 sub setup_components {
453     my $self = shift;
454     warn("Setting up default components");
455     my $class = $self->resolve( service => 'application_name' );
456     my @comps = @{ $self->resolve( service => 'locate_components' ) };
457     my %comps = map { $_ => 1 } @comps;
458     my $deprecatedcatalyst_component_names = 0;
459
460     for my $component ( @comps ) {
461
462         # We pass ignore_loaded here so that overlay files for (e.g.)
463         # Model::DBI::Schema sub-classes are loaded - if it's in @comps
464         # we know M::P::O found a file on disk so this is safe
465
466         Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
467     }
468
469     for my $component (@comps) {
470         $self->add_component( $component );
471         # FIXME - $instance->expand_modules() is broken
472         my @expanded_components = $self->expand_component_module( $component );
473
474         if (
475             !$deprecatedcatalyst_component_names &&
476             ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
477             ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
478         ) {
479             # FIXME - should I be calling warn here?
480             # Maybe it's time to remove it, or become fatal
481             $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
482                 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
483             );
484         }
485
486         for my $component (@expanded_components) {
487             $self->add_component( $component )
488                 unless $comps{$component};
489         }
490     }
491 }
492
493 sub _fix_syntax {
494     my $config     = shift;
495     my @components = (
496         map +{
497             prefix => $_ eq 'Component' ? '' : $_ . '::',
498             values => delete $config->{ lc $_ } || delete $config->{ $_ }
499         },
500         grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
501             qw( Component Model M View V Controller C Plugin )
502     );
503
504     foreach my $comp ( @components ) {
505         my $prefix = $comp->{ prefix };
506         foreach my $element ( keys %{ $comp->{ values } } ) {
507             $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
508         }
509     }
510 }
511
512 sub _config_substitutions {
513     my ( $self, $name, $subs, $arg ) = @_;
514
515     $subs->{ HOME } ||= sub { shift->path_to( '' ); };
516     $subs->{ ENV } ||=
517         sub {
518             my ( $c, $v ) = @_;
519             if (! defined($ENV{$v})) {
520                 Catalyst::Exception->throw( message =>
521                     "Missing environment variable: $v" );
522                 return "";
523             } else {
524                 return $ENV{ $v };
525             }
526         };
527     $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
528     $subs->{ literal } ||= sub { return $_[ 1 ]; };
529     my $subsre = join( '|', keys %$subs );
530
531     $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
532     return $arg;
533 }
534
535 sub get_component_from_sub_container {
536     my ( $self, $sub_container_name, $name, $c, @args ) = @_;
537
538     my $sub_container = $self->get_sub_container( $sub_container_name );
539
540     if (!$name) {
541         my $default = $sub_container->default_component;
542
543         return $sub_container->get_component( $default, $c, @args )
544             if $default && $sub_container->has_service( $default );
545
546         # FIXME - should I be calling $c->log->warn here?
547         # this is never a controller, so this is safe
548         $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
549         $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
550         $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
551         $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
552
553         return;
554     }
555
556     return $sub_container->get_component_regexp( $name, $c, @args )
557         if ref $name;
558
559     return $sub_container->get_component( $name, $c, @args )
560         if $sub_container->has_service( $name );
561
562     $c->log->warn(
563         "Attempted to use $sub_container_name '$name', " .
564         "but it does not exist"
565     );
566
567     return;
568 }
569
570 sub find_component {
571     my ( $self, $component, @args ) = @_;
572     my ( $type, $name ) = _get_component_type_name($component);
573     my @result;
574
575     return $self->get_component_from_sub_container(
576         $type, $name, @args
577     ) if $type;
578
579     my $query = ref $component
580               ? $component
581               : qr{^$component$}
582               ;
583
584     for my $subcontainer_name (qw/model view controller/) {
585         my $subcontainer = $self->get_sub_container( $subcontainer_name );
586         my @components   = $subcontainer->get_service_list;
587         @result          = grep { m{$component} } @components;
588
589         return map { $subcontainer->get_component( $_, @args ) } @result
590             if @result;
591     }
592
593     # one last search for things like $c->comp(qr/::M::/)
594     @result = $self->_find_component_regexp(
595         $component, @args
596     ) if !@result and ref $component;
597
598     # it expects an empty list on failed searches
599     return @result;
600 }
601
602 sub _find_component_regexp {
603     my ( $self, $component, $ctx, @args ) = @_;
604     my @result;
605
606     my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
607
608     for (@components) {
609         my ($type, $name) = _get_component_type_name($_);
610
611         push @result, $self->get_component_from_sub_container(
612             $type, $name, $ctx, @args
613         ) if $type;
614     }
615
616     return @result;
617 }
618
619 sub get_all_components {
620     my ($self, $class) = @_;
621     my %components;
622
623     # FIXME - if we're getting from these containers, we need to either:
624     #   - pass 'ctx' and 'accept_context_args' OR
625     #   - make these params optional
626     foreach my $type (qw/model view controller /) {
627         my $container = $self->get_sub_container($type);
628
629         for my $component ($container->get_service_list) {
630             my $comp_service = $container->get_service($component);
631
632             $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
633         }
634     }
635
636     return lock_hash %components;
637 }
638
639 sub add_component {
640     my ( $self, $component ) = @_;
641     my ( $type, $name ) = _get_component_type_name($component);
642
643     return unless $type;
644
645     # The 'component' sub-container will create the object, and store it's
646     # instance, which, by default, will live throughout the application.
647     # The model/view/controller sub-containers only reference the instance
648     # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
649     # sub every time they are called, when it exists.
650     my $instance_container       = $self->get_sub_container('component');
651     my $accept_context_container = $self->get_sub_container($type);
652
653     # Custom containers might have added the service already
654     # We don't want to override that
655     return if $accept_context_container->has_service( $name );
656
657     my $component_service_name = "${type}_${name}";
658
659     $instance_container->add_service(
660         Catalyst::IOC::ConstructorInjection->new(
661             name      => $component_service_name,
662             catalyst_component_name => $component,
663             class     => $component,
664             lifecycle => 'Singleton',
665             dependencies => [
666                 depends_on( '/application_name' ),
667             ],
668         )
669     );
670
671     $accept_context_container->add_service(
672         Catalyst::IOC::BlockInjection->new(
673             name         => $name,
674             catalyst_component_name => $component,
675             dependencies => [
676                 depends_on( "/component/$component_service_name" ),
677             ],
678             block => sub { shift->param($component_service_name) },
679         )
680     );
681 }
682
683 # FIXME: should this sub exist?
684 # should it be moved to Catalyst::Utils,
685 # or replaced by something already existing there?
686 sub _get_component_type_name {
687     my ( $component ) = @_;
688     my $result;
689
690     while ( !$result and (my $index = index $component, '::') > 0 ) {
691         my $type   = lc substr $component, 0, $index;
692         $component = substr $component, $index + 2;
693         $result    = first { $type eq $_ or $type eq substr($_, 0, 1) }
694                          qw{ model view controller };
695     }
696
697     return ($result, $component);
698 }
699
700 sub expand_component_module {
701     my ( $class, $module ) = @_;
702     return Devel::InnerPackage::list_packages( $module );
703 }
704
705 1;
706
707 __END__
708
709 =pod
710
711 =head1 NAME
712
713 Catalyst::Container - IOC for Catalyst components
714
715 =head1 SYNOPSIS
716
717 =head1 DESCRIPTION
718
719 =head1 METHODS
720
721 =head1 Building Containers
722
723 =head2 build_component_subcontainer
724
725 Container that stores all components, i.e. all models, views and controllers
726 together. Each service is an instance of the actual component, and by default
727 it lives while the application is running. Retrieving components from this
728 subcontainer will instantiate the component, if it hasn't been instantiated
729 already, but will not execute ACCEPT_CONTEXT.
730
731 =head2 build_model_subcontainer
732
733 Container that stores references for all models that are inside the components
734 subcontainer. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
735
736 =head2 build_view_subcontainer
737
738 Same as L<build_model_subcontainer>, but for views.
739
740 =head2 build_controller_subcontainer
741
742 Same as L<build_model_subcontainer>, but for controllers.
743
744 =head1 Building Services
745
746 =head2 build_application_name_service
747
748 Name of the application (such as MyApp).
749
750 =head2 build_driver_service
751
752 Config options passed directly to the driver being used.
753
754 =head2 build_file_service
755
756 ?
757
758 =head2 build_substitutions_service
759
760 This method substitutes macros found with calls to a function. There are a
761 number of default macros:
762
763 =over
764
765 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
766
767 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
768
769 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
770
771 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
772 C<__DATA__> as a config value, for example)
773
774 =back
775
776 The parameter list is split on comma (C<,>). You can override this method to
777 do your own string munging, or you can define your own macros in
778 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
779 Example:
780
781     MyApp->config( 'Plugin::ConfigLoader' => {
782         substitutions => {
783             baz => sub { my $c = shift; qux( @_ ); },
784         },
785     });
786
787 The above will respond to C<__baz(x,y)__> in config strings.
788
789 =head2 build_extensions_service
790
791 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
792
793 =head2 build_prefix_service
794
795 The prefix, based on the application name, that will be used to lookup the
796 config files (which will be in the format $prefix.$extension). If the app is
797 MyApp::Foo, the prefix will be myapp_foo.
798
799 =head2 build_path_service
800
801 The path to the config file (or environment variable, if defined).
802
803 =head2 build_config_service
804
805 The resulting configuration for the application, after it has successfully
806 been loaded, and all substitutions have been made.
807
808 =head2 build_raw_config_service
809
810 The merge of local_config and global_config hashes, before substitutions.
811
812 =head2 build_global_files_service
813
814 Gets all files for config that don't have the local_suffix, such as myapp.conf.
815
816 =head2 build_local_files_service
817
818 Gets all files for config that have the local_suffix, such as myapp_local.conf.
819
820 =head2 build_global_config_service
821
822 Reads config from global_files.
823
824 =head2 build_local_config_service
825
826 Reads config from local_files.
827
828 =head2 build_class_config_service
829
830 Reads config set from the application's class attribute config,
831 i.e. MyApp->config( name => 'MyApp', ... )
832
833 =head2 build_config_path_service
834
835 Splits the path to the config file, and returns on array ref containing
836 the path to the config file minus the extension in the first position,
837 and the extension in the second.
838
839 =head2 build_config_local_suffix_service
840
841 Determines the suffix of files used to override the main config. By default
842 this value is C<local>, which will load C<myapp_local.conf>.  The suffix can
843 be specified in the following order of preference:
844
845 =over
846
847 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
848
849 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
850
851 =back
852
853 The first one of these values found replaces the default of C<local> in the
854 name of the local config file to be loaded.
855
856 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
857 ConfigLoader will try and load C<myapp_testing.conf> instead of
858 C<myapp_local.conf>.
859
860 =head2 build_locate_components_service
861
862 This method is meant to provide a list of component modules that should be
863 setup for the application.  By default, it will use L<Module::Pluggable>.
864
865 Specify a C<setup_components> config option to pass additional options directly
866 to L<Module::Pluggable>.
867
868 =head1 Other methods
869
870 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
871
872 Looks for components in a given subcontainer (such as controller, model or
873 view), and returns the searched component. If $name is undef, it returns the
874 default component (such as default_view, if $sub_container is 'view'). If
875 $name is a regexp, it returns an array of matching components. Otherwise, it
876 looks for the component with name $name.
877
878 =head2 get_all_components
879
880 Fetches all the components, in each of the sub_containers model, view and
881 controller, and returns a readonly hash. The keys are the class names, and
882 the values are the blessed objects. This is what is returned by $c->components.
883
884 =head2 add_component
885
886 Adds a component to the appropriate subcontainer. The subcontainer is guessed
887 by the component name given.
888
889 =head2 find_component
890
891 Searches for components in all containers. If $component is the full class
892 name, the subcontainer is guessed, and it gets the searched component in there.
893 Otherwise, it looks for a component with that name in all subcontainers. If
894 $component is a regexp it calls _find_component_regexp and matches all
895 components against that regexp.
896
897 =head2 expand_component_module
898
899 Components found by C<locate_components> will be passed to this method, which
900 is expected to return a list of component (package) names to be set up.
901
902 =head2 setup_components
903
904 =head1 AUTHORS
905
906 Catalyst Contributors, see Catalyst.pm
907
908 =head1 COPYRIGHT
909
910 This library is free software. You can redistribute it and/or modify it under
911 the same terms as Perl itself.
912
913 =cut