overridden locate_components
[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     foreach my $type (qw/model view controller /) {
643         my $container = $self->get_sub_container($type);
644
645         for my $component ($container->get_service_list) {
646             my $comp_service = $container->get_service($component);
647
648             $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
649         }
650     }
651
652     return lock_hash %components;
653 }
654
655 sub add_component {
656     my ( $self, $component ) = @_;
657     my ( $type, $name ) = _get_component_type_name($component);
658
659     return unless $type;
660
661     # The 'component' sub-container will create the object, and store it's
662     # instance, which, by default, will live throughout the application.
663     # The model/view/controller sub-containers only reference the instance
664     # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
665     # sub every time they are called, when it exists.
666     my $instance_container       = $self->get_sub_container('component');
667     my $accept_context_container = $self->get_sub_container($type);
668
669     # Custom containers might have added the service already
670     # We don't want to override that
671     return if $accept_context_container->has_service( $name );
672
673     my $component_service_name = "${type}_${name}";
674
675     $instance_container->add_service(
676         Catalyst::IOC::ConstructorInjection->new(
677             name      => $component_service_name,
678             catalyst_component_name => $component,
679             class     => $component,
680             lifecycle => 'Singleton',
681             dependencies => [
682                 depends_on( '/application_name' ),
683             ],
684         )
685     );
686
687     $accept_context_container->add_service(
688         Catalyst::IOC::BlockInjection->new(
689             name         => $name,
690             catalyst_component_name => $component,
691             dependencies => [
692                 depends_on( "/component/$component_service_name" ),
693             ],
694             block => sub { shift->param($component_service_name) },
695         )
696     );
697 }
698
699 # FIXME: should this sub exist?
700 # should it be moved to Catalyst::Utils,
701 # or replaced by something already existing there?
702 sub _get_component_type_name {
703     my ( $component ) = @_;
704     my $result;
705
706     while ( !$result and (my $index = index $component, '::') > 0 ) {
707         my $type   = lc substr $component, 0, $index;
708         $component = substr $component, $index + 2;
709         $result    = first { $type eq $_ or $type eq substr($_, 0, 1) }
710                          qw{ model view controller };
711     }
712
713     return ($result, $component);
714 }
715
716 sub expand_component_module {
717     my ( $class, $module ) = @_;
718     return Devel::InnerPackage::list_packages( $module );
719 }
720
721 1;
722
723 __END__
724
725 =pod
726
727 =head1 NAME
728
729 Catalyst::Container - IOC for Catalyst components
730
731 =head1 SYNOPSIS
732
733 =head1 DESCRIPTION
734
735 =head1 METHODS
736
737 =head1 Building Containers
738
739 =head2 build_component_subcontainer
740
741 Container that stores all components, i.e. all models, views and controllers
742 together. Each service is an instance of the actual component, and by default
743 it lives while the application is running. Retrieving components from this
744 subcontainer will instantiate the component, if it hasn't been instantiated
745 already, but will not execute ACCEPT_CONTEXT.
746
747 =head2 build_model_subcontainer
748
749 Container that stores references for all models that are inside the components
750 subcontainer. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
751
752 =head2 build_view_subcontainer
753
754 Same as L<build_model_subcontainer>, but for views.
755
756 =head2 build_controller_subcontainer
757
758 Same as L<build_model_subcontainer>, but for controllers.
759
760 =head1 Building Services
761
762 =head2 build_application_name_service
763
764 Name of the application (such as MyApp).
765
766 =head2 build_driver_service
767
768 Config options passed directly to the driver being used.
769
770 =head2 build_file_service
771
772 ?
773
774 =head2 build_substitutions_service
775
776 This method substitutes macros found with calls to a function. There are a
777 number of default macros:
778
779 =over
780
781 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
782
783 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
784
785 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
786
787 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
788 C<__DATA__> as a config value, for example)
789
790 =back
791
792 The parameter list is split on comma (C<,>). You can override this method to
793 do your own string munging, or you can define your own macros in
794 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
795 Example:
796
797     MyApp->config( 'Plugin::ConfigLoader' => {
798         substitutions => {
799             baz => sub { my $c = shift; qux( @_ ); },
800         },
801     });
802
803 The above will respond to C<__baz(x,y)__> in config strings.
804
805 =head2 build_extensions_service
806
807 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
808
809 =head2 build_prefix_service
810
811 The prefix, based on the application name, that will be used to lookup the
812 config files (which will be in the format $prefix.$extension). If the app is
813 MyApp::Foo, the prefix will be myapp_foo.
814
815 =head2 build_path_service
816
817 The path to the config file (or environment variable, if defined).
818
819 =head2 build_config_service
820
821 The resulting configuration for the application, after it has successfully
822 been loaded, and all substitutions have been made.
823
824 =head2 build_raw_config_service
825
826 The merge of local_config and global_config hashes, before substitutions.
827
828 =head2 build_global_files_service
829
830 Gets all files for config that don't have the local_suffix, such as myapp.conf.
831
832 =head2 build_local_files_service
833
834 Gets all files for config that have the local_suffix, such as myapp_local.conf.
835
836 =head2 build_global_config_service
837
838 Reads config from global_files.
839
840 =head2 build_local_config_service
841
842 Reads config from local_files.
843
844 =head2 build_class_config_service
845
846 Reads config set from the application's class attribute config,
847 i.e. MyApp->config( name => 'MyApp', ... )
848
849 =head2 build_config_path_service
850
851 Splits the path to the config file, and returns on array ref containing
852 the path to the config file minus the extension in the first position,
853 and the extension in the second.
854
855 =head2 build_config_local_suffix_service
856
857 Determines the suffix of files used to override the main config. By default
858 this value is C<local>, which will load C<myapp_local.conf>.  The suffix can
859 be specified in the following order of preference:
860
861 =over
862
863 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
864
865 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
866
867 =back
868
869 The first one of these values found replaces the default of C<local> in the
870 name of the local config file to be loaded.
871
872 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
873 ConfigLoader will try and load C<myapp_testing.conf> instead of
874 C<myapp_local.conf>.
875
876 =head2 build_locate_components_service
877
878 This method is meant to provide a list of component modules that should be
879 setup for the application.  By default, it will use L<Module::Pluggable>.
880
881 Specify a C<setup_components> config option to pass additional options directly
882 to L<Module::Pluggable>.
883
884 =head1 Other methods
885
886 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
887
888 Looks for components in a given subcontainer (such as controller, model or
889 view), and returns the searched component. If $name is undef, it returns the
890 default component (such as default_view, if $sub_container is 'view'). If
891 $name is a regexp, it returns an array of matching components. Otherwise, it
892 looks for the component with name $name.
893
894 =head2 get_all_components
895
896 Fetches all the components, in each of the sub_containers model, view and
897 controller, and returns a readonly hash. The keys are the class names, and
898 the values are the blessed objects. This is what is returned by $c->components.
899
900 =head2 add_component
901
902 Adds a component to the appropriate subcontainer. The subcontainer is guessed
903 by the component name given.
904
905 =head2 find_component
906
907 Searches for components in all containers. If $component is the full class
908 name, the subcontainer is guessed, and it gets the searched component in there.
909 Otherwise, it looks for a component with that name in all subcontainers. If
910 $component is a regexp it calls _find_component_regexp and matches all
911 components against that regexp.
912
913 =head2 expand_component_module
914
915 Components found by C<locate_components> will be passed to this method, which
916 is expected to return a list of component (package) names to be set up.
917
918 =head2 setup_components
919
920 =head1 AUTHORS
921
922 Catalyst Contributors, see Catalyst.pm
923
924 =head1 COPYRIGHT
925
926 This library is free software. You can redistribute it and/or modify it under
927 the same terms as Perl itself.
928
929 =cut