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