Fix spelling test fails
[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         # FIXME - why not just say: @comps = $class->locate_components() ?
469         $class->log->warn(qq{You have overridden locate_components. That } .
470             qq{no longer works. Please refer to the documentation to achieve } .
471             qq{similar results.\n}
472         );
473     }
474
475     for my $component ( @comps ) {
476
477         # We pass ignore_loaded here so that overlay files for (e.g.)
478         # Model::DBI::Schema sub-classes are loaded - if it's in @comps
479         # we know M::P::O found a file on disk so this is safe
480
481         Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
482     }
483
484     for my $component (@comps) {
485         $self->add_component( $component );
486         # FIXME - $instance->expand_modules() is broken
487         my @expanded_components = $self->expand_component_module( $component );
488
489         if (
490             !$deprecatedcatalyst_component_names &&
491             ($deprecatedcatalyst_component_names = $component =~ m/::[CMV]::/) ||
492             ($deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components)
493         ) {
494             # FIXME - should I be calling warn here?
495             # Maybe it's time to remove it, or become fatal
496             $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
497                 qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
498             );
499         }
500
501         for my $component (@expanded_components) {
502             $self->add_component( $component )
503                 unless $comps{$component};
504         }
505     }
506 }
507
508 sub _fix_syntax {
509     my $config     = shift;
510     my @components = (
511         map +{
512             prefix => $_ eq 'Component' ? '' : $_ . '::',
513             values => delete $config->{ lc $_ } || delete $config->{ $_ }
514         },
515         grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
516             qw( Component Model M View V Controller C Plugin )
517     );
518
519     foreach my $comp ( @components ) {
520         my $prefix = $comp->{ prefix };
521         foreach my $element ( keys %{ $comp->{ values } } ) {
522             $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
523         }
524     }
525 }
526
527 sub _config_substitutions {
528     my ( $self, $name, $subs, $arg ) = @_;
529
530     $subs->{ HOME } ||= sub { shift->path_to( '' ); };
531     $subs->{ ENV } ||=
532         sub {
533             my ( $c, $v ) = @_;
534             if (! defined($ENV{$v})) {
535                 Catalyst::Exception->throw( message =>
536                     "Missing environment variable: $v" );
537                 return "";
538             } else {
539                 return $ENV{ $v };
540             }
541         };
542     $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
543     $subs->{ literal } ||= sub { return $_[ 1 ]; };
544     my $subsre = join( '|', keys %$subs );
545
546     $arg =~ s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $name, $2 ? split( /,/, $2 ) : () ) }eg;
547     return $arg;
548 }
549
550 sub get_component_from_sub_container {
551     my ( $self, $sub_container_name, $name, $c, @args ) = @_;
552
553     my $sub_container = $self->get_sub_container( $sub_container_name );
554
555     if (!$name) {
556         my $default = $sub_container->default_component;
557
558         return $sub_container->get_component( $default, $c, @args )
559             if $default && $sub_container->has_service( $default );
560
561         # FIXME - should I be calling $c->log->warn here?
562         # this is never a controller, so this is safe
563         $c->log->warn( "Calling \$c->$sub_container_name() is not supported unless you specify one of:" );
564         $c->log->warn( "* \$c->config(default_$sub_container_name => 'the name of the default $sub_container_name to use')" );
565         $c->log->warn( "* \$c->stash->{current_$sub_container_name} # the name of the view to use for this request" );
566         $c->log->warn( "* \$c->stash->{current_${sub_container_name}_instance} # the instance of the $sub_container_name to use for this request" );
567
568         return;
569     }
570
571     return $sub_container->get_component_regexp( $name, $c, @args )
572         if ref $name;
573
574     return $sub_container->get_component( $name, $c, @args )
575         if $sub_container->has_service( $name );
576
577     $c->log->warn(
578         "Attempted to use $sub_container_name '$name', " .
579         "but it does not exist"
580     );
581
582     return;
583 }
584
585 sub find_component {
586     my ( $self, $component, @args ) = @_;
587     my ( $type, $name ) = _get_component_type_name($component);
588     my @result;
589
590     return $self->get_component_from_sub_container(
591         $type, $name, @args
592     ) if $type;
593
594     my $query = ref $component
595               ? $component
596               : qr{^$component$}
597               ;
598
599     for my $subcontainer_name (qw/model view controller/) {
600         my $subcontainer = $self->get_sub_container( $subcontainer_name );
601         my @components   = $subcontainer->get_service_list;
602         @result          = grep { m{$component} } @components;
603
604         return map { $subcontainer->get_component( $_, @args ) } @result
605             if @result;
606     }
607
608     # one last search for things like $c->comp(qr/::M::/)
609     @result = $self->_find_component_regexp(
610         $component, @args
611     ) if !@result and ref $component;
612
613     # it expects an empty list on failed searches
614     return @result;
615 }
616
617 sub _find_component_regexp {
618     my ( $self, $component, $ctx, @args ) = @_;
619     my @result;
620
621     my @components = grep { m{$component} } keys %{ $self->get_all_components($ctx) };
622
623     for (@components) {
624         my ($type, $name) = _get_component_type_name($_);
625
626         push @result, $self->get_component_from_sub_container(
627             $type, $name, $ctx, @args
628         ) if $type;
629     }
630
631     return @result;
632 }
633
634 sub get_all_components {
635     my ($self, $class) = @_;
636     my %components;
637
638     # FIXME - if we're getting from these containers, we need to either:
639     #   - pass 'ctx' and 'accept_context_args' OR
640     #   - make these params optional
641     # big problem when setting up the dispatcher - this method is called
642     # as $container->get_all_components('MyApp'). What to do with Request
643     # life cycles?
644     foreach my $type (qw/model view controller /) {
645         my $container = $self->get_sub_container($type);
646
647         for my $component ($container->get_service_list) {
648             my $comp_service = $container->get_service($component);
649
650             $components{$comp_service->catalyst_component_name} = $comp_service->get(ctx => $class);
651         }
652     }
653
654     return lock_hash %components;
655 }
656
657 sub add_component {
658     my ( $self, $component ) = @_;
659     my ( $type, $name ) = _get_component_type_name($component);
660
661     return unless $type;
662
663     # The 'component' sub-container will create the object, and store it's
664     # instance, which, by default, will live throughout the application.
665     # The model/view/controller sub-containers only reference the instance
666     # held in the aforementioned sub-container, and execute the ACCEPT_CONTEXT
667     # sub every time they are called, when it exists.
668     my $instance_container       = $self->get_sub_container('component');
669     my $accept_context_container = $self->get_sub_container($type);
670
671     # Custom containers might have added the service already
672     # We don't want to override that
673     return if $accept_context_container->has_service( $name );
674
675     my $component_service_name = "${type}_${name}";
676
677     $instance_container->add_service(
678         Catalyst::IOC::ConstructorInjection->new(
679             name      => $component_service_name,
680             catalyst_component_name => $component,
681             class     => $component,
682             lifecycle => 'Singleton',
683             dependencies => [
684                 depends_on( '/application_name' ),
685             ],
686         )
687     );
688
689     $accept_context_container->add_service(
690         Catalyst::IOC::BlockInjection->new(
691             name         => $name,
692             catalyst_component_name => $component,
693             dependencies => [
694                 depends_on( "/component/$component_service_name" ),
695             ],
696             block => sub { shift->param($component_service_name) },
697         )
698     );
699 }
700
701 # FIXME: should this sub exist?
702 # should it be moved to Catalyst::Utils,
703 # or replaced by something already existing there?
704 sub _get_component_type_name {
705     my ( $component ) = @_;
706     my $result;
707
708     while ( !$result and (my $index = index $component, '::') > 0 ) {
709         my $type   = lc substr $component, 0, $index;
710         $component = substr $component, $index + 2;
711         $result    = first { $type eq $_ or $type eq substr($_, 0, 1) }
712                          qw{ model view controller };
713     }
714
715     return ($result, $component);
716 }
717
718 sub expand_component_module {
719     my ( $class, $module ) = @_;
720     return Devel::InnerPackage::list_packages( $module );
721 }
722
723 1;
724
725 __END__
726
727 =pod
728
729 =head1 NAME
730
731 Catalyst::Container - IOC for Catalyst components
732
733 =head1 SYNOPSIS
734
735 =head1 DESCRIPTION
736
737 =head1 METHODS
738
739 =head1 Methods for Building Containers
740
741 =head2 build_component_subcontainer
742
743 Container that stores all components, i.e. all models, views and controllers
744 together. Each service is an instance of the actual component, and by default
745 it lives while the application is running. Retrieving components from this
746 sub-container will instantiate the component, if it hasn't been instantiated
747 already, but will not execute ACCEPT_CONTEXT.
748
749 =head2 build_model_subcontainer
750
751 Container that stores references for all models that are inside the components
752 sub-container. Retrieving a model triggers ACCEPT_CONTEXT, if it exists.
753
754 =head2 build_view_subcontainer
755
756 Same as L<build_model_subcontainer>, but for views.
757
758 =head2 build_controller_subcontainer
759
760 Same as L<build_model_subcontainer>, but for controllers.
761
762 =head1 Methods for Building Services
763
764 =head2 build_application_name_service
765
766 Name of the application (such as MyApp).
767
768 =head2 build_driver_service
769
770 Config options passed directly to the driver being used.
771
772 =head2 build_file_service
773
774 ?
775
776 =head2 build_substitutions_service
777
778 This method substitutes macros found with calls to a function. There are a
779 number of default macros:
780
781 =over
782
783 =item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
784
785 =item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
786
787 =item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
788
789 =item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
790 C<__DATA__> as a config value, for example)
791
792 =back
793
794 The parameter list is split on comma (C<,>). You can override this method to
795 do your own string munging, or you can define your own macros in
796 C<< <MyApp->config( 'Plugin::ConfigLoader' => { substitutions => { ... } } ) >>.
797 Example:
798
799     MyApp->config( 'Plugin::ConfigLoader' => {
800         substitutions => {
801             baz => sub { my $c = shift; qux( @_ ); },
802         },
803     });
804
805 The above will respond to C<__baz(x,y)__> in config strings.
806
807 =head2 build_extensions_service
808
809 Config::Any's available config file extensions (e.g. xml, json, pl, etc).
810
811 =head2 build_prefix_service
812
813 The prefix, based on the application name, that will be used to look-up the
814 config files (which will be in the format $prefix.$extension). If the app is
815 MyApp::Foo, the prefix will be myapp_foo.
816
817 =head2 build_path_service
818
819 The path to the config file (or environment variable, if defined).
820
821 =head2 build_config_service
822
823 The resulting configuration for the application, after it has successfully
824 been loaded, and all substitutions have been made.
825
826 =head2 build_raw_config_service
827
828 The merge of local_config and global_config hashes, before substitutions.
829
830 =head2 build_global_files_service
831
832 Gets all files for config that don't have the local_suffix, such as myapp.conf.
833
834 =head2 build_local_files_service
835
836 Gets all files for config that have the local_suffix, such as myapp_local.conf.
837
838 =head2 build_global_config_service
839
840 Reads config from global_files.
841
842 =head2 build_local_config_service
843
844 Reads config from local_files.
845
846 =head2 build_class_config_service
847
848 Reads config set from the application's class attribute config,
849 i.e. MyApp->config( name => 'MyApp', ... )
850
851 =head2 build_config_path_service
852
853 Splits the path to the config file, and returns on array ref containing
854 the path to the config file minus the extension in the first position,
855 and the extension in the second.
856
857 =head2 build_config_local_suffix_service
858
859 Determines the suffix of files used to override the main config. By default
860 this value is C<local>, which will load C<myapp_local.conf>.  The suffix can
861 be specified in the following order of preference:
862
863 =over
864
865 =item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
866
867 =item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
868
869 =back
870
871 The first one of these values found replaces the default of C<local> in the
872 name of the local config file to be loaded.
873
874 For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
875 ConfigLoader will try and load C<myapp_testing.conf> instead of
876 C<myapp_local.conf>.
877
878 =head2 build_locate_components_service
879
880 This method is meant to provide a list of component modules that should be
881 setup for the application.  By default, it will use L<Module::Pluggable>.
882
883 Specify a C<setup_components> config option to pass additional options directly
884 to L<Module::Pluggable>.
885
886 =head1 Other methods
887
888 =head2 get_component_from_sub_container($sub_container, $name, $c, @args)
889
890 Looks for components in a given sub-container (such as controller, model or
891 view), and returns the searched component. If $name is undef, it returns the
892 default component (such as default_view, if $sub_container is 'view'). If
893 $name is a regexp, it returns an array of matching components. Otherwise, it
894 looks for the component with name $name.
895
896 =head2 get_all_components
897
898 Fetches all the components, in each of the sub_containers model, view and
899 controller, and returns a read-only hash. The keys are the class names, and
900 the values are the blessed objects. This is what is returned by $c->components.
901
902 =head2 add_component
903
904 Adds a component to the appropriate sub-container. The sub-container is guessed
905 by the component name given.
906
907 =head2 find_component
908
909 Searches for components in all containers. If $component is the full class
910 name, the sub-container is guessed, and it gets the searched component in there.
911 Otherwise, it looks for a component with that name in all sub-containers. If
912 $component is a regexp it calls _find_component_regexp and matches all
913 components against that regexp.
914
915 =head2 expand_component_module
916
917 Components found by C<locate_components> will be passed to this method, which
918 is expected to return a list of component (package) names to be set up.
919
920 =head2 setup_components
921
922 Uses locate_components service to list the components, and adds them to the
923 appropriate sub-containers, using add_component().
924
925 =head1 AUTHORS
926
927 Catalyst Contributors, see Catalyst.pm
928
929 =head1 COPYRIGHT
930
931 This library is free software. You can redistribute it and/or modify it under
932 the same terms as Perl itself.
933
934 =cut