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