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