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